aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/nvi/FAQ160
-rw-r--r--contrib/nvi/LAYOUT128
-rw-r--r--contrib/nvi/LICENSE40
-rw-r--r--contrib/nvi/README113
-rw-r--r--contrib/nvi/build/ExtUtils/Embed.pm473
-rw-r--r--contrib/nvi/build/Makefile.in630
-rw-r--r--contrib/nvi/build/README369
-rw-r--r--contrib/nvi/build/README.LynxOS320
-rw-r--r--contrib/nvi/build/acconfig.h82
-rw-r--r--contrib/nvi/build/aclocal.m417
-rwxr-xr-xcontrib/nvi/build/config.guess571
-rw-r--r--contrib/nvi/build/config.h.in179
-rwxr-xr-xcontrib/nvi/build/config.sub872
-rwxr-xr-xcontrib/nvi/build/configure4446
-rw-r--r--contrib/nvi/build/configure.in725
-rw-r--r--contrib/nvi/build/distrib84
-rwxr-xr-xcontrib/nvi/build/install-sh238
-rw-r--r--contrib/nvi/build/pathnames.h.in45
-rw-r--r--contrib/nvi/build/port.h.in185
-rw-r--r--contrib/nvi/build/recover.in49
-rw-r--r--contrib/nvi/build/spell.ok58
-rw-r--r--contrib/nvi/catalog/Makefile84
-rw-r--r--contrib/nvi/catalog/README166
-rw-r--r--contrib/nvi/catalog/dump.c114
-rw-r--r--contrib/nvi/catalog/dutch317
-rw-r--r--contrib/nvi/catalog/dutch.base307
-rw-r--r--contrib/nvi/catalog/dutch.check37
-rw-r--r--contrib/nvi/catalog/dutch.owner1
-rw-r--r--contrib/nvi/catalog/english317
-rw-r--r--contrib/nvi/catalog/english.base309
-rw-r--r--contrib/nvi/catalog/english.check36
-rw-r--r--contrib/nvi/catalog/english.owner1
-rw-r--r--contrib/nvi/catalog/french317
-rw-r--r--contrib/nvi/catalog/french.base309
-rw-r--r--contrib/nvi/catalog/french.check34
-rw-r--r--contrib/nvi/catalog/german317
-rw-r--r--contrib/nvi/catalog/german.base307
-rw-r--r--contrib/nvi/catalog/german.check36
-rw-r--r--contrib/nvi/catalog/german.owner1
-rw-r--r--contrib/nvi/catalog/ru_RU.KOI8-R267
-rw-r--r--contrib/nvi/catalog/ru_RU.KOI8-R.base219
-rw-r--r--contrib/nvi/catalog/ru_RU.KOI8-R.check169
-rw-r--r--contrib/nvi/catalog/ru_RU.KOI8-R.owner1
-rw-r--r--contrib/nvi/catalog/ru_SU.KOI8-R267
-rw-r--r--contrib/nvi/catalog/ru_SU.KOI8-R.base219
-rw-r--r--contrib/nvi/catalog/ru_SU.KOI8-R.check169
-rw-r--r--contrib/nvi/catalog/ru_SU.KOI8-R.owner1
-rw-r--r--contrib/nvi/catalog/spanish317
-rw-r--r--contrib/nvi/catalog/spanish.base309
-rw-r--r--contrib/nvi/catalog/spanish.check35
-rw-r--r--contrib/nvi/catalog/spell.ok19
-rw-r--r--contrib/nvi/catalog/swedish317
-rw-r--r--contrib/nvi/catalog/swedish.base307
-rw-r--r--contrib/nvi/catalog/swedish.check34
-rw-r--r--contrib/nvi/catalog/swedish.owner1
-rw-r--r--contrib/nvi/cl/README.signal174
-rw-r--r--contrib/nvi/cl/cl.h78
-rw-r--r--contrib/nvi/cl/cl_bsd.c345
-rw-r--r--contrib/nvi/cl/cl_funcs.c704
-rw-r--r--contrib/nvi/cl/cl_main.c471
-rw-r--r--contrib/nvi/cl/cl_read.c334
-rw-r--r--contrib/nvi/cl/cl_screen.c581
-rw-r--r--contrib/nvi/cl/cl_term.c459
-rw-r--r--contrib/nvi/clib/bsearch.c90
-rw-r--r--contrib/nvi/clib/env.c160
-rw-r--r--contrib/nvi/clib/gethostname.c22
-rw-r--r--contrib/nvi/clib/getopt.c130
-rw-r--r--contrib/nvi/clib/memchr.c65
-rw-r--r--contrib/nvi/clib/memmove.c147
-rw-r--r--contrib/nvi/clib/memset.c137
-rw-r--r--contrib/nvi/clib/mkstemp.c133
-rw-r--r--contrib/nvi/clib/mmap.c50
-rw-r--r--contrib/nvi/clib/snprintf.c45
-rw-r--r--contrib/nvi/clib/strdup.c63
-rw-r--r--contrib/nvi/clib/strerror.c74
-rw-r--r--contrib/nvi/clib/strpbrk.c62
-rw-r--r--contrib/nvi/clib/strsep.c85
-rw-r--r--contrib/nvi/clib/strtol.c134
-rw-r--r--contrib/nvi/clib/strtoul.c113
-rw-r--r--contrib/nvi/clib/vsnprintf.c31
-rw-r--r--contrib/nvi/common/api.c525
-rw-r--r--contrib/nvi/common/args.h29
-rw-r--r--contrib/nvi/common/common.h96
-rw-r--r--contrib/nvi/common/cut.c368
-rw-r--r--contrib/nvi/common/cut.h77
-rw-r--r--contrib/nvi/common/delete.c160
-rw-r--r--contrib/nvi/common/exf.c1498
-rw-r--r--contrib/nvi/common/exf.h82
-rw-r--r--contrib/nvi/common/gs.h210
-rw-r--r--contrib/nvi/common/key.c865
-rw-r--r--contrib/nvi/common/key.h222
-rw-r--r--contrib/nvi/common/line.c576
-rw-r--r--contrib/nvi/common/log.c717
-rw-r--r--contrib/nvi/common/log.h20
-rw-r--r--contrib/nvi/common/main.c617
-rw-r--r--contrib/nvi/common/mark.c277
-rw-r--r--contrib/nvi/common/mark.h42
-rw-r--r--contrib/nvi/common/mem.h168
-rw-r--r--contrib/nvi/common/msg.c895
-rw-r--r--contrib/nvi/common/msg.h65
-rw-r--r--contrib/nvi/common/options.awk9
-rw-r--r--contrib/nvi/common/options.c1141
-rw-r--r--contrib/nvi/common/options.h101
-rw-r--r--contrib/nvi/common/options_f.c367
-rw-r--r--contrib/nvi/common/put.c231
-rw-r--r--contrib/nvi/common/recover.c878
-rw-r--r--contrib/nvi/common/screen.c233
-rw-r--r--contrib/nvi/common/screen.h203
-rw-r--r--contrib/nvi/common/search.c492
-rw-r--r--contrib/nvi/common/seq.c395
-rw-r--r--contrib/nvi/common/seq.h44
-rw-r--r--contrib/nvi/common/util.c230
-rw-r--r--contrib/nvi/common/util.h56
-rw-r--r--contrib/nvi/docs/TODO147
-rw-r--r--contrib/nvi/docs/USD.doc/edit/Makefile11
-rw-r--r--contrib/nvi/docs/USD.doc/edit/edit.vindex115
-rw-r--r--contrib/nvi/docs/USD.doc/edit/edittut.ms2280
-rw-r--r--contrib/nvi/docs/USD.doc/exref/Makefile17
-rw-r--r--contrib/nvi/docs/USD.doc/exref/ex.rm2213
-rw-r--r--contrib/nvi/docs/USD.doc/exref/ex.summary730
-rw-r--r--contrib/nvi/docs/USD.doc/vi.man/Makefile16
-rw-r--r--contrib/nvi/docs/USD.doc/vi.man/spell.ok179
-rw-r--r--contrib/nvi/docs/USD.doc/vi.man/vi.11608
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/Makefile32
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff1924
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/index.so260
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/merge.awk16
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/ref.so103
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff1303
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/spell.ok414
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff3085
-rw-r--r--contrib/nvi/docs/USD.doc/vi.ref/vi.ref1840
-rw-r--r--contrib/nvi/docs/USD.doc/vitut/Makefile22
-rw-r--r--contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms1081
-rw-r--r--contrib/nvi/docs/USD.doc/vitut/vi.chars645
-rw-r--r--contrib/nvi/docs/USD.doc/vitut/vi.in2074
-rw-r--r--contrib/nvi/docs/USD.doc/vitut/vi.summary468
-rw-r--r--contrib/nvi/docs/changelog1102
-rw-r--r--contrib/nvi/docs/ev55
-rw-r--r--contrib/nvi/docs/features83
-rw-r--r--contrib/nvi/docs/help229
-rw-r--r--contrib/nvi/docs/internals/autowrite88
-rw-r--r--contrib/nvi/docs/internals/context32
-rw-r--r--contrib/nvi/docs/internals/cscope.NOTES142
-rw-r--r--contrib/nvi/docs/internals/gdb.script76
-rw-r--r--contrib/nvi/docs/internals/input350
-rw-r--r--contrib/nvi/docs/internals/openmode36
-rw-r--r--contrib/nvi/docs/internals/quoting208
-rw-r--r--contrib/nvi/docs/internals/structures68
-rw-r--r--contrib/nvi/docs/interp/interp190
-rw-r--r--contrib/nvi/docs/interp/spell.ok46
-rw-r--r--contrib/nvi/docs/spell.ok173
-rw-r--r--contrib/nvi/docs/tutorial/vi.advanced1458
-rw-r--r--contrib/nvi/docs/tutorial/vi.beginner741
-rwxr-xr-xcontrib/nvi/docs/tutorial/vi.tut.csh24
-rw-r--r--contrib/nvi/ex/ex.awk6
-rw-r--r--contrib/nvi/ex/ex.c2370
-rw-r--r--contrib/nvi/ex/ex.h228
-rw-r--r--contrib/nvi/ex/ex_abbrev.c117
-rw-r--r--contrib/nvi/ex/ex_append.c277
-rw-r--r--contrib/nvi/ex/ex_args.c327
-rw-r--r--contrib/nvi/ex/ex_argv.c756
-rw-r--r--contrib/nvi/ex/ex_at.c126
-rw-r--r--contrib/nvi/ex/ex_bang.c186
-rw-r--r--contrib/nvi/ex/ex_cd.c129
-rw-r--r--contrib/nvi/ex/ex_cmd.c457
-rw-r--r--contrib/nvi/ex/ex_cscope.c1057
-rw-r--r--contrib/nvi/ex/ex_delete.c65
-rw-r--r--contrib/nvi/ex/ex_display.c145
-rw-r--r--contrib/nvi/ex/ex_edit.c153
-rw-r--r--contrib/nvi/ex/ex_equal.c59
-rw-r--r--contrib/nvi/ex/ex_file.c80
-rw-r--r--contrib/nvi/ex/ex_filter.c316
-rw-r--r--contrib/nvi/ex/ex_global.c328
-rw-r--r--contrib/nvi/ex/ex_init.c417
-rw-r--r--contrib/nvi/ex/ex_join.c177
-rw-r--r--contrib/nvi/ex/ex_map.c121
-rw-r--r--contrib/nvi/ex/ex_mark.c45
-rw-r--r--contrib/nvi/ex/ex_mkexrc.c101
-rw-r--r--contrib/nvi/ex/ex_move.c198
-rw-r--r--contrib/nvi/ex/ex_open.c46
-rw-r--r--contrib/nvi/ex/ex_perl.c69
-rw-r--r--contrib/nvi/ex/ex_preserve.c103
-rw-r--r--contrib/nvi/ex/ex_print.c352
-rw-r--r--contrib/nvi/ex/ex_put.c51
-rw-r--r--contrib/nvi/ex/ex_quit.c46
-rw-r--r--contrib/nvi/ex/ex_read.c360
-rw-r--r--contrib/nvi/ex/ex_screen.c138
-rw-r--r--contrib/nvi/ex/ex_script.c798
-rw-r--r--contrib/nvi/ex/ex_set.c46
-rw-r--r--contrib/nvi/ex/ex_shell.c378
-rw-r--r--contrib/nvi/ex/ex_shift.c191
-rw-r--r--contrib/nvi/ex/ex_source.c85
-rw-r--r--contrib/nvi/ex/ex_stop.c51
-rw-r--r--contrib/nvi/ex/ex_subst.c1459
-rw-r--r--contrib/nvi/ex/ex_tag.c1324
-rw-r--r--contrib/nvi/ex/ex_tcl.c80
-rw-r--r--contrib/nvi/ex/ex_txt.c430
-rw-r--r--contrib/nvi/ex/ex_undo.c77
-rw-r--r--contrib/nvi/ex/ex_usage.c196
-rw-r--r--contrib/nvi/ex/ex_util.c234
-rw-r--r--contrib/nvi/ex/ex_version.c39
-rw-r--r--contrib/nvi/ex/ex_visual.c161
-rw-r--r--contrib/nvi/ex/ex_write.c375
-rw-r--r--contrib/nvi/ex/ex_yank.c46
-rw-r--r--contrib/nvi/ex/ex_z.c150
-rw-r--r--contrib/nvi/ex/script.h23
-rw-r--r--contrib/nvi/ex/tag.h107
-rw-r--r--contrib/nvi/ex/version.h2
-rw-r--r--contrib/nvi/include/bitstring.h143
-rw-r--r--contrib/nvi/include/cl_extern.h56
-rw-r--r--contrib/nvi/include/com_extern.h199
-rw-r--r--contrib/nvi/include/ex_def.h78
-rw-r--r--contrib/nvi/include/ex_extern.h127
-rw-r--r--contrib/nvi/include/ip_extern.h23
-rw-r--r--contrib/nvi/include/options_def.h79
-rw-r--r--contrib/nvi/include/perl_extern.h8
-rw-r--r--contrib/nvi/include/sys/queue.h259
-rw-r--r--contrib/nvi/include/tcl_extern.h1
-rw-r--r--contrib/nvi/include/tk_extern.h29
-rw-r--r--contrib/nvi/include/vi_extern.h142
-rw-r--r--contrib/nvi/ip/IP_INSTRUCTIONS41
-rw-r--r--contrib/nvi/ip/ip.h92
-rw-r--r--contrib/nvi/ip/ip_funcs.c443
-rw-r--r--contrib/nvi/ip/ip_main.c165
-rw-r--r--contrib/nvi/ip/ip_read.c307
-rw-r--r--contrib/nvi/ip/ip_screen.c87
-rw-r--r--contrib/nvi/ip/ip_term.c108
-rw-r--r--contrib/nvi/ip_cl/Makefile20
-rw-r--r--contrib/nvi/ip_cl/ip_cl.c742
-rw-r--r--contrib/nvi/perl_api/VI.pod218
-rw-r--r--contrib/nvi/perl_api/nviperl.pod43
-rw-r--r--contrib/nvi/perl_api/perl.xs1115
-rw-r--r--contrib/nvi/perl_api/perlsfio.c85
-rw-r--r--contrib/nvi/perl_api/typemap42
-rw-r--r--contrib/nvi/perl_scripts/forall.pl10
-rw-r--r--contrib/nvi/perl_scripts/make.pl27
-rw-r--r--contrib/nvi/perl_scripts/tk.pl20
-rw-r--r--contrib/nvi/perl_scripts/wc.pl11
-rw-r--r--contrib/nvi/tcl_api/tcl.c852
-rw-r--r--contrib/nvi/tcl_scripts/errors.tcl44
-rw-r--r--contrib/nvi/tcl_scripts/gnats.tcl95
-rw-r--r--contrib/nvi/tcl_scripts/mailprocs.tcl115
-rw-r--r--contrib/nvi/tcl_scripts/wc.tcl16
-rw-r--r--contrib/nvi/tk/init.tcl1096
-rw-r--r--contrib/nvi/tk/tk_funcs.c346
-rw-r--r--contrib/nvi/tk/tk_main.c423
-rw-r--r--contrib/nvi/tk/tk_read.c207
-rw-r--r--contrib/nvi/tk/tk_screen.c86
-rw-r--r--contrib/nvi/tk/tk_term.c169
-rw-r--r--contrib/nvi/tk/tk_util.c250
-rw-r--r--contrib/nvi/tk/tki.h64
-rw-r--r--contrib/nvi/vi/getc.c234
-rw-r--r--contrib/nvi/vi/v_at.c109
-rw-r--r--contrib/nvi/vi/v_ch.c295
-rw-r--r--contrib/nvi/vi/v_cmd.c505
-rw-r--r--contrib/nvi/vi/v_delete.c107
-rw-r--r--contrib/nvi/vi/v_ex.c696
-rw-r--r--contrib/nvi/vi/v_increment.c267
-rw-r--r--contrib/nvi/vi/v_init.c126
-rw-r--r--contrib/nvi/vi/v_itxt.c537
-rw-r--r--contrib/nvi/vi/v_left.c293
-rw-r--r--contrib/nvi/vi/v_mark.c234
-rw-r--r--contrib/nvi/vi/v_match.c170
-rw-r--r--contrib/nvi/vi/v_paragraph.c344
-rw-r--r--contrib/nvi/vi/v_put.c146
-rw-r--r--contrib/nvi/vi/v_redraw.c39
-rw-r--r--contrib/nvi/vi/v_replace.c203
-rw-r--r--contrib/nvi/vi/v_right.c145
-rw-r--r--contrib/nvi/vi/v_screen.c65
-rw-r--r--contrib/nvi/vi/v_scroll.c474
-rw-r--r--contrib/nvi/vi/v_search.c515
-rw-r--r--contrib/nvi/vi/v_section.c252
-rw-r--r--contrib/nvi/vi/v_sentence.c359
-rw-r--r--contrib/nvi/vi/v_status.c41
-rw-r--r--contrib/nvi/vi/v_txt.c2950
-rw-r--r--contrib/nvi/vi/v_ulcase.c179
-rw-r--r--contrib/nvi/vi/v_undo.c139
-rw-r--r--contrib/nvi/vi/v_util.c180
-rw-r--r--contrib/nvi/vi/v_word.c547
-rw-r--r--contrib/nvi/vi/v_xchar.c107
-rw-r--r--contrib/nvi/vi/v_yank.c82
-rw-r--r--contrib/nvi/vi/v_z.c149
-rw-r--r--contrib/nvi/vi/v_zexit.c54
-rw-r--r--contrib/nvi/vi/vi.c1251
-rw-r--r--contrib/nvi/vi/vi.h377
-rw-r--r--contrib/nvi/vi/vs_line.c514
-rw-r--r--contrib/nvi/vi/vs_msg.c927
-rw-r--r--contrib/nvi/vi/vs_refresh.c885
-rw-r--r--contrib/nvi/vi/vs_relative.c305
-rw-r--r--contrib/nvi/vi/vs_smap.c1260
-rw-r--r--contrib/nvi/vi/vs_split.c607
292 files changed, 98515 insertions, 0 deletions
diff --git a/contrib/nvi/FAQ b/contrib/nvi/FAQ
new file mode 100644
index 000000000000..357650a589bd
--- /dev/null
+++ b/contrib/nvi/FAQ
@@ -0,0 +1,160 @@
+@(#)FAQ 8.13 (Berkeley) 10/14/96
+
+Q: How can I get vi to display my character set?
+A: Vi uses the C library routine isprint(3) to determine if a character
+ is printable, or should be displayed as an octal or hexadecimal value
+ on the screen. Generally, if vi is displaying printable characters
+ in octal/hexadecimal forms, your environment is not configured correctly.
+ Try looking at the man pages that allow you to configure your locale.
+ For example, to configure an ISO 8859-1 locale under Solaris using csh,
+ you would do:
+
+ setenv LANG C
+ setenv LC_CTYPE iso_8859_1
+
+ Other LC_CTYPE systems/values that I'm told work:
+
+ System Value
+ ====== =====
+ FreeBSD lt_LN.ISO_8859-1
+ HP-UX 9.X american.iso88591
+ HP-UX 10.X en_US.iso88591
+ SunOS 4.X iso_8859_1
+ SunOS 5.X iso_8859_1
+
+ If there's no other solution, you can use the print and noprint edit
+ options of vi to specify that a specific character is printable or not
+ printable.
+
+Q: My map won't work!
+A: One thing that you should immediately check if a vi map doesn't work
+ is if depends on the final cursor position after a P or p command.
+ Historic vi's were inconsistent as to the final position of the cursor,
+ and, to make matter worse, the final cursor position also depended on
+ whether the put text came from a named or unnamed buffer! Vi follows
+ the POSIX 1003.2 standard on this one, and makes this consistent, always
+ placing the cursor on the first character.
+
+Q: I'm using ksh or csh as my vi edit option shell value, and file
+ expansions don't work right!
+A: The problem may be in your ksh or csh startup files, e.g., .cshrc. Vi
+ executes the shell to do name expansion, and the shell generally reads
+ its startup files. If the startup files are not correctly configured
+ for non-interactive use, e.g., they always echo a prompt to the screen,
+ vi will be unable to parse the output and things will not work
+ correctly.
+
+Q: How does the iclower edit option differ from the ignorecase (i.e. ic)
+ edit option?
+A: The difference is that the ignorecase edit option always ignores the
+ case of letters in the Regular Expression (RE), and the iclower edit
+ option only ignores the case if there are no upper-case letters in the
+ RE. If any upper-case letters appear in the Regular Expression, then
+ it will be treated case-sensitively, as if the ignorecase edit option
+ was not set.
+
+Q: When I edit binary files, vi appends a <newline> to the last line!
+A: This is historic practice for vi, and further, it's required by the
+ POSIX 1003.2 standard. My intent is to provide a command line and/or
+ edit option to turn this behavior off when I switch to version 2.0 of
+ the Berkeley DB package.
+
+Q: My cursor keys don't work when I'm in text input mode!
+A: A common problem over slow links is that the set of characters sent by
+ the cursor keys don't arrive close enough together for vi to understand
+ that they are a single keystroke, and not separate keystrokes. Try
+ increasing the value of the escapetime edit option, which will cause
+ vi to wait longer before deciding that the <escape> character that
+ starts cursor key sequences doesn't have any characters following it.
+
+Q: When I edit some files, vi seems to hang forever, and I have to kill it.
+A: Vi uses flock(2) and fcntl(2) to do file locking. When it attempts to
+ acquired a lock for a file on an NFS mounted filesystem, it can hang
+ for a very long (perhaps infinite) period of time. Turning off the
+ "lock" edit option will keep vi from attempting to acquire any locks
+ on the files you edit.
+
+Q: When I compile vi I get lots of warnings about pointer assignments
+ being incompatible!
+A: Vi is partially written to support wide characters. When this code
+ interfaces with the code that doesn't yet support wide characters,
+ the pointer types clash. This will hopefully be fixed in the near
+ future, but I've been saying that for awhile, now.
+
+Q: I get jumpy scrolling behavior in the screen!
+A: This is almost certainly a problem with the system's terminfo or
+ termcap information for your terminal. If the terminfo/termcap entry
+ doesn't have the settable scrolling region capabilities, or the more
+ powerful scrolling commands, these behaviors can result. Historic
+ implementations of vi, and some of the vi clones, don't suffer from
+ this problem because they wrote their own screen support instead of
+ using the curses library.
+
+ The solution is to find a good terminfo or termcap entry for your
+ terminal, which will fix the problem for all of the applications on
+ your system, not just vi. Eric Raymond maintains the freely
+ redistributable termcap/terminfo entries. They can be downloaded
+ from http://www.ccil.org/~esr/ncurses.html, or you can contact him
+ at esr@snark.thyrsus.com.
+
+Q: The entire screen repaints on every keystroke!
+A: Your system's curses implementation is broken. You should use the
+ curses implementation provided with vi or a curses replacement such
+ as ncurses. Eric Raymond is one of the maintainers of the freely
+ redistributable ncurses package. You can download ncurses from
+ http://www.ccil.org/~esr/ncurses.html, or you can contact him at
+ esr@snark.thyrsus.com.
+
+Q: When I use vi on a Sun console (terminal type sun-34) the screen
+ is occasionally trashed, usually when exiting vi!
+A: The Sun console can't handle the 'al' capability of the termcap
+ entry (the il1 capability of terminfo entries). If you delete that
+ entry from your terminfo/termcap information everything should work
+ correctly.
+
+Q: I don't have a version of ctags (or I have ctags, but it doesn't tag
+ nearly enough things)!
+A: There's a version of ctags available on the 4.4BSD-Lite distributions,
+ as well as the FreeBSD, NetBSD, Linux and GNU distributions. Or, you
+ might want to try Exuberant Ctags:
+
+ Title: Exuberant Ctags
+ Version: 1.3
+ Entered-date: 16JUN96
+ Description:
+ A better ctags which generates tags for all possible tag types:
+ macro definitions, enumerated values (values inside enum{...}),
+ function and method definitions, enum/struct/union tags, external
+ function prototypes (optional), typedefs, and variable
+ declarations. It is far less easily fooled by code containing #if
+ preprocessor conditional constructs, using a conditional path
+ selection algorithm to resolve complicated choices, and a
+ fall-back algorithm when this one fails. Can also be used to print
+ out a list of selected objects found in source files.
+ Keywords: ctags, tags, exuberant
+ Author: darren@sirsi.com (Darren Hiebert)
+ darren@hiwaay.net (Darren Hiebert)
+ Maintained-by: darren@sirsi.com (Darren Hiebert)
+ darren@hiwaay.net (Darren Hiebert)
+ Primary-site: sunsite.unc.edu /pub/Linux/devel/lang/c
+ 27kB ctags-1.3.tar.gz
+ Alternate-site: ftp.halcyon.com /local/gvr
+ 27kB ctags-1.3.tar.gz
+ Original-site:
+ Platforms: UNIX, MSDOS, WindowsNT, Windows95, OS/2, Amiga
+ Copying-policy: Public domain
+
+Q: When I update a file I already have open, and use :e to reread it, I
+ get nul's for the rest of the file!
+A: Your system's implementation of mmap(2) has a bug; you will have to
+ exit vi and re-execute it.
+
+Q: Where can I get cscope?
+A: Cscope is available on UNIXWare System V Release 4.0 variants such as
+ Sun Solaris 2.x (/opt/SUNWspro/bin) and UNIXWare System V Release 4.1.
+
+ You can buy version 13.3 source with an unrestricted license for $400
+ from AT&T Software Solutions by calling +1-800-462-8146. Binary
+ redistribution of cscope is an additional $1500, one-time flat fee.
+
+ For more information, see http://www.unipress.com/att/new/cscope.html.
diff --git a/contrib/nvi/LAYOUT b/contrib/nvi/LAYOUT
new file mode 100644
index 000000000000..e3a55ebb5778
--- /dev/null
+++ b/contrib/nvi/LAYOUT
@@ -0,0 +1,128 @@
+# @(#)LAYOUT 8.12 (Berkeley) 10/10/96
+
+LAYOUT
+ This file: the layout of the nvi sources.
+
+LICENSE
+ Nvi's copyright notice and conditions for redistribution.
+
+README
+ Welcome message and basic information.
+
+build/
+ The build/configuration directory for nvi. See build/README for
+ more information.
+
+catalog/
+ Support for message catalogs for nvi. See catalog/README for more
+ information.
+
+cl/
+ Source files for nvi's curses screen support.
+
+clib/
+ Replacement source files for C library functions.
+
+common/
+ Source files for pieces of code that are shared by ex and vi,
+ e.g., searching and logging code or code translating line numbers
+ into requests to the dbopen(3) database code. It also has the
+ interface code for modifying "records" in the underlying database.
+
+curses/
+ A stripped-down replacement curses library. Do not try and use
+ this library outside of nvi, many standard curses functions have
+ been removed because nvi doesn't use them. See build/README for
+ more information.
+
+db/
+ A stripped-down replacement DB library. Do not try and use this
+ library outside of nvi, many standard DB functions have been
+ removed because nvi doesn't use them. See db/README for more
+ information.
+
+docs/
+ Ex/vi documentation, both current and historic.
+
+ USD.doc/
+ [USD stands for "User's Supplementary Documents".]
+
+ edit/ Roff source for "Edit: A tutorial". This document
+ was USD:14 in the 4.3BSD manuals, but was not
+ distributed with 4.4BSD.
+
+ exref/ Roff source for "Ex Reference Manual -- Version
+ 3.7". This document was USD:16 in the 4.3BSD
+ manuals, and USD tabbed 12 in the 4.4BSD manuals.
+
+ vi.man/ Roff source for a UNIX manual page for nex/nvi.
+ An updated version of the 4.4BSD manual page.
+
+ vi.ref/ Roff source for the nex/nvi reference document.
+ An updated version of the 4.4BSD document, USD
+ tabbed 13.
+
+ vitut/ Roff source for "An Introduction to Display
+ Editing with Vi". This document was USD:15 in
+ the 4.3BSD manuals, but was not distributed with
+ 4.4BSD. It includes the historic "Vi Quick
+ Reference" card.
+
+
+ PostScript preformatted versions of the nex/nvi reference
+ manual and manual page are in the files named with a ".ps"
+ suffix, in their respective source directories. Flat text
+ preformatted versions of the nex/nvi reference manual and
+ manual page are in the files named with a ".txt" suffix,
+ in their respective source directories.
+
+ changelog -- Log of changes from version to version.
+ features -- Todo list, suggested features list.
+ internals/
+ autowrite -- Vi autowrite option discussion.
+ context -- Previous context marks discussion.
+ gdb.script -- GDB debugging scripts.
+ input -- Vi maps, executable buffers, and input discussion.
+ openmode -- Open mode behaviors.
+ quoting -- Vi quoting discussion.
+ structures -- Out-of-date nvi internal structure description.
+ tutorial/ -- Historic vi tutorial(s), of unknown quality.
+
+ex/
+ 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 the ex directory. If it's vi only,
+ it's in the vi directory. Files are generally named by the
+ command(s) they support, but occasionally with a name that
+ describes their functionality.
+
+ version.h -- Version information.
+
+include/
+ Replacement include files:
+
+ bitstring.h -- The 4.4BSD bitstring operations.
+ sys/queue.h -- The 4.4BSD queue operations.
+
+perl_api/
+ Source code supporting the Perl scripting language for nvi.
+
+perl_scripts/
+ Scripts for Perl included with nvi.
+
+regex/
+ Henry Spencer's POSIX 1003.2 regular expression (RE) library.
+
+tcl_api/
+ Source code supporting the Tcl scripting language for nvi.
+
+tcl_scripts/
+ Scripts for Tcl included with nvi.
+
+tk/
+ Source files for nvi's Tk screen support.
+
+ init.tcl -- Vi startup tcl script.
+
+vi/
+ The vi source code.
diff --git a/contrib/nvi/LICENSE b/contrib/nvi/LICENSE
new file mode 100644
index 000000000000..78e8f4a40e19
--- /dev/null
+++ b/contrib/nvi/LICENSE
@@ -0,0 +1,40 @@
+The vi program is freely redistributable. You are welcome to copy, modify
+and share it with others under the conditions listed in this file. If any
+company (not any individual!) finds vi sufficiently useful that you would
+have purchased it, or if any company wishes to redistribute it, contributions
+to the authors would be appreciated.
+
+/*-
+ * Copyright (c) 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/contrib/nvi/README b/contrib/nvi/README
new file mode 100644
index 000000000000..33db36a5c862
--- /dev/null
+++ b/contrib/nvi/README
@@ -0,0 +1,113 @@
+# @(#)README 8.147 (Berkeley) 10/19/96
+
+This is the README for nex/nvi, a freely redistributable implementation
+of the ex/vi text editors originally distributed as part of the Fourth
+Berkeley Software Distribution (4BSD), by the University of California,
+Berkeley.
+
+The source code for nex/nvi can be retrieved by using anonymous ftp to
+ftp.cs.berkeley.edu. The file ucb/4bsd/nvi.tar.gz is the gzip'd archive,
+of version 1.71 of nex/nvi. This version is believed to be stable and
+problem free. The file ucb/4bsd/nvi-###.ALPHA.tar.gz is a gzip'd archive
+of the current alpha-test release of nex/nvi. This version reflects the
+current development tree, and will be more likely to have problems.
+
+See the file:
+ build/README for information on building nvi.
+ LAYOUT for a description of where everything is.
+ LICENSE for the copyright and redistribution terms.
+
+If you have any questions about nex/nvi, problems with it, or concerns
+about the conditions for redistribution, please contact me:
+
+ Keith Bostic +1-508-287-4781
+ 394 E. Riding Dr. bostic@bostic.com
+ Carlisle, MA 01741
+ USA
+
+Keith Bostic
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o This software is several years old and is the product of many folks' work.
+
+ This software was originally derived from software contributed to
+ the University of California, Berkeley by Steve Kirkendall, the
+ author of the vi clone elvis. Without his work, this work would
+ have been far more difficult.
+
+ IEEE POSIX 1003.2 style regular expression support is courtesy of
+ Henry Spencer, for which I am *very* grateful.
+
+ Elan Amir did the original 4BSD curses work that made it possible
+ to support a full-screen editor using curses.
+
+ George Neville-Neil added the Tcl interpreter, and the initial
+ interpreter design was his.
+
+ Sven Verdoolaege added the Perl interpreter.
+
+ Rob Mayoff provided the original Cscope support.
+
+o Many, many people suggested enhancements, and provided bug reports and
+ testing, far too many to individually thank.
+
+o From the original vi acknowledgements, by William Joy and Mark Horton:
+
+ Bruce Englar encouraged the early development of this display
+ editor. Peter Kessler helped bring sanity to version 2's
+ command layout. Bill Joy wrote versions 1 and 2.0 through 2.7,
+ and created the framework that users see in the present editor.
+ Mark Horton added macros and other features and made the editor
+ work on a large number of terminals and Unix systems.
+
+o And...
+ The financial support of UUNET Communications Services is gratefully
+ acknowledged.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Status:
+
+This software is in beta test, and it's pretty stable. Almost all of the
+historic functionality in ex/vi is there, the only major missing pieces
+are open mode and the lisp edit option.
+
+Nvi is largely 8-bit clean. This isn't difficult to fix, and was left in
+during initial development to keep things simple. Wide character support
+will be integrated at the same time that it is made fully 8-bit clean.
+
+There aren't a lot of new features in nex/nvi, but there are a few things
+you might like. The "Additional Features" section of the reference work
+(docs/USD.doc/vi.ref/vi.ref.txt, docs/USD.doc/vi.ref/vi.ref.ps) has more
+information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Debugging:
+
+Code fixes are greatly appreciated, of course, but if you can't provide
+them, please email me as much information as you can as to how I might
+reproduce the bug, and I'll try to fix it locally. Stack traces of core
+dumps are only rarely helpful -- an example file with a set of keystrokes
+that causes the problem is almost invariably necessary. I know it's
+annoying, but simply playing with the bug until you can reproduce it at
+will, with minimal keystrokes, is immensely helpful to me.
+
+Please include the following in the bug report;
+
+ o The version of nvi you're running (use :version to get it).
+ o The row/column dimensions of the screen (80 x 32).
+ o Unless you're confident that they're not part of the problem,
+ your startup files (.exrc, .nexrc) and the environment variable
+ (EXINIT, NEXINIT) values. (Cutting and pasting the output
+ of ":set all" is usually sufficient.)
+
+If you want to do your own debugging, recompile the program with DEBUG
+defined. (Configuring with --enable-debug will do this for you.) This
+turns on the additional command-line option -D, that takes either s or w
+as an argument. The option -Ds causes nvi to ignore the EXINIT and
+.exrc files on startup, and -Dw causes nvi to print out the process id
+and wait for you to enter a <carriage-return> to continue.
+
+If you're running a memory checker (e.g. Purify) on nvi, you will first
+want to recompile everything with "-DPURIFY" set in the CFLAGS. This
+initializes allocated pages in the DB code, and free's allocated memory
+at the end of the nvi execution.
diff --git a/contrib/nvi/build/ExtUtils/Embed.pm b/contrib/nvi/build/ExtUtils/Embed.pm
new file mode 100644
index 000000000000..04525c18c908
--- /dev/null
+++ b/contrib/nvi/build/ExtUtils/Embed.pm
@@ -0,0 +1,473 @@
+# $Id: Embed.pm,v 1.17 1996/07/02 13:48:17 dougm Exp $
+require 5.002;
+
+package ExtUtils::Embed;
+require Exporter;
+require FileHandle;
+use Config;
+use Getopt::Std;
+
+#Only when we need them
+#require ExtUtils::MakeMaker;
+#require ExtUtils::Liblist;
+
+use vars qw(@ISA @EXPORT $VERSION
+ @Extensions $Verbose $lib_ext
+ $opt_o $opt_s
+ );
+use strict;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.17 $ =~ /(\d+)\.(\d+)/);
+#for the namespace change
+$Devel::embed::VERSION = "99.99";
+
+sub Version { $VERSION; }
+
+@ISA = qw(Exporter);
+@EXPORT = qw(&xsinit &ldopts
+ &ccopts &ccflags &ccdlflags &perl_inc
+ &xsi_header &xsi_protos &xsi_body);
+
+#let's have Miniperl borrow from us instead
+#require ExtUtils::Miniperl;
+#*canon = \&ExtUtils::Miniperl::canon;
+
+$Verbose = 0;
+$lib_ext = $Config{lib_ext} || '.a';
+
+sub xsinit {
+ my($file, $std, $mods) = @_;
+ my($fh,@mods,%seen);
+ $file ||= "perlxsi.c";
+
+ if (@_) {
+ @mods = @$mods if $mods;
+ }
+ else {
+ getopts('o:s:');
+ $file = $opt_o if defined $opt_o;
+ $std = $opt_s if defined $opt_s;
+ @mods = @ARGV;
+ }
+ $std = 1 unless scalar @mods;
+
+ if ($file eq "STDOUT") {
+ $fh = \*STDOUT;
+ }
+ else {
+ $fh = new FileHandle "> $file";
+ }
+
+ push(@mods, static_ext()) if defined $std;
+ @mods = grep(!$seen{$_}++, @mods);
+
+ print $fh &xsi_header();
+ print $fh "EXTERN_C void xs_init _((void));\n\n";
+ print $fh &xsi_protos(@mods);
+
+ print $fh "\nEXTERN_C void\nxs_init()\n{\n";
+ print $fh &xsi_body(@mods);
+ print $fh "}\n";
+
+}
+
+sub xsi_header {
+ return <<EOF;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#ifdef __cplusplus
+}
+# ifndef EXTERN_C
+# define EXTERN_C extern "C"
+# endif
+#else
+# ifndef EXTERN_C
+# define EXTERN_C extern
+# endif
+#endif
+
+EOF
+}
+
+sub xsi_protos {
+ my(@exts) = @_;
+ my(@retval,%seen);
+
+ foreach $_ (@exts){
+ my($pname) = canon('/', $_);
+ my($mname, $cname);
+ ($mname = $pname) =~ s!/!::!g;
+ ($cname = $pname) =~ s!/!__!g;
+ my($ccode) = "EXTERN_C void boot_${cname} _((CV* cv));\n";
+ next if $seen{$ccode}++;
+ push(@retval, $ccode);
+ }
+ return join '', @retval;
+}
+
+sub xsi_body {
+ my(@exts) = @_;
+ my($pname,@retval,%seen);
+ my($dl) = canon('/','DynaLoader');
+ push(@retval, "\tdXSUB_SYS;\n") if $] > 5.002;
+ push(@retval, "\tchar *file = __FILE__;\n\n");
+
+ foreach $_ (@exts){
+ my($pname) = canon('/', $_);
+ my($mname, $cname, $ccode);
+ ($mname = $pname) =~ s!/!::!g;
+ ($cname = $pname) =~ s!/!__!g;
+ if ($pname eq $dl){
+ # Must NOT install 'DynaLoader::boot_DynaLoader' as 'bootstrap'!
+ # boot_DynaLoader is called directly in DynaLoader.pm
+ $ccode = "\t/* DynaLoader is a special case */\n\tnewXS(\"${mname}::boot_${cname}\", boot_${cname}, file);\n";
+ push(@retval, $ccode) unless $seen{$ccode}++;
+ } else {
+ $ccode = "\tnewXS(\"${mname}::bootstrap\", boot_${cname}, file);\n";
+ push(@retval, $ccode) unless $seen{$ccode}++;
+ }
+ }
+ return join '', @retval;
+}
+
+sub static_ext {
+ unless (scalar @Extensions) {
+ @Extensions = sort split /\s+/, $Config{static_ext};
+ unshift @Extensions, qw(DynaLoader);
+ }
+ @Extensions;
+}
+
+sub ldopts {
+ require ExtUtils::MakeMaker;
+ require ExtUtils::Liblist;
+ my($std,$mods,$link_args,$path) = @_;
+ my(@mods,@link_args,@argv);
+ my($dllib,$config_libs,@potential_libs,@path);
+ local($") = ' ' unless $" eq ' ';
+ my $MM = bless {} => 'MY';
+ if (scalar @_) {
+ @link_args = @$link_args if $link_args;
+ @mods = @$mods if $mods;
+ }
+ else {
+ @argv = @ARGV;
+ #hmm
+ while($_ = shift @argv) {
+ /^-std$/ && do { $std = 1; next; };
+ /^--$/ && do { @link_args = @argv; last; };
+ /^-I(.*)/ && do { $path = $1 || shift @argv; next; };
+ push(@mods, $_);
+ }
+ }
+ $std = 1 unless scalar @link_args;
+ @path = $path ? split(/:/, $path) : @INC;
+
+ push(@potential_libs, @link_args) if scalar @link_args;
+ push(@potential_libs, $Config{libs}) if defined $std;
+
+ push(@mods, static_ext()) if $std;
+
+ my($mod,@ns,$root,$sub,$extra,$archive,@archives);
+ print STDERR "Searching (@path) for archives\n" if $Verbose;
+ foreach $mod (@mods) {
+ @ns = split('::', $mod);
+ $sub = $ns[-1];
+ $root = $MM->catdir(@ns);
+
+ print STDERR "searching for '$sub${lib_ext}'\n" if $Verbose;
+ foreach (@path) {
+ next unless -e ($archive = $MM->catdir($_,"auto",$root,"$sub$lib_ext"));
+ push @archives, $archive;
+ if(-e ($extra = $MM->catdir($_,"auto",$root,"extralibs.ld"))) {
+ local(*FH);
+ if(open(FH, $extra)) {
+ my($libs) = <FH>; chomp $libs;
+ push @potential_libs, split /\s+/, $libs;
+ }
+ else {
+ warn "Couldn't open '$extra'";
+ }
+ }
+ last;
+ }
+ }
+ #print STDERR "\@potential_libs = @potential_libs\n";
+
+ my($extralibs, $bsloadlibs, $ldloadlibs, $ld_run_path) =
+ $MM->ext(join ' ',
+ $MM->catdir("-L$Config{archlib}", "CORE"), " -lperl",
+ @potential_libs);
+
+ my $ld_or_bs = $bsloadlibs || $ldloadlibs;
+ print STDERR "bs: $bsloadlibs ** ld: $ldloadlibs" if $Verbose;
+ my $linkage = "$Config{ldflags} @archives $ld_or_bs";
+ print STDERR "ldopts: '$linkage'\n" if $Verbose;
+
+ return $linkage if scalar @_;
+ print "$linkage\n";
+}
+
+sub ccflags {
+ print " $Config{ccflags} ";
+}
+
+sub ccdlflags {
+ print " $Config{ccdlflags} ";
+}
+
+sub perl_inc {
+ print " -I$Config{archlib}/CORE ";
+}
+
+sub ccopts {
+ ccflags;
+ ccdlflags;
+ perl_inc;
+}
+
+sub canon {
+ my($as, @ext) = @_;
+ foreach(@ext) {
+ # might be X::Y or lib/auto/X/Y/Y.a
+ next if s!::!/!g;
+ s:^(lib|ext)/(auto/)?::;
+ s:/\w+\.\w+$::;
+ }
+ grep(s:/:$as:, @ext) if ($as ne '/');
+ @ext;
+}
+
+__END__
+
+=head1 NAME
+
+ExtUtils::Embed - Utilities for embedding Perl in C/C++ applications
+
+=head1 SYNOPSIS
+
+
+ perl -MExtUtils::Embed -e xsinit
+ perl -MExtUtils::Embed -e ldopts
+
+=head1 DESCRIPTION
+
+ExtUtils::Embed provides utility functions for embedding a Perl interpreter
+and extensions in your C/C++ applications.
+Typically, an application B<Makefile> will invoke ExtUtils::Embed
+functions while building your application.
+
+=head1 @EXPORT
+
+ExtUtils::Embed exports the following functions:
+
+L<xsinit()>, L<ldopts()>, L<ccopts()>, L<perl_inc()>, L<ccflags()>,
+L<ccdlflags()>, L<xsi_header()>, L<xsi_protos()>, L<xsi_body()>
+
+=head1 FUNCTIONS
+
+=item xsinit()
+
+Generate C/C++ code for the XS intializer function.
+
+When invoked as C<`perl -MExtUtils::Embed -e xsinit --`>
+the following options are recognized:
+
+B<-o> <output filename> (Defaults to B<perlxsi.c>)
+
+B<-o STDOUT> will print to STDOUT.
+
+B<-std> (Write code for extensions that are linked with the current Perl.)
+
+Any additional arguments are expected to be names of modules
+to generate code for.
+
+When invoked with parameters the following are accepted and optional:
+
+C<xsinit($filename,$std,[@modules])>
+
+Where,
+
+B<$filename> is equivalent to the B<-o> option.
+
+B<$std> is boolean, equivalent to the B<-std> option.
+
+B<[@modules]> is an array ref, same as additional arguments mentioned above.
+
+=item Examples
+
+
+ perl -MExtUtils::Embed -e xsinit -- -o xsinit.c Socket
+
+
+This will generate code with an B<xs_init> function that glues the perl B<Socket::bootstrap> function
+to the C B<boot_Socket> function and writes it to a file named "xsinit.c".
+
+Note that B<DynaLoader> is a special case where it must call B<boot_DynaLoader> directly.
+
+ perl -MExtUtils::Embed -e xsinit
+
+
+This will generate code for linking with B<DynaLoader> and
+each static extension found in B<$Config{static_ext}>.
+The code is written to the default file name B<perlxsi.c>.
+
+
+ perl -MExtUtils::Embed -e xsinit -- -o xsinit.c -std DBI DBD::Oracle
+
+
+Here, code is written for all the currently linked extensions along with code
+for B<DBI> and B<DBD::Oracle>.
+
+If you have a working B<DynaLoader> then there is rarely any need to statically link in any
+other extensions.
+
+=item ldopts()
+
+Output arguments for linking the Perl library and extensions to your
+application.
+
+When invoked as C<`perl -MExtUtils::Embed -e ldopts --`>
+the following options are recognized:
+
+B<-std>
+
+Output arguments for linking the Perl library and any extensions linked
+with the current Perl.
+
+B<-I> <path1:path2>
+
+Search path for ModuleName.a archives.
+Default path is B<@INC>.
+Library archives are expected to be found as
+B</some/path/auto/ModuleName/ModuleName.a>
+For example, when looking for B<Socket.a> relative to a search path,
+we should find B<auto/Socket/Socket.a>
+
+When looking for B<DBD::Oracle> relative to a search path,
+we should find B<auto/DBD/Oracle/Oracle.a>
+
+Keep in mind, you can always supply B</my/own/path/ModuleName.a>
+as an additional linker argument.
+
+B<--> <list of linker args>
+
+Additional linker arguments to be considered.
+
+Any additional arguments found before the B<--> token
+are expected to be names of modules to generate code for.
+
+When invoked with parameters the following are accepted and optional:
+
+C<ldopts($std,[@modules],[@link_args],$path)>
+
+Where,
+
+B<$std> is boolean, equivalent to the B<-std> option.
+
+B<[@modules]> is equivalent to additional arguments found before the B<--> token.
+
+B<[@link_args]> is equivalent to arguments found after the B<--> token.
+
+B<$path> is equivalent to the B<-I> option.
+
+In addition, when ldopts is called with parameters, it will return the argument string
+rather than print it to STDOUT.
+
+=item Examples
+
+
+ perl -MExtUtils::Embed -e ldopts
+
+
+This will print arguments for linking with B<libperl.a>, B<DynaLoader> and
+extensions found in B<$Config{static_ext}>. This includes libraries
+found in B<$Config{libs}> and the first ModuleName.a library
+for each extension that is found by searching B<@INC> or the path
+specifed by the B<-I> option.
+In addition, when ModuleName.a is found, additional linker arguments
+are picked up from the B<extralibs.ld> file in the same directory.
+
+
+ perl -MExtUtils::Embed -e ldopts -- -std Socket
+
+
+This will do the same as the above example, along with printing additional arguments for linking with the B<Socket> extension.
+
+
+ perl -MExtUtils::Embed -e ldopts -- DynaLoader
+
+
+This will print arguments for linking with just the B<DynaLoader> extension
+and B<libperl.a>.
+
+
+ perl -MExtUtils::Embed -e ldopts -- -std Msql -- -L/usr/msql/lib -lmsql
+
+
+Any arguments after the second '--' token are additional linker
+arguments that will be examined for potential conflict. If there is no
+conflict, the additional arguments will be part of the output.
+
+
+=item perl_inc()
+
+For including perl header files this function simply prints:
+
+ -I $Config{archlib}/CORE
+
+So, rather than having to say:
+
+ perl -MConfig -e 'print "-I $Config{archlib}/CORE"'
+
+Just say:
+
+ perl -MExtUtils::Embed -e perl_inc
+
+=item ccflags(), ccdlflags()
+
+These functions simply print $Config{ccflags} and $Config{ccdlflags}
+
+=item ccopts()
+
+This function combines perl_inc(), ccflags() and ccdlflags() into one.
+
+=item xsi_header()
+
+This function simply returns a string defining the same B<EXTERN_C> macro as
+B<perlmain.c> along with #including B<perl.h> and B<EXTERN.h>.
+
+=item xsi_protos(@modules)
+
+This function returns a string of B<boot_$ModuleName> prototypes for each @modules.
+
+=item xsi_body(@modules)
+
+This function returns a string of calls to B<newXS()> that glue the module B<bootstrap>
+function to B<boot_ModuleName> for each @modules.
+
+B<xsinit()> uses the xsi_* functions to generate most of it's code.
+
+=head1 EXAMPLES
+
+For examples on how to use B<ExtUtils::Embed> for building C/C++ applications
+with embedded perl, see the eg/ directory and the I<perlembed> man page.
+
+=head1 SEE ALSO
+
+the I<perlembed> man page
+
+=head1 AUTHOR
+
+Doug MacEachern <dougm@osf.org>
+
+Based on ideas from Tim Bunce <Tim.Bunce@ig.co.uk> and
+B<minimod.pl> by Andreas Koenig <k@anna.in-berlin.de> and Tim Bunce.
+
+=cut
+
diff --git a/contrib/nvi/build/Makefile.in b/contrib/nvi/build/Makefile.in
new file mode 100644
index 000000000000..54025e7d3b9d
--- /dev/null
+++ b/contrib/nvi/build/Makefile.in
@@ -0,0 +1,630 @@
+# @(#)Makefile.in 8.75 (Berkeley) 10/23/96
+
+srcdir= @srcdir@/..
+CC= @CC@
+OPTFLAG=@OPTFLAG@
+CFLAGS= -c $(OPTFLAG) @CFLAGS@ -I. -I$(srcdir)/include @CPPFLAGS@
+LDFLAGS=@LDFLAGS@
+PERL= @vi_cv_path_perl@
+PERLLIB=@vi_cv_perllib@
+SHRPENV=@shrpenv@
+
+# Objects
+COBJS= addbytes.o addch.o box.o clear.o clrtobot.o clrtoeol.o \
+ cr_put.o ctrace.o cur_hash.o curses.o delch.o deleteln.o delwin.o \
+ erase.o fullname.o getch.o getstr.o id_subwins.o idlok.o initscr.o \
+ insch.o insertln.o longname.o move.o mvwin.o newwin.o overlay.o \
+ overwrite.o putchar.o refresh.o scroll.o setterm.o standout.o \
+ toucholap.o touchwin.o tscroll.o tstp.o tty.o unctrl.o waddnstr.o
+CLOBJS= cl_bsd.o cl_funcs.o cl_main.o cl_read.o cl_screen.o cl_term.o
+DBOBJS= db.o mpool.o \
+ bt_close.o bt_conv.o bt_debug.o bt_delete.o bt_get.o bt_open.o \
+ bt_overflow.o bt_page.o bt_put.o bt_search.o bt_seq.o \
+ bt_split.o bt_utils.o \
+ rec_close.o rec_delete.o rec_get.o rec_open.o rec_put.o \
+ rec_search.o rec_seq.o rec_utils.o
+REOBJS= regcomp.o regerror.o regexec.o regfree.o
+TKOBJS= tk_funcs.o tk_main.o tk_read.o tk_screen.o tk_term.o tk_util.o
+VIOBJS= cut.o delete.o ex.o ex_abbrev.o ex_append.o ex_args.o ex_argv.o \
+ ex_at.o ex_bang.o ex_cd.o ex_cmd.o ex_cscope.o ex_delete.o \
+ ex_display.o ex_edit.o ex_equal.o ex_file.o ex_filter.o \
+ ex_global.o ex_init.o ex_join.o ex_map.o ex_mark.o ex_mkexrc.o \
+ ex_move.o ex_open.o ex_perl.o ex_preserve.o ex_print.o ex_put.o \
+ ex_quit.o ex_read.o ex_screen.o ex_script.o ex_set.o ex_shell.o \
+ ex_shift.o ex_source.o ex_stop.o ex_subst.o ex_tag.o ex_tcl.o \
+ ex_txt.o ex_undo.o ex_usage.o ex_util.o ex_version.o ex_visual.o \
+ ex_write.o ex_yank.o ex_z.o exf.o getc.o key.o line.o log.o main.o \
+ mark.o msg.o options.o options_f.o put.o recover.o screen.o \
+ search.o seq.o util.o v_at.o v_ch.o v_cmd.o v_delete.o v_ex.o \
+ v_increment.o v_init.o v_itxt.o v_left.o v_mark.o v_match.o \
+ v_paragraph.o v_put.o v_redraw.o v_replace.o v_right.o v_screen.o \
+ v_scroll.o v_search.o v_section.o v_sentence.o v_status.o v_txt.o \
+ v_ulcase.o v_undo.o v_util.o v_word.o v_xchar.o v_yank.o v_z.o \
+ v_zexit.o vi.o vs_line.o vs_msg.o vs_refresh.o vs_relative.o \
+ vs_smap.o vs_split.o
+
+all: nvi @tknvi@
+
+NVIALL= $(CLOBJS) $(VIOBJS) @cobjs@ @LIBOBJS@
+nvi nex: $(NVIALL)
+ $(SHRPENV) $(CC) $(LDFLAGS) -o $@ $(NVIALL) @LIBS@
+ -rm -f nex
+ ln $@ nex
+
+TKALL= $(TKOBJS) $(VIOBJS) @LIBOBJS@
+tknvi: $(TKALL)
+ $(SHRPENV) $(CC) $(LDFLAGS) -o $@ $(TKALL) @TKLIBS@
+
+chmod= @vi_cv_path_chmod@
+cp= @vi_cv_path_cp@
+ln= @vi_cv_path_ln@
+mkdir= @vi_cv_path_mkdir@
+rm= @vi_cv_path_rm@
+strip= @vi_cv_path_strip@
+
+prefix= @prefix@
+bindir= @bindir@
+datadir=@datadir@
+mandir= @mandir@
+exec_prefix=@exec_prefix@
+
+dmode= 755
+emode= 555
+fmode= 444
+
+transform=@program_transform_name@
+
+install: all install_common
+ @echo "Installing vi, ex, view: $(bindir) ..."
+ [ -d $(bindir) ] || \
+ ($(mkdir) $(bindir) && $(chmod) $(dmode) $(bindir))
+ cd $(bindir) && $(rm) -f `echo vi | sed '$(transform)'`
+ $(cp) nvi $(bindir)/`echo vi | sed '$(transform)'`
+ cd $(bindir) && [ -f $(strip) ] && \
+ $(strip) `echo vi | sed '$(transform)'`
+ cd $(bindir) && $(chmod) $(emode) `echo vi | sed '$(transform)'`
+ cd $(bindir) && $(rm) -f `echo ex | sed '$(transform)'`
+ cd $(bindir) && $(rm) -f `echo view | sed '$(transform)'`
+ cd $(bindir) && $(ln) \
+ `echo vi | sed '$(transform)'` `echo ex | sed '$(transform)'`
+ cd $(bindir) && $(ln) \
+ `echo vi | sed '$(transform)'` `echo view | sed '$(transform)'`
+ [ -d $(mandir) ] || \
+ ($(mkdir) $(mandir) && $(chmod) $(dmode) $(mandir))
+ [ -d $(mandir)/cat1 ] || \
+ ($(mkdir) $(mandir)/cat1 && $(chmod) $(dmode) $(mandir)/cat1)
+ @echo "Installing man pages: $(mandir) ..."
+ cd $(mandir)/cat1 && $(rm) -f `echo vi.0 | sed '$(transform)'`
+ $(cp) $(srcdir)/docs/USD.doc/vi.man/vi.0 \
+ $(mandir)/cat1/`echo vi.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(chmod) $(fmode) `echo vi.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(rm) -f `echo ex.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(rm) -f `echo view.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(ln) \
+ `echo vi.0 | sed '$(transform)'` `echo ex.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(ln) \
+ `echo vi.0 | sed '$(transform)'` `echo view.0 | sed '$(transform)'`
+ [ -d $(mandir)/man1 ] || \
+ ($(mkdir) $(mandir)/man1 && $(chmod) $(dmode) $(mandir)/man1)
+ cd $(mandir)/man1 && $(rm) -f `echo vi.1 | sed '$(transform)'`
+ $(cp) $(srcdir)/docs/USD.doc/vi.man/vi.1 \
+ $(mandir)/man1/`echo vi.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(chmod) $(fmode) `echo vi.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(rm) -f `echo ex.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(rm) -f `echo view.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(ln) \
+ `echo vi.1 | sed '$(transform)'` `echo ex.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(ln) \
+ `echo vi.1 | sed '$(transform)'` `echo view.1 | sed '$(transform)'`
+
+cat= dutch english french german ru_SU.KOI8-R spanish swedish
+install_common:
+ [ -f $(chmod) ]
+ [ -f $(cp) ]
+ [ -f $(ln) ]
+ [ -f $(mkdir) ]
+ [ -f $(rm) ]
+ [ -d $(prefix) ] || \
+ ($(mkdir) $(prefix) && $(chmod) $(dmode) $(prefix))
+ [ -d $(exec_prefix) ] || \
+ ($(mkdir) $(exec_prefix) && $(chmod) $(dmode) $(exec_prefix))
+ [ -d $(datadir) ] || \
+ ($(mkdir) $(datadir) && $(chmod) $(dmode) $(datadir))
+ $(rm) -rf $(datadir)/vi
+ $(mkdir) $(datadir)/vi && $(chmod) $(dmode) $(datadir)/vi
+ @echo "Installing message catalogs: $(datadir)/vi/catalog ..."
+ $(mkdir) $(datadir)/vi/catalog && \
+ $(chmod) $(dmode) $(datadir)/vi/catalog
+ (cd $(srcdir)/catalog && $(cp) $(cat) $(datadir)/vi/catalog && \
+ cd $(datadir)/vi/catalog && $(chmod) $(fmode) *)
+ @echo "Installing Perl scripts: $(datadir)/vi/perl ..."
+ $(mkdir) $(datadir)/vi/perl && $(chmod) $(dmode) $(datadir)/vi/perl
+ [ -f VI.pm ] && $(cp) VI.pm $(datadir)/vi/perl && \
+ cd $(datadir)/vi/perl && $(chmod) $(fmode) VI.pm)
+ (cd $(srcdir)/perl_scripts && $(cp) *.pl $(datadir)/vi/perl && \
+ cd $(datadir)/vi/perl && $(chmod) $(fmode) *.pl)
+ @echo "Installing Tcl scripts: $(datadir)/vi/tcl ..."
+ $(mkdir) $(datadir)/vi/tcl && $(chmod) $(dmode) $(datadir)/vi/tcl
+ (cd $(srcdir)/tcl_scripts && $(cp) *.tcl $(datadir)/vi/tcl && \
+ cd $(datadir)/vi/tcl && $(chmod) $(fmode) *.tcl)
+ @echo "Installing recover script: $(datadir)/vi/recover ..."
+ ($(cp) recover $(datadir)/vi/recover && \
+ $(chmod) $(emode) $(datadir)/vi/recover)
+
+uninstall:
+ $(rm) -rf $(datadir)/vi
+ cd $(bindir) && $(rm) -f `echo ex | sed '$(transform)'`
+ cd $(bindir) && $(rm) -f `echo vi | sed '$(transform)'`
+ cd $(bindir) && $(rm) -f `echo view | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(rm) -f `echo ex.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(rm) -f `echo vi.0 | sed '$(transform)'`
+ cd $(mandir)/cat1 && $(rm) -f `echo view.0 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(rm) -f `echo ex.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(rm) -f `echo vi.1 | sed '$(transform)'`
+ cd $(mandir)/man1 && $(rm) -f `echo view.1 | sed '$(transform)'`
+
+docs:
+ cd $(srcdir)/docs/USD.doc/vi.ref && $(MAKE)
+ cd $(srcdir)/docs/USD.doc/vi.man && $(MAKE)
+ cd $(srcdir)/docs/USD.doc/edit && $(MAKE)
+ cd $(srcdir)/docs/USD.doc/exref && $(MAKE)
+ cd $(srcdir)/docs/USD.doc/vitut && $(MAKE)
+
+clean:
+ -rm -f *.core *.o memcpy.c perl.c
+ -rm -f nex nvi tknvi core
+ -rm -f $(COBJS) $(CLOBJS) $(DBOBJS) $(REOBJS)
+ -rm -f $(TKOBJS) $(VIOBJS)
+
+cleandocs:
+ cd $(srcdir)/docs/USD.doc/vi.ref && $(MAKE) clean
+ cd $(srcdir)/docs/USD.doc/vi.man && $(MAKE) clean
+ cd $(srcdir)/docs/USD.doc/edit && $(MAKE) clean
+ cd $(srcdir)/docs/USD.doc/exref && $(MAKE) clean
+ cd $(srcdir)/docs/USD.doc/vitut && $(MAKE) clean
+
+distclean maintainer-clean realclean: clean cleandocs
+ -rm -f Makefile config.cache config.h config.log config.status
+ -rm -f pathnames.h port.h
+
+# Vi curses sources.
+cl_bsd.o: $(srcdir)/cl/cl_bsd.c
+ $(CC) $(CFLAGS) $?
+cl_funcs.o: $(srcdir)/cl/cl_funcs.c
+ $(CC) $(CFLAGS) $?
+cl_main.o: $(srcdir)/cl/cl_main.c
+ $(CC) $(CFLAGS) $?
+cl_read.o: $(srcdir)/cl/cl_read.c
+ $(CC) $(CFLAGS) $?
+cl_screen.o: $(srcdir)/cl/cl_screen.c
+ $(CC) $(CFLAGS) $?
+cl_term.o: $(srcdir)/cl/cl_term.c
+ $(CC) $(CFLAGS) $?
+
+# Vi Tk sources.
+tk_funcs.o: $(srcdir)/tk/tk_funcs.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+tk_main.o: $(srcdir)/tk/tk_main.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+tk_read.o: $(srcdir)/tk/tk_read.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+tk_screen.o: $(srcdir)/tk/tk_screen.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+tk_term.o: $(srcdir)/tk/tk_term.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+tk_util.o: $(srcdir)/tk/tk_util.c
+ $(CC) $(CFLAGS) @XINCS@ $?
+
+# Vi Tcl/Perl interpreter sources.
+api.o: $(srcdir)/common/api.c
+ $(CC) $(CFLAGS) $?
+perl.c: $(srcdir)/perl_api/perl.xs $(srcdir)/perl_api/typemap
+ echo "#define _PATH_PERLSCRIPTS \"$(datadir)/vi/perl\"" > $@
+ $(PERL) $(PERLLIB)/ExtUtils/xsubpp -typemap \
+ $(PERLLIB)/ExtUtils/typemap $(srcdir)/perl_api/perl.xs >> $@
+ ($(PERL) -ne 'print "sub $$1 {\$$curscr->$$1(\@_)}\n" \
+ if /newXS\("VI::([^":]*)"/;' $@ ; echo "1;") > VI.pm
+perl.o: perl.c
+ $(CC) $(CFLAGS) $?
+perlsfio.o: $(srcdir)/perl_api/perlsfio.c
+ $(CC) $(CFLAGS) $?
+tcl.o: $(srcdir)/tcl_api/tcl.c
+ $(CC) $(CFLAGS) $?
+
+# Vi sources.
+cut.o: $(srcdir)/common/cut.c
+ $(CC) $(CFLAGS) $?
+delete.o: $(srcdir)/common/delete.c
+ $(CC) $(CFLAGS) $?
+exf.o: $(srcdir)/common/exf.c
+ $(CC) $(CFLAGS) $?
+key.o: $(srcdir)/common/key.c
+ $(CC) $(CFLAGS) $?
+line.o: $(srcdir)/common/line.c
+ $(CC) $(CFLAGS) $?
+log.o: $(srcdir)/common/log.c
+ $(CC) $(CFLAGS) $?
+main.o: $(srcdir)/common/main.c
+ $(CC) $(CFLAGS) $?
+mark.o: $(srcdir)/common/mark.c
+ $(CC) $(CFLAGS) $?
+msg.o: $(srcdir)/common/msg.c
+ $(CC) $(CFLAGS) $?
+options.o: $(srcdir)/common/options.c
+ $(CC) $(CFLAGS) $?
+options_f.o: $(srcdir)/common/options_f.c
+ $(CC) $(CFLAGS) $?
+put.o: $(srcdir)/common/put.c
+ $(CC) $(CFLAGS) $?
+screen.o: $(srcdir)/common/screen.c
+ $(CC) $(CFLAGS) $?
+search.o: $(srcdir)/common/search.c
+ $(CC) $(CFLAGS) $?
+seq.o: $(srcdir)/common/seq.c
+ $(CC) $(CFLAGS) $?
+recover.o: $(srcdir)/common/recover.c
+ $(CC) $(CFLAGS) $?
+util.o: $(srcdir)/common/util.c
+ $(CC) $(CFLAGS) $?
+ex.o: $(srcdir)/ex/ex.c
+ $(CC) $(CFLAGS) $?
+ex_abbrev.o: $(srcdir)/ex/ex_abbrev.c
+ $(CC) $(CFLAGS) $?
+ex_append.o: $(srcdir)/ex/ex_append.c
+ $(CC) $(CFLAGS) $?
+ex_args.o: $(srcdir)/ex/ex_args.c
+ $(CC) $(CFLAGS) $?
+ex_argv.o: $(srcdir)/ex/ex_argv.c
+ $(CC) $(CFLAGS) $?
+ex_at.o: $(srcdir)/ex/ex_at.c
+ $(CC) $(CFLAGS) $?
+ex_bang.o: $(srcdir)/ex/ex_bang.c
+ $(CC) $(CFLAGS) $?
+ex_cd.o: $(srcdir)/ex/ex_cd.c
+ $(CC) $(CFLAGS) $?
+ex_cmd.o: $(srcdir)/ex/ex_cmd.c
+ $(CC) $(CFLAGS) $?
+ex_cscope.o: $(srcdir)/ex/ex_cscope.c
+ $(CC) $(CFLAGS) $?
+ex_delete.o: $(srcdir)/ex/ex_delete.c
+ $(CC) $(CFLAGS) $?
+ex_digraph.o: $(srcdir)/ex/ex_digraph.c
+ $(CC) $(CFLAGS) $?
+ex_display.o: $(srcdir)/ex/ex_display.c
+ $(CC) $(CFLAGS) $?
+ex_edit.o: $(srcdir)/ex/ex_edit.c
+ $(CC) $(CFLAGS) $?
+ex_equal.o: $(srcdir)/ex/ex_equal.c
+ $(CC) $(CFLAGS) $?
+ex_file.o: $(srcdir)/ex/ex_file.c
+ $(CC) $(CFLAGS) $?
+ex_filter.o: $(srcdir)/ex/ex_filter.c
+ $(CC) $(CFLAGS) $?
+ex_global.o: $(srcdir)/ex/ex_global.c
+ $(CC) $(CFLAGS) $?
+ex_init.o: $(srcdir)/ex/ex_init.c
+ $(CC) $(CFLAGS) $?
+ex_join.o: $(srcdir)/ex/ex_join.c
+ $(CC) $(CFLAGS) $?
+ex_map.o: $(srcdir)/ex/ex_map.c
+ $(CC) $(CFLAGS) $?
+ex_mark.o: $(srcdir)/ex/ex_mark.c
+ $(CC) $(CFLAGS) $?
+ex_mkexrc.o: $(srcdir)/ex/ex_mkexrc.c
+ $(CC) $(CFLAGS) $?
+ex_move.o: $(srcdir)/ex/ex_move.c
+ $(CC) $(CFLAGS) $?
+ex_open.o: $(srcdir)/ex/ex_open.c
+ $(CC) $(CFLAGS) $?
+ex_perl.o: $(srcdir)/ex/ex_perl.c
+ $(CC) $(CFLAGS) $?
+ex_preserve.o: $(srcdir)/ex/ex_preserve.c
+ $(CC) $(CFLAGS) $?
+ex_print.o: $(srcdir)/ex/ex_print.c
+ $(CC) $(CFLAGS) $?
+ex_put.o: $(srcdir)/ex/ex_put.c
+ $(CC) $(CFLAGS) $?
+ex_quit.o: $(srcdir)/ex/ex_quit.c
+ $(CC) $(CFLAGS) $?
+ex_read.o: $(srcdir)/ex/ex_read.c
+ $(CC) $(CFLAGS) $?
+ex_screen.o: $(srcdir)/ex/ex_screen.c
+ $(CC) $(CFLAGS) $?
+ex_script.o: $(srcdir)/ex/ex_script.c
+ $(CC) $(CFLAGS) $?
+ex_set.o: $(srcdir)/ex/ex_set.c
+ $(CC) $(CFLAGS) $?
+ex_shell.o: $(srcdir)/ex/ex_shell.c
+ $(CC) $(CFLAGS) $?
+ex_shift.o: $(srcdir)/ex/ex_shift.c
+ $(CC) $(CFLAGS) $?
+ex_source.o: $(srcdir)/ex/ex_source.c
+ $(CC) $(CFLAGS) $?
+ex_stop.o: $(srcdir)/ex/ex_stop.c
+ $(CC) $(CFLAGS) $?
+ex_subst.o: $(srcdir)/ex/ex_subst.c
+ $(CC) $(CFLAGS) $?
+ex_tag.o: $(srcdir)/ex/ex_tag.c
+ $(CC) $(CFLAGS) $?
+ex_tcl.o: $(srcdir)/ex/ex_tcl.c
+ $(CC) $(CFLAGS) $?
+ex_txt.o: $(srcdir)/ex/ex_txt.c
+ $(CC) $(CFLAGS) $?
+ex_undo.o: $(srcdir)/ex/ex_undo.c
+ $(CC) $(CFLAGS) $?
+ex_usage.o: $(srcdir)/ex/ex_usage.c
+ $(CC) $(CFLAGS) $?
+ex_util.o: $(srcdir)/ex/ex_util.c
+ $(CC) $(CFLAGS) $?
+ex_version.o: $(srcdir)/ex/ex_version.c
+ $(CC) $(CFLAGS) $?
+ex_visual.o: $(srcdir)/ex/ex_visual.c
+ $(CC) $(CFLAGS) $?
+ex_write.o: $(srcdir)/ex/ex_write.c
+ $(CC) $(CFLAGS) $?
+ex_yank.o: $(srcdir)/ex/ex_yank.c
+ $(CC) $(CFLAGS) $?
+ex_z.o: $(srcdir)/ex/ex_z.c
+ $(CC) $(CFLAGS) $?
+getc.o: $(srcdir)/vi/getc.c
+ $(CC) $(CFLAGS) $?
+v_at.o: $(srcdir)/vi/v_at.c
+ $(CC) $(CFLAGS) $?
+v_ch.o: $(srcdir)/vi/v_ch.c
+ $(CC) $(CFLAGS) $?
+v_cmd.o: $(srcdir)/vi/v_cmd.c
+ $(CC) $(CFLAGS) $?
+v_delete.o: $(srcdir)/vi/v_delete.c
+ $(CC) $(CFLAGS) $?
+v_ex.o: $(srcdir)/vi/v_ex.c
+ $(CC) $(CFLAGS) $?
+v_increment.o: $(srcdir)/vi/v_increment.c
+ $(CC) $(CFLAGS) $?
+v_init.o: $(srcdir)/vi/v_init.c
+ $(CC) $(CFLAGS) $?
+v_itxt.o: $(srcdir)/vi/v_itxt.c
+ $(CC) $(CFLAGS) $?
+v_left.o: $(srcdir)/vi/v_left.c
+ $(CC) $(CFLAGS) $?
+v_mark.o: $(srcdir)/vi/v_mark.c
+ $(CC) $(CFLAGS) $?
+v_match.o: $(srcdir)/vi/v_match.c
+ $(CC) $(CFLAGS) $?
+v_paragraph.o: $(srcdir)/vi/v_paragraph.c
+ $(CC) $(CFLAGS) $?
+v_put.o: $(srcdir)/vi/v_put.c
+ $(CC) $(CFLAGS) $?
+v_redraw.o: $(srcdir)/vi/v_redraw.c
+ $(CC) $(CFLAGS) $?
+v_replace.o: $(srcdir)/vi/v_replace.c
+ $(CC) $(CFLAGS) $?
+v_right.o: $(srcdir)/vi/v_right.c
+ $(CC) $(CFLAGS) $?
+v_screen.o: $(srcdir)/vi/v_screen.c
+ $(CC) $(CFLAGS) $?
+v_scroll.o: $(srcdir)/vi/v_scroll.c
+ $(CC) $(CFLAGS) $?
+v_search.o: $(srcdir)/vi/v_search.c
+ $(CC) $(CFLAGS) $?
+v_section.o: $(srcdir)/vi/v_section.c
+ $(CC) $(CFLAGS) $?
+v_sentence.o: $(srcdir)/vi/v_sentence.c
+ $(CC) $(CFLAGS) $?
+v_status.o: $(srcdir)/vi/v_status.c
+ $(CC) $(CFLAGS) $?
+v_txt.o: $(srcdir)/vi/v_txt.c
+ $(CC) -c @no_op_OPTFLAG@ @CFLAGS@ -I. -I$(srcdir)/include @CPPFLAGS@ $?
+v_ulcase.o: $(srcdir)/vi/v_ulcase.c
+ $(CC) $(CFLAGS) $?
+v_undo.o: $(srcdir)/vi/v_undo.c
+ $(CC) $(CFLAGS) $?
+v_util.o: $(srcdir)/vi/v_util.c
+ $(CC) $(CFLAGS) $?
+v_word.o: $(srcdir)/vi/v_word.c
+ $(CC) $(CFLAGS) $?
+v_xchar.o: $(srcdir)/vi/v_xchar.c
+ $(CC) $(CFLAGS) $?
+v_yank.o: $(srcdir)/vi/v_yank.c
+ $(CC) $(CFLAGS) $?
+v_z.o: $(srcdir)/vi/v_z.c
+ $(CC) $(CFLAGS) $?
+v_zexit.o: $(srcdir)/vi/v_zexit.c
+ $(CC) $(CFLAGS) $?
+vi.o: $(srcdir)/vi/vi.c
+ $(CC) $(CFLAGS) $?
+vs_line.o: $(srcdir)/vi/vs_line.c
+ $(CC) $(CFLAGS) $?
+vs_msg.o: $(srcdir)/vi/vs_msg.c
+ $(CC) $(CFLAGS) $?
+vs_refresh.o: $(srcdir)/vi/vs_refresh.c
+ $(CC) $(CFLAGS) $?
+vs_relative.o: $(srcdir)/vi/vs_relative.c
+ $(CC) $(CFLAGS) $?
+vs_smap.o: $(srcdir)/vi/vs_smap.c
+ $(CC) $(CFLAGS) $?
+vs_split.o: $(srcdir)/vi/vs_split.c
+ $(CC) $(CFLAGS) $?
+
+addbytes.o: $(srcdir)/curses/addbytes.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+addch.o: $(srcdir)/curses/addch.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+box.o: $(srcdir)/curses/box.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+clear.o: $(srcdir)/curses/clear.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+clrtobot.o: $(srcdir)/curses/clrtobot.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+clrtoeol.o: $(srcdir)/curses/clrtoeol.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+cr_put.o: $(srcdir)/curses/cr_put.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+ctrace.o: $(srcdir)/curses/ctrace.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+cur_hash.o: $(srcdir)/curses/cur_hash.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+curses.o: $(srcdir)/curses/curses.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+delch.o: $(srcdir)/curses/delch.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+deleteln.o: $(srcdir)/curses/deleteln.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+delwin.o: $(srcdir)/curses/delwin.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+erase.o: $(srcdir)/curses/erase.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+fullname.o: $(srcdir)/curses/fullname.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+getch.o: $(srcdir)/curses/getch.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+getstr.o: $(srcdir)/curses/getstr.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+id_subwins.o: $(srcdir)/curses/id_subwins.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+idlok.o: $(srcdir)/curses/idlok.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+initscr.o: $(srcdir)/curses/initscr.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+insch.o: $(srcdir)/curses/insch.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+insertln.o: $(srcdir)/curses/insertln.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+longname.o: $(srcdir)/curses/longname.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+move.o: $(srcdir)/curses/move.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+mvwin.o: $(srcdir)/curses/mvwin.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+newwin.o: $(srcdir)/curses/newwin.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+overlay.o: $(srcdir)/curses/overlay.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+overwrite.o: $(srcdir)/curses/overwrite.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+putchar.o: $(srcdir)/curses/putchar.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+refresh.o: $(srcdir)/curses/refresh.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+scroll.o: $(srcdir)/curses/scroll.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+setterm.o: $(srcdir)/curses/setterm.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+standout.o: $(srcdir)/curses/standout.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+toucholap.o: $(srcdir)/curses/toucholap.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+touchwin.o: $(srcdir)/curses/touchwin.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+tscroll.o: $(srcdir)/curses/tscroll.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+tstp.o: $(srcdir)/curses/tstp.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+tty.o: $(srcdir)/curses/tty.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+unctrl.o: $(srcdir)/curses/unctrl.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+waddnstr.o: $(srcdir)/curses/waddnstr.c
+ $(CC) -D_CURSES_PRIVATE $(CFLAGS) $?
+
+# DB sources.
+db.o: $(srcdir)/db/db/db.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) $?
+mpool.o: $(srcdir)/db/mpool/mpool.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/mpool $?
+bt_close.o: $(srcdir)/db/btree/bt_close.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_conv.o: $(srcdir)/db/btree/bt_conv.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_debug.o: $(srcdir)/db/btree/bt_debug.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_delete.o: $(srcdir)/db/btree/bt_delete.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_get.o: $(srcdir)/db/btree/bt_get.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_open.o: $(srcdir)/db/btree/bt_open.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_overflow.o: $(srcdir)/db/btree/bt_overflow.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_page.o: $(srcdir)/db/btree/bt_page.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_put.o: $(srcdir)/db/btree/bt_put.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_search.o: $(srcdir)/db/btree/bt_search.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_seq.o: $(srcdir)/db/btree/bt_seq.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_split.o: $(srcdir)/db/btree/bt_split.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+bt_utils.o: $(srcdir)/db/btree/bt_utils.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/btree $?
+rec_close.o: $(srcdir)/db/recno/rec_close.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_delete.o: $(srcdir)/db/recno/rec_delete.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_get.o: $(srcdir)/db/recno/rec_get.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_open.o: $(srcdir)/db/recno/rec_open.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_put.o: $(srcdir)/db/recno/rec_put.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_search.o: $(srcdir)/db/recno/rec_search.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_seq.o: $(srcdir)/db/recno/rec_seq.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+rec_utils.o: $(srcdir)/db/recno/rec_utils.c
+ $(CC) -D__DBINTERFACE_PRIVATE $(CFLAGS) -I$(srcdir)/db/recno $?
+
+# Regular Expressions sources.
+regcomp.o: $(srcdir)/regex/regcomp.c
+ $(CC) -D__REGEX_PRIVATE $(CFLAGS) $?
+regerror.o: $(srcdir)/regex/regerror.c
+ $(CC) -D__REGEX_PRIVATE $(CFLAGS) $?
+regexec.o: $(srcdir)/regex/regexec.c
+ $(CC) -D__REGEX_PRIVATE $(CFLAGS) $?
+regfree.o: $(srcdir)/regex/regfree.c
+ $(CC) -D__REGEX_PRIVATE $(CFLAGS) $?
+
+# Random replacement and workaround sources.
+addnstr.o: $(srcdir)/clib/addnstr.c
+ $(CC) $(CFLAGS) $?
+bsearch.o: $(srcdir)/clib/bsearch.c
+ $(CC) $(CFLAGS) $?
+env.o: $(srcdir)/clib/env.c
+ $(CC) $(CFLAGS) $?
+fchmod.o: $(srcdir)/clib/fchmod.c
+ $(CC) $(CFLAGS) $(INC) $?
+gethostname.o: $(srcdir)/clib/gethostname.c
+ $(CC) $(CFLAGS) $(INC) $?
+getopt.o: $(srcdir)/clib/getopt.c
+ $(CC) $(CFLAGS) $(INC) $?
+memchr.o: $(srcdir)/clib/memchr.c
+ $(CC) $(CFLAGS) $?
+memcpy.o: $(srcdir)/clib/memmove.c
+ $(cp) $? memcpy.c
+ $(CC) $(CFLAGS) -DMEMCOPY memcpy.c
+memmove.o: $(srcdir)/clib/memmove.c
+ $(CC) $(CFLAGS) -DMEMMOVE $?
+memset.o: $(srcdir)/clib/memset.c
+ $(CC) $(CFLAGS) $?
+mkstemp.o: $(srcdir)/clib/mkstemp.c
+ $(CC) $(CFLAGS) $?
+mmap.o: $(srcdir)/clib/mmap.c
+ $(CC) $(CFLAGS) $?
+realloc.o: $(srcdir)/clib/realloc.c
+ $(CC) $(CFLAGS) $?
+snprintf.o: $(srcdir)/clib/snprintf.c
+ $(CC) $(CFLAGS) $?
+strdup.o: $(srcdir)/clib/strdup.c
+ $(CC) $(CFLAGS) $?
+strerror.o: $(srcdir)/clib/strerror.c
+ $(CC) $(CFLAGS) $?
+strpbrk.o: $(srcdir)/clib/strpbrk.c
+ $(CC) $(CFLAGS) $?
+strsep.o: $(srcdir)/clib/strsep.c
+ $(CC) $(CFLAGS) $?
+strtol.o: $(srcdir)/clib/strtol.c
+ $(CC) $(CFLAGS) $?
+strtoul.o: $(srcdir)/clib/strtoul.c
+ $(CC) $(CFLAGS) $?
+vsnprintf.o: $(srcdir)/clib/vsnprintf.c
+ $(CC) $(CFLAGS) $?
diff --git a/contrib/nvi/build/README b/contrib/nvi/build/README
new file mode 100644
index 000000000000..efbce2b9dfbb
--- /dev/null
+++ b/contrib/nvi/build/README
@@ -0,0 +1,369 @@
+# @(#)README 8.26 (Berkeley) 10/19/96
+
+Nvi uses the GNU autoconf program for configuration and compilation. You
+should enter:
+
+ configure
+ make
+
+and nvi will configure the system and build one or two binaries: nvi and
+tknvi. You can use any path to the configure script, e.g., to build for
+an x86 architecture, I suggest that you do:
+
+ mkdir build.x86
+ cd build.x86
+ ../build/configure
+ make
+
+There are options that you can specify to the configure command. See
+the next section for a description of these options.
+
+If you want to rebuild or reconfigure nvi, for example, because you change
+your mind as to the curses library that you want to use, create a new
+directory and reconfigure it using "configure" and whatever options you
+choose, don't try to selectively edit the files.
+
+By default, nvi is installed as "vi", with hard links to "ex" and "view".
+To install them using different names, use the configure program options.
+For example, to install them as "nvi", "nex" and "nview", use:
+
+ configure --program-prefix=n
+
+See the section below on installation for details.
+
+Note, if you're building nvi on a LynxOS system, you should read the
+README.LynxOS file in this directory for additional build instructions
+that are specific to that operating system.
+
+If you have trouble with this procedure, send email to the addresses
+listed in ../README. In that email, please provide a complete script
+of the output for all of the above commands that you entered.
+
+=-=-=-=-=-=-=
+NVI'S OPTIONS TO THE CONFIGURE PROGRAM
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+There are many options that you can enter to the configuration program.
+To see a complete list of the options, enter "configure --help". Only
+a few of them are nvi specific. These options are as follows:
+
+ --disable-curses DON'T use the nvi-provided curses routines.
+ --disable-db DON'T use the nvi-provided DB routines.
+ --disable-re DON'T use the nvi-provided RE routines.
+ --enable-debug Build a debugging version.
+ --enable-perlinterp Include a Perl interpreter in vi.
+ --enable-tclinterp Include a Tk/Tcl interpreter in vi.
+ --enable-tknvi Build a Tk/Tcl front-end for vi.
+
+disable-curses:
+ By default, nvi loads its own implementation of the curses
+ routines (which are a stripped-down version of the 4.4BSD curses
+ library). If you have your own curses library implementation and
+ you want to use it instead, enter:
+
+ --disable-curses
+
+ as an argument to configure, and the curses routines will be taken
+ from whatever libraries you load. Note: System V based curses
+ implementations are usually broken. See the last section of this
+ README for further information about nvi and the curses library.
+
+disable-db:
+ By default, nvi loads its own versions of the Berkeley DB routines
+ (which are a stripped-down version of DB 1.85). If you have your
+ own version of the Berkeley DB routines and you want to use them
+ instead, enter:
+
+ --disable-db
+
+ as an argument to configure, and the DB routines will be taken
+ from whatever libraries you load. Make sure that the DB routines
+ you use are at least version 1.85 or later.
+
+disable-re:
+ By default, nvi loads its own versions of the POSIX 1003.2 Regular
+ Expression routines (which are Henry Spencer's implementation).
+ If your C library contains an implementation of the POSIX 1003.2
+ RE routines (note, this is NOT the same as the historic UNIX RE
+ routines), and you want to use them instead, enter:
+
+ --disable-re
+
+ as an argument to configure, and the RE routines will be taken
+ from whatever libraries you load. Please ensure that your RE
+ routines implement Henry Spencer's extensions for doing vi-style
+ "word" searches.
+
+enable-debug:
+ If you want to build nvi with no optimization (i.e. without -O
+ as a compiler flag), with -g as a compiler flag, and with DEBUG
+ defined during compilation, enter:
+
+ --enable-debug
+
+ as an argument to configure.
+
+enable-perlinterp:
+ If you have the Perl 5 libraries and you want to compile in the
+ Perl interpreter, enter:
+
+ --enable-perlinterp
+
+ as an argument to configure. (Note: this is NOT possible with
+ Perl 4, or even with Perl 5 versions earlier than 5.002.)
+
+enable-tclinterp:
+ If you have the Tk/Tcl libraries and you want to compile in the
+ Tcl/Tk interpreter, enter:
+
+ --enable-tclinterp
+
+ as an argument to configure. If your Tk/Tcl include files and
+ libraries aren't in the standard library and include locations,
+ see the next section of this README file for more information.
+
+enable-tknvi:
+ If you have the Tk/Tcl libraries and you want to build the Tcl/Tk
+ nvi front-end, enter:
+
+ --enable-tknvi
+
+ as an argument to configure. If your Tk/Tcl include files and
+ libraries aren't in the standard library and include locations,
+ see the next section of this README file for more information.
+
+=-=-=-=-=-=-=
+ADDING OR CHANGING COMPILERS, OR COMPILE OR LOAD LINE FLAGS
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+If you want to use a specific compiler, specify the CC environment
+variable before running configure. For example:
+
+ env CC=gcc configure
+
+Using anything other than the native compiler will almost certainly
+mean that you'll want to check the compile and load line flags, too.
+
+If you want to specify additional load line flags, specify the ADDLDFLAGS
+environment variable before running configure. For example:
+
+ env ADDLDFLAGS="-Q" configure
+
+would specify the -Q flag in the load line when the nvi programs are
+loaded.
+
+If you don't want configure to use the default load line flags for the
+system, specify the LDFLAGS environment variable before running configure.
+For example:
+
+ env LDFLAGS="-32" configure
+
+will cause configure to set the load line flags to "-32", and not set
+them based on the current system.
+
+If you want to specify additional compile line flags, specify the
+ADDCPPFLAGS environment variable before running configure. For example:
+
+ env ADDCPPFLAGS="-I../foo" configure
+
+would cause the compiler to be passed the -I../foo flag when compiling
+test programs during configuration as well as when building nvi object
+files.
+
+If you don't want configure to use the default compile line flags for the
+system, specify the CPPFLAGS environment variable before running configure.
+For example:
+
+ env CPPFLAGS="-I.." configure
+
+will cause configure to use "-I.." as the compile line flags instead of
+the default values.
+
+=-=-=-=-=-=-=
+ADDING LIBRARIES AND INCLUDE FILES
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+If the Tk/Tcl or any other include files or libraries are in non-standard
+places on your system, you will need to specify the directory path where
+they can be found.
+
+If you want to specify additional library paths, set the ADDLIBS environment
+variable before running configure. For example:
+
+ env ADDLIBS="-L/a/b -L/e/f -ldb" configure
+
+would specify two additional directories to search for libraries, /a/b
+and /e/f, and one additional library to load, "db".
+
+If you want to specify additional include paths, specify the ADDCPPFLAGS
+environment variable before running configure. For example:
+
+ env ADDCPPFLAGS="-I/usr/local/include" LIBS="-ldb" configure
+
+would search /usr/local/include for include files, as well as load the db
+library as described above.
+
+As a final example, let's say that you've downloaded ncurses from the net
+and you've built it in a directory named ncurses which is at the same
+level in the filesystem hierarchy as nvi. You would enter something like:
+
+ env ADDCPPFLAGS="-I../../ncurses/include" \
+ ADDLIBS="-L../../ncurses/libraries" configure
+
+to cause nvi to look for the curses include files and the curses library
+in the ncurses environment.
+
+Notes:
+ Make sure that you prepend -L to any library directory names, and
+ that you prepend -I to any include file directory names! Also,
+ make sure that you quote the paths as shown above, i.e. with
+ single or double quotes around the values you're specifying for
+ ADDCPPFLAGS and ADDLIBS.
+
+ =-=-=-=-=-=
+ You should NOT need to add any libraries or include files to load
+ the Perl5 interpreter. The configure script will obtain that
+ information directly from the Perl5 program. This means that the
+ configure script must be able to find perl in its path. It looks
+ for "perl5" first, and then "perl". If you're building a Perl
+ interpreter and neither is found, it's a fatal error.
+
+ =-=-=-=-=-=
+ You do not need to specify additional libraries to load Tk/Tcl,
+ Perl or curses, as the nvi configuration script adds the
+ appropriate libraries to the load line whenever you specify
+ --enable-tknvi or other Perl or Tk/Tcl related option, or build
+ the Tk/Tcl or curses version of nvi. The library names that are
+ automatically loaded are as follows:
+
+ for Perl: -lperl
+ for Tk/Tcl: -ltk -ltcl -lm
+ for curses: -lcurses
+
+ In addition, the configure script loads:
+
+ ... the X libraries when loading the Tk/Tcl libraries,
+ if they exist.
+
+ ... the -ltermcap or -ltermlib libraries when loading
+ any curses library, if they exist.
+
+ =-=-=-=-=-=
+ The env command is available on most systems, and simply sets one
+ or more environment variables before running a command. If the
+ env command is not available to you, you can set the environment
+ variables in your shell before running configure. For example,
+ in sh or ksh, you could do:
+
+ ADDLIBS="-L/a/b -L/e/f -ldb" configure
+
+ and in csh or tcsh, you could do:
+
+ setenv ADDLIBS "-L/a/b -L/e/f -ldb"
+ configure
+
+ See your shell manual page for further information.
+
+=-=-=-=-=-=-=
+INSTALLING NVI
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Nvi installs the following files into the following locations, with
+the following default values:
+
+Variables: Default value:
+prefix /usr/local
+exec_prefix $(prefix)
+bindir $(prefix)/bin
+datadir $(prefix)/share
+mandir $(prefix)/man
+
+File(s): Default location
+----------------------------------------
+vi $(bindir)/vi
+vi.1 $(mandir)/man1/vi.1
+vi.0 $(mandir)/cat1/vi.0
+Perl scripts $(datadir)/vi/perl/
+Tcl scripts $(datadir)/vi/tcl/
+Message Catalogs $(datadir)/vi/catalog/
+
+Notes:
+ There are two hard links to the vi program, named ex and view.
+ Similarly, there are two hard links to the unformatted vi manual
+ page, named ex.1 and view.1, and two hard links to the formatted
+ manual page, named ex.0 and view.0. These links are created when
+ the program and man pages are installed.
+
+ If you want to install vi, ex, view and the man pages as nvi, nex,
+ nview, use the configure option --program-prefix=n. Other, more
+ complex transformations are possible -- use configure --help to
+ see more options.
+
+ To move the entire installation tree somewhere besides /usr/local,
+ change the value of both "exec_prefix" and "prefix". To move the
+ binaries to a different place, change the value of "bindir".
+ Similarly, to put the datafiles (the message catalogs, Perl and
+ Tcl scripts) or the man pages in a different place, change the
+ value of "datadir" or "mandir". These values can be changed as
+ part of configuration:
+
+ configure --exec_prefix=/usr/contrib --prefix=/usr/share
+
+ or when doing the install itself:
+
+ make exec_prefix=/usr/contrib prefix=/usr/contrib install
+
+ The datafile directory (e.g., /usr/local/share/vi by default) is
+ completely removed and then recreated as part of the installation
+ process.
+
+=-=-=-=-=-=-=
+NVI AND THE CURSES LIBRARY
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+The major portability problem for nvi is selecting a curses library.
+Unfortunately, it is common to find broken versions of curses -- the
+original System V curses was broken, resulting in all vendors whose
+implementations are derived from System V having broken implementations
+in turn.
+
+For this reason, BY DEFAULT, nvi uses the stripped-down curses library
+that's included in its distribution. Of course, it would be preferable
+to use the vendor's curses library, or one of the newer implementations
+of curses, e.g., ncurses.
+
+To use the vendor's curses library, specify the:
+
+ --disable-curses
+
+argument to the configure command. If you use the vendor's or other
+curses library, and you see any of the following symptoms:
+
+ + Core dumps in curses routines.
+ + Missing routines when compiling.
+ + Repainting the wrong characters on the screen.
+ + Displaying inverse video in the wrong places.
+ + Failure to reset your terminal to the correct modes on exit.
+
+you have a broken curses implementation, and you should reconfigure nvi
+to use another curses library or the curses library provided with nvi.
+
+There are two alternative sources for curses libraries:
+
+#1: Compile the 4BSD curses library from any of the recent BSD
+ releases: FreeBSD, NetBSD or 4.4BSD-Lite release 2. These
+ libraries should be able to support nvi.
+
+#2: Retrieve and build the ncurses library. This library is not
+ recommended unreservedly, at least for now, for two reasons.
+ First, it can't be built on any system where the compiler
+ doesn't support function prototypes. Second, it currently has
+ a few bugs in its support for nvi. It mostly works, but it's
+ still not quite right.
+
+One final note. If you see the following symptoms:
+
+ + Line-by-line screen repainting instead of scrolling.
+
+it usually means that your termcap or terminfo information is insufficient
+for the terminal.
diff --git a/contrib/nvi/build/README.LynxOS b/contrib/nvi/build/README.LynxOS
new file mode 100644
index 000000000000..2cc68dafc861
--- /dev/null
+++ b/contrib/nvi/build/README.LynxOS
@@ -0,0 +1,320 @@
+README.LynxOS
+=============
+
+Written by Ronald F. Guilmette <rfg@monkeys.com>
+
+Last modified Wed Aug 14 23:10:07 PDT 1996
+------------------------------------------
+
+0. Introduction
+---------------
+
+This file describes how to build and install the Berkeley nvi editor for
+the LynxOS 2.4.0 operating system.
+
+LynxOS 2.4.0 is available for a variety of different hardware platforms, in
+particular, x86, m680x0, Sparc, and PowerPC. I have successfully built nvi
+on all four of these flavors of LynxOS by following the procedures given in
+this file.
+
+Note that these procedures may not work on versions of LynxOS prior to 2.4.0.
+(As I understand it, a good deal of work went into making the 2.4.0 release
+more POSIX-compliant, and I have no idea what build glitches, if any, you
+might encounter if you try to build nvi on a pre-2.4.0 version of LynxOS.)
+
+There are basically four steps to configuring, building, and installing nvi
+on LynxOS, namely:
+
+ 1. Get setup to use the proper C compiler.
+ 2. Replace your installed `tr' program.
+ 3. Fix your system include files.
+ 4. Do a normal configure, build, and install of nvi.
+
+These steps are described in separate sections below.
+
+1. Get Setup to Use the Proper C Compiler
+------------------------------------------
+
+The first step when building nvi on LynxOS is to set your $PATH environment
+variable properly so that the gcc 2.x compiler appears first on your path,
+prior to the older (and less robust) gcc 1.xx compiler (typically installed
+as /bin/gcc) and/or the old Lynx proprietary C compiler (typically installed
+as /bin/cc), both of which may also be present on your system.
+
+Note that for most operating systems, the configure script for nvi tries
+to use whatever compiler you have installed (and in your $PATH) as "cc",
+however in the special case of LynxOS, the configure script will auto-
+matically try to find a "gcc" program on your $PATH in preference to a
+compiler called "cc". If the nvi configure script only find a compiler
+called "cc", that's OK. It will still try to see if that is really just
+the GNU C compiler installed under the name "cc".
+
+Regardless of the name however (be it "gcc" or "cc") the first C compiler
+in your $PATH should be some _recent_ (i.e. 2.0 or later) version of the
+GNU C compiler... and the nvi configure script now checks that this is the
+case, and fails if it isn't.
+
+Oddly enough, LynxOS 2.4.0 (and some prior versions) shipped with as many
+as three different C compilers installed, so it is important to set your
+$PATH environment variable carfully in order to get the proper C compiler
+to appear first in your $PATH. You want to avoid having either the /bin/gcc
+compiler or the /bin/cc compiler be the first C compiler in your $PATH.
+
+To make sure that the GNU C version 2.x compiler which was shipped with your
+LynxOS system appears first on your path, you will need to either set your
+$PATH variable (for sh/bash/ksh users) or your $path variable (for csh/tcsh
+users). You can, of course, just do this at the shell command prompt, but
+it is probably better to actually edit this change into your .profile file
+(for sh/bash/ksh users) or into your .cshrc file (for csh/tcsh users).
+
+The pathname of the directory that contains the GNU C version 2.x compiler
+is (unfortunately) dependent upon the exact type of LynxOS system you have.
+
+For LynxOS 2.4.0 on x86 systems, gcc 2.x is located in:
+
+ /cygnus/94q4-lynxos-x86/bin
+
+For LynxOS 2.4.0 on m680x0 systems, gcc 2.x is located in:
+
+ /cygnus/94q4-lynxos-68k/bin
+
+For LynxOS 2.4.0 on Sparc systems, gcc 2.x is located in:
+
+ /cygnus/94q4-lynxos-usparc/bin
+
+For LynxOS 2.4.0 on PowerPC systems, gcc 2.x is located in:
+
+ /cygnus/95q2-lynxos-ppc/bin
+
+(Note also that these locations may change in LynxOS 2.5.x and beyond.)
+
+Anyway, it is imperative that you setup your $PATH environment variable
+(*before* you do the configure step for nvi) so that the GNU C version 2.x
+compiler appears in your $PATH before either the /bin/cc or /bin/gcc
+compilers (if present). If you fail to do this, the configure step for
+nvi will fail, because the compiler script actually checks (now) that the
+compiler you are using (if your are on a LynxOS system) is gcc 2.0 or
+later.
+
+To make absolutely sure that you will be configuring and building nvi with
+the proper C compiler (i.e. the GNU C version 2.x compiler on your system)
+you should add the directory name listed above for your specific system type
+to your $PATH setting in your $HOME/.profile file. (For csh/tcsh users, you
+will instead want to add the relevant directory name to the setting of your
+$path variable in your ~/.cshrc file.) Once you have added the proper direc-
+tory name (from the list given above) to your $HOME/.profile file (or to your
+~/.cshrc file, if you are using csh or tcsh) you should log out completely
+and then log back into the system just to make sure your new $PATH/$path
+setting takes effect properly.
+
+When you finish making this adjustment to your $PATH (or $path), the most
+up-to-date version of gcc on your system should be available to you as the
+first `gcc' program on your $PATH. You should verify that this is indeed the
+case simply by typing `gcc -v' and then checking the version number reported
+by the compiler. It should say either "2.6-94q4" or (on PowerPC systems) it
+should say "2.6-95q2". If you don't get these results, try again to set your
+$PATH (or $path) until you do. You won't be able to build nvi until you are
+properly setup to use gcc version 2.0 or later.
+
+Performing the steps shown above will insure that your subsequent configura-
+tion and build steps for nvi will make use of the most up-to-date version of
+gcc that was shipped with your Lynx operating system. (Note that the versions
+of gcc which are currently shipping with LynxOS 2.4.0 are also somewhat out-
+of-date themselves, but they are still quite a bit newer and more bug-free
+and ANSI conformant that those other two C compilers, /bin/cc and /bin/gcc,
+which also ship with LynxOS 2.4.0.)
+
+(Note: At present, LynxOS version 2.4.0 is the latest officially released
+version of LynxOS, and all of the above information is accurate and correct
+for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
+future releases of LynxOS may provide a still newer version of gcc, and that
+it may be located in the /usr/bin directory. Thus, if you are building nvi
+for some LynxOS version later than 2.4.0, you may wish to check and see if
+your system has a program called /usr/bin/gcc, and use that version of gcc,
+if available, rather than the one suggested above.)
+
+2. Replace Your Installed `tr' Program
+---------------------------------------
+
+The `tr' program which comes bundled with LynxOS 2.4.0 (as /bin/tr) has a
+somewhat obscure bug which just happens to be tickled by almost all GNU
+`autoconf' generated `configure' scripts (including the one that nowadays
+comes bundled with nvi). Using the stock /bin/tr program on LynxOS when
+executing such `configure' scripts _will_ cause these scripts to malfunction
+in various ways. It is therefore imperative that you replace your LynxOS
+/bin/tr program with a properly working version of the `tr' command _before_
+you even try to configure nvi. (You can tell if your `tr' program has the
+bug by executng the command "echo ab- | tr ab- ABC". If this yields the
+string "Ab-" then you have the bug. If it yields "ABC" then you don't.)
+
+You can obtain sources for a working version of the `tr' command as part of
+the GNU `textutils' package (the latest version of which, at the time of this
+writing, is 1.19). The GNU textutils package is available for downloading
+from prep.ai.mit.edu in the pub/gnu directory. Look for the file named
+textutils-1.19.tar.gz, or an even more recent version of textutils, if one
+is available. Fetch it, gunzip it, untar it, and follow the directions in
+the INSTALL file included in the tar file to build and install the entire
+textutils set of utility programs (which includes a working `tr' program).
+Then just make sure that the GNU version of `tr' appears on your $PATH
+_before_ the LynxOS version of `tr' (i.e. /bin/tr). Be sure to do this
+step _before_ you start to configure nvi.
+
+When building the textutils set of programs, I suggest that you use the most
+up-to-date C compiler available on your system (as described above). Also,
+note that it will be important for you to AVOID using the -O (optimize)
+compiler option when building the GNU textutils package, even if you are
+using the most up-to-date version of gcc which shipped with your system.
+If you try to use -O when building the textutils package on an x86 with
+the Cygnus 94q4 C compiler, you will end up with a `tr' program which will
+malfunction even worse than the one you are trying to replace! If you use
+-O when building the textutils package on LynxOS on the PowerPC (using the
+Cygnus 95q2 C compiler) you will just get yourself a compiler crash. So
+just don't use -O when building textutils. You can avoid using -O by in-
+voking make in the textutils directory as follows:
+
+ make CFLAGS="-g"
+
+(Note: At present, LynxOS version 2.4.0 is the latest officially released
+version of LynxOS, and all of the above information is accurate and correct
+for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
+the bug in the /bin/tr program will be fixed in future releases of LynxOS,
+so if you have a version of LynxOS later than 2.4.0, you may wish to check
+and see if your /bin/tr program even has the problematic bug before bothering
+with all of this.)
+
+
+3. Fix Your System Include Files
+---------------------------------
+
+If you are building nvi on a PowerPC system, it is also important that you
+apply the patches given at the end of this file to your /usr/include files.
+(Note that you will have to be root in order to do this.) Two of the patches
+included below fix a pair of serious bugs in the /usr/include/stdarg.h file
+on the PowerPC, and you really _do_ want to have these bugs fixed anyway,
+because without these fixes, anything that you compile which uses <stdarg.h>
+will very likely malfunction at run-time.
+
+Regardless of which LynxOS platform you are using (i.e. x86, PowerPC, Sparc,
+or m680x0) you may want to apply all of the system include files patches that
+are included below anyway. Doing so will clean up a few minor problems with
+the relevant system include files (i.e. <stdarg.h>, <ioctl.h>, and <wait.h>)
+and this step will also prevent a few warnings which you would otherwise get
+during the build of nvi.
+
+You can apply all of the patches given at the end of this file simply by
+doing the following:
+
+ su root
+ cd /usr/include
+ /bin/patch < this-file
+
+Where `this-file' is the actual full pathname of the file you are now reading,
+wherever it may reside on your own system.
+
+(Note: At present, LynxOS version 2.4.0 is the latest officially released
+version of LynxOS, and all of the above information is accurate and correct
+for LynxOS 2.4.0 as of the time of this writing. However it is rumored that
+future releases of LynxOS may incorporate some or all of the important system
+include file fixes provided below. Thus, if you are building nvi for some
+LynxOS version later than 2.4.0, you should probably go ahead and try to
+apply the patches given below to your system include files, and then just
+don't worry about it if these patches seem to have already been applied.)
+
+
+4. A Brief Note about Sendmail
+-------------------------------
+
+I should mention also that LynxOS does not normally ship with the `sendmail'
+mail transfer program installed, either under /usr/lib/ or anywhere else for
+that matter. This isn't really a big problem, but nvi normally wants and
+expects to have a sendmail program available so that it can send users notifi-
+cations (by mail) whenever a partially edited file is preserved by the editor
+in response to a sudden system crash, a sudden system shutdown, or an unexpect-
+ed serial-line hangup. You can configure and build nvi without any sendmail
+program installed on your system, but you will get warnings about its absence
+when you are doing the initial configure step prior to actually building nvi.
+If you want to have a fully-functional nvi which does send out notification
+messages (by mail) whenever partially edited files are preserved during a
+serial line hangup or system crash, then you should get the BSD sendmail
+sources (via ftp from ftp.cs.berkeley.edu), build and install sendmail, and
+then reconfigure, rebuild, and reinstall nvi.
+
+Please contact me at the E-mail address below if you experience any problems in
+building or using nvi on LynxOS. I make no guarrantees, but I may be willing
+to try to help.
+
+Ron Guilmette
+Roseville, California
+<rfg@monkeys.com>
+August 14, 1996
+
+
+cut here for LynxOS 2.4.0 system include files patches
+-----------------------------------------------------------------------------
+*** wait.h Fri Apr 26 10:02:45 1996
+--- wait.h Sun May 19 05:36:50 1996
+***************
+*** 94,104 ****
+ /* Function prototypes */
+ #ifndef __LYNXOS
+- #ifdef _POSIX_SOURCE
+ extern pid_t wait _AP((int *));
+ extern pid_t waitpid _AP((pid_t, int *, int));
+! #else
+! extern int wait _AP((union wait *));
+! extern int waitpid _AP((int, union wait *, int));
+! extern int wait3 _AP((union wait *, int, struct rusage *));
+ #endif
+ #endif /* !__LYNXOS */
+--- 94,101 ----
+ /* Function prototypes */
+ #ifndef __LYNXOS
+ extern pid_t wait _AP((int *));
+ extern pid_t waitpid _AP((pid_t, int *, int));
+! #ifndef _POSIX_SOURCE
+! extern int wait3 _AP((int *, int, struct rusage *));
+ #endif
+ #endif /* !__LYNXOS */
+*** ioctl.h Fri Apr 26 16:50:51 1996
+--- ioctl.h Sat May 18 17:55:16 1996
+***************
+*** 572,576 ****
+
+ #ifndef __LYNXOS
+! extern int ioctl _AP((int, int, char *));
+ #endif
+
+--- 572,576 ----
+
+ #ifndef __LYNXOS
+! extern int ioctl _AP((int, int, ...));
+ #endif
+
+*** stdarg.h Fri Apr 26 16:51:02 1996
+--- stdarg.h Sat May 18 19:34:13 1996
+***************
+*** 88,92 ****
+ (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
+
+! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg ()))
+
+ void va_end(va_list); /* Defined in libgcc.a */
+--- 88,92 ----
+ (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
+
+! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg (LASTARG)))
+
+ void va_end(va_list); /* Defined in libgcc.a */
+***************
+*** 162,166 ****
+ (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
+
+! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg ()))
+
+ void va_end(va_list); /* Defined in libgcc.a */
+--- 162,166 ----
+ (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
+
+! #define va_start(AP, LASTARG) (AP = ((char *) __builtin_next_arg (LASTARG)))
+
+ void va_end(va_list); /* Defined in libgcc.a */
diff --git a/contrib/nvi/build/acconfig.h b/contrib/nvi/build/acconfig.h
new file mode 100644
index 000000000000..567f9ee60070
--- /dev/null
+++ b/contrib/nvi/build/acconfig.h
@@ -0,0 +1,82 @@
+/* @(#)acconfig.h 8.18 (Berkeley) 7/2/96 */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef ssize_t
+
+/* Define if you want a debugging version. */
+#undef DEBUG
+
+/* Define if you have a System V-style (broken) gettimeofday. */
+#undef HAVE_BROKEN_GETTIMEOFDAY
+
+/* Define if you have a Ultrix-style (broken) vdisable. */
+#undef HAVE_BROKEN_VDISABLE
+
+/* Define if you have a BSD version of curses. */
+#undef HAVE_BSD_CURSES
+
+/* Define if you have the curses(3) addnstr function. */
+#undef HAVE_CURSES_ADDNSTR
+
+/* Define if you have the curses(3) beep function. */
+#undef HAVE_CURSES_BEEP
+
+/* Define if you have the curses(3) flash function. */
+#undef HAVE_CURSES_FLASH
+
+/* Define if you have the curses(3) idlok function. */
+#undef HAVE_CURSES_IDLOK
+
+/* Define if you have the curses(3) keypad function. */
+#undef HAVE_CURSES_KEYPAD
+
+/* Define if you have the curses(3) newterm function. */
+#undef HAVE_CURSES_NEWTERM
+
+/* Define if you have the curses(3) setupterm function. */
+#undef HAVE_CURSES_SETUPTERM
+
+/* Define if you have the curses(3) tigetstr/tigetnum functions. */
+#undef HAVE_CURSES_TIGETSTR
+
+/* Define if you have the DB __hash_open call in the C library. */
+#undef HAVE_DB_HASH_OPEN
+
+/* Define if you have the chsize(2) system call. */
+#undef HAVE_FTRUNCATE_CHSIZE
+
+/* Define if you have the ftruncate(2) system call. */
+#undef HAVE_FTRUNCATE_FTRUNCATE
+
+/* Define if you have fcntl(2) style locking. */
+#undef HAVE_LOCK_FCNTL
+
+/* Define if you have flock(2) style locking. */
+#undef HAVE_LOCK_FLOCK
+
+/* Define if you want to compile in the Perl interpreter. */
+#undef HAVE_PERL_INTERP
+
+/* Define if your Perl is at least 5.003_01. */
+#undef HAVE_PERL_5_003_01
+
+/* Define if you have the Berkeley style revoke(2) system call. */
+#undef HAVE_REVOKE
+
+/* Define if you have the Berkeley style strsep(3) function. */
+#undef HAVE_STRSEP
+
+/* Define if you have <sys/mman.h> */
+#undef HAVE_SYS_MMAN_H
+
+/* Define if you have <sys/select.h> */
+#undef HAVE_SYS_SELECT_H
+
+/* Define if you have the System V style pty calls. */
+#undef HAVE_SYS5_PTY
+
+/* Define if you want to compile in the Tcl interpreter. */
+#undef HAVE_TCL_INTERP
+
+/* Define if your sprintf returns a pointer, not a length. */
+#undef SPRINTF_RET_CHARPNT
diff --git a/contrib/nvi/build/aclocal.m4 b/contrib/nvi/build/aclocal.m4
new file mode 100644
index 000000000000..de7e57ed3aac
--- /dev/null
+++ b/contrib/nvi/build/aclocal.m4
@@ -0,0 +1,17 @@
+AC_DEFUN(AM_SANITY_CHECK_CC,
+[dnl Derived from macros from Bruno Haible and from Cygnus.
+AC_MSG_CHECKING([whether the compiler ($CC $CFLAGS $LDFLAGS) actually works])
+AC_LANG_SAVE
+ AC_LANG_C
+ AC_TRY_RUN([main() { exit(0); }],
+ am_cv_prog_cc_works=yes, am_cv_prog_cc_works=no,
+ dnl When crosscompiling, just try linking.
+ AC_TRY_LINK([], [], am_cv_prog_cc_works=yes,
+ am_cv_prog_cc_works=no))
+AC_LANG_RESTORE
+case "$am_cv_prog_cc_works" in
+ *no) AC_MSG_ERROR([Installation or configuration problem: C compiler cannot create executables.]) ;;
+ *yes) ;;
+esac
+AC_MSG_RESULT(yes)
+])dnl
diff --git a/contrib/nvi/build/config.guess b/contrib/nvi/build/config.guess
new file mode 100755
index 000000000000..4c314d97aacb
--- /dev/null
+++ b/contrib/nvi/build/config.guess
@@ -0,0 +1,571 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:V*:*)
+ # After 1.2, OSF1 uses "V1.3" for uname -r.
+ echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^V//'`
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ # 1.2 uses "1.2" for uname -r.
+ echo alpha-dec-osf${UNAME_RELEASE}
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ Pyramid*:OSx*:*:*)
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ sun4*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:4*:UMIPS)
+ echo mips-mips-riscos4sysv
+ exit 0 ;;
+ mips:*:5*:RISCos)
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+ -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ if /usr/sbin/lsattr -EHl proc0 | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[3478]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/7?? | 9000/8?[79] ) HP_ARCH=hppa1.1 ;;
+ 9000/8?? ) HP_ARCH=hppa1.0 ;;
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*C90:*:*:*)
+ echo c90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,/.*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us.
+ ld_help_string=`ld --help 2>&1`
+ if echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: elf_i?86"; then
+ echo "${UNAME_MACHINE}-unknown-linux" ; exit 0
+ elif echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: i?86linux"; then
+ echo "${UNAME_MACHINE}-unknown-linuxaout" ; exit 0
+ elif echo $ld_help_string | grep >/dev/null 2>&1 "supported emulations: i?86coff"; then
+ echo "${UNAME_MACHINE}-unknown-linuxcoff" ; exit 0
+ elif test "${UNAME_MACHINE}" = "alpha" ; then
+ echo alpha-unknown-linux ; exit 0
+ else
+ # Either a pre-BFD a.out linker (linuxoldld) or one that does not give us
+ # useful --help. Gcc wants to distinguish between linuxoldld and linuxaout.
+ test ! -d /usr/lib/ldscripts/. \
+ && echo "${UNAME_MACHINE}-unknown-linuxoldld" && exit 0
+ # Determine whether the default compiler is a.out or elf
+ cat >dummy.c <<EOF
+main(argc, argv)
+int argc;
+char *argv[];
+{
+#ifdef __ELF__
+ printf ("%s-unknown-linux\n", argv[1]);
+#else
+ printf ("%s-unknown-linuxaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ ${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+ else
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-unknown-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ echo ${UNAME_MACHINE}-unknown-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-unknown-sysv32
+ fi
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-unknown-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M680?0:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0)
+ uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3 && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68*:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:* | uSPARC2:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *FTX*)
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ printf ("%s-next-nextstep%s\n", __ARCHITECTURE__, version==2 ? "2" : "3");
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-unknown-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm dummy.c dummy && exit 0
+rm -f dummy.c dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/contrib/nvi/build/config.h.in b/contrib/nvi/build/config.h.in
new file mode 100644
index 000000000000..c87fcdd7f214
--- /dev/null
+++ b/contrib/nvi/build/config.h.in
@@ -0,0 +1,179 @@
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define if your struct stat has st_blksize. */
+#undef HAVE_ST_BLKSIZE
+
+/* Define if you have <vfork.h>. */
+#undef HAVE_VFORK_H
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef mode_t
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+#undef size_t
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if your <sys/time.h> declares struct tm. */
+#undef TM_IN_SYS_TIME
+
+/* Define vfork as fork if vfork does not work. */
+#undef vfork
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef ssize_t
+
+/* Define if you want a debugging version. */
+#undef DEBUG
+
+/* Define if you have a System V-style (broken) gettimeofday. */
+#undef HAVE_BROKEN_GETTIMEOFDAY
+
+/* Define if you have a Ultrix-style (broken) vdisable. */
+#undef HAVE_BROKEN_VDISABLE
+
+/* Define if you have a BSD version of curses. */
+#undef HAVE_BSD_CURSES
+
+/* Define if you have the curses(3) addnstr function. */
+#undef HAVE_CURSES_ADDNSTR
+
+/* Define if you have the curses(3) beep function. */
+#undef HAVE_CURSES_BEEP
+
+/* Define if you have the curses(3) flash function. */
+#undef HAVE_CURSES_FLASH
+
+/* Define if you have the curses(3) idlok function. */
+#undef HAVE_CURSES_IDLOK
+
+/* Define if you have the curses(3) keypad function. */
+#undef HAVE_CURSES_KEYPAD
+
+/* Define if you have the curses(3) newterm function. */
+#undef HAVE_CURSES_NEWTERM
+
+/* Define if you have the curses(3) setupterm function. */
+#undef HAVE_CURSES_SETUPTERM
+
+/* Define if you have the curses(3) tigetstr/tigetnum functions. */
+#undef HAVE_CURSES_TIGETSTR
+
+/* Define if you have the chsize(2) system call. */
+#undef HAVE_FTRUNCATE_CHSIZE
+
+/* Define if you have the ftruncate(2) system call. */
+#undef HAVE_FTRUNCATE_FTRUNCATE
+
+/* Define if you have fcntl(2) style locking. */
+#undef HAVE_LOCK_FCNTL
+
+/* Define if you have flock(2) style locking. */
+#undef HAVE_LOCK_FLOCK
+
+/* Define if you want to compile in the Perl interpreter. */
+#undef HAVE_PERL_INTERP
+
+/* Define if your Perl is at least 5.003_01. */
+#undef HAVE_PERL_5_003_01
+
+/* Define if you have the Berkeley style revoke(2) system call. */
+#undef HAVE_REVOKE
+
+/* Define if you have <sys/mman.h> */
+#undef HAVE_SYS_MMAN_H
+
+/* Define if you have <sys/select.h> */
+#undef HAVE_SYS_SELECT_H
+
+/* Define if you have the System V style pty calls. */
+#undef HAVE_SYS5_PTY
+
+/* Define if you want to compile in the Tcl interpreter. */
+#undef HAVE_TCL_INTERP
+
+/* Define if your sprintf returns a pointer, not a length. */
+#undef SPRINTF_RET_CHARPNT
+
+/* Define if you have the bsearch function. */
+#undef HAVE_BSEARCH
+
+/* Define if you have the gethostname function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define if you have the getopt function. */
+#undef HAVE_GETOPT
+
+/* Define if you have the getpagesize function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define if you have the memchr function. */
+#undef HAVE_MEMCHR
+
+/* Define if you have the memcpy function. */
+#undef HAVE_MEMCPY
+
+/* Define if you have the memmove function. */
+#undef HAVE_MEMMOVE
+
+/* Define if you have the memset function. */
+#undef HAVE_MEMSET
+
+/* Define if you have the mkstemp function. */
+#undef HAVE_MKSTEMP
+
+/* Define if you have the mmap function. */
+#undef HAVE_MMAP
+
+/* Define if you have the select function. */
+#undef HAVE_SELECT
+
+/* Define if you have the setenv function. */
+#undef HAVE_SETENV
+
+/* Define if you have the snprintf function. */
+#undef HAVE_SNPRINTF
+
+/* Define if you have the strdup function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+
+/* Define if you have the strpbrk function. */
+#undef HAVE_STRPBRK
+
+/* Define if you have the strsep function. */
+#undef HAVE_STRSEP
+
+/* Define if you have the strtol function. */
+#undef HAVE_STRTOL
+
+/* Define if you have the strtoul function. */
+#undef HAVE_STRTOUL
+
+/* Define if you have the unsetenv function. */
+#undef HAVE_UNSETENV
+
+/* Define if you have the valloc function. */
+#undef HAVE_VALLOC
+
+/* Define if you have the vsnprintf function. */
+#undef HAVE_VSNPRINTF
diff --git a/contrib/nvi/build/config.sub b/contrib/nvi/build/config.sub
new file mode 100755
index 000000000000..43f086781962
--- /dev/null
+++ b/contrib/nvi/build/config.sub
@@ -0,0 +1,872 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS (if any).
+basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+if [ $basic_machine != $1 ]
+then os=`echo $1 | sed 's/.*-/-/'`
+else os=; fi
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp )
+ os=
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-unknown/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i[345]86 | i860 | m68k | m68000 | m88k | ns32k | arm \
+ | arme[lb] | pyramid \
+ | tron | a29k | 580 | i960 | h8300 | hppa1.0 | hppa1.1 \
+ | alpha | we32k | ns16k | clipper | sparclite | i370 | sh \
+ | powerpc | powerpcle | sparc64 | 1750a | dsp16xx | mips64 | mipsel \
+ | pdp11 | mips64el | mips64orion | mips64orionel \
+ | sparc)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ vax-* | tahoe-* | i[345]86-* | i860-* | m68k-* | m68000-* | m88k-* \
+ | sparc-* | ns32k-* | fx80-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* | power-* \
+ | none-* | 580-* | cray2-* | h8300-* | i960-* | xmp-* | ymp-* \
+ | hppa1.0-* | hppa1.1-* | alpha-* | we32k-* | cydra-* | ns16k-* \
+ | pn-* | np1-* | xps100-* | clipper-* | orion-* | sparclite-* \
+ | pdp11-* | sh-* | powerpc-* | powerpcle-* | sparc64-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigados)
+ basic_machine=m68k-cbm
+ os=-amigados
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[345]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ os=-sysv32
+ ;;
+ i[345]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ os=-sysv4
+ ;;
+ i[345]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ os=-sysv
+ ;;
+ i[345]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-unknown/'`
+ os=-solaris2
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ mac | macintosh)
+ basic_machine=m68k-apple
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | p6)
+ # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium
+ basic_machine=i586-intel
+ ;;
+ pentium-* | p5-* | p6-*)
+ # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ k5)
+ # We don't have specific support for AMD's K5 yet, so just call it a Pentium
+ basic_machine=i586-amd
+ ;;
+ nexen)
+ # We don't have specific support for Nexgen yet, so just call it a Pentium
+ basic_machine=i586-nexgen
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ mips)
+ basic_machine=mips-mips
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -unixware* | svr4*)
+ os=-sysv4
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[345]* \
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigados* | -msdos* | -newsos* | -unicos* | -aos* \
+ | -nindy* | -vxworks* | -ebmon* | -hms* | -mvs* | -clix* \
+ | -riscos* | -linux* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* )
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -aux*)
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigados
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
diff --git a/contrib/nvi/build/configure b/contrib/nvi/build/configure
new file mode 100755
index 000000000000..e2055d113ca8
--- /dev/null
+++ b/contrib/nvi/build/configure
@@ -0,0 +1,4446 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.7
+# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --enable-debug Build a debugging version."
+ac_help="$ac_help
+ --with-x use the X Window System"
+ac_help="$ac_help
+ --enable-perlinterp Include a Perl interpreter in vi."
+ac_help="$ac_help
+ --enable-tknvi Build a Tk/Tcl front-end for vi."
+ac_help="$ac_help
+ --enable-tclinterp Include a Tk/Tcl interpreter in vi."
+ac_help="$ac_help
+ --disable-curses DON'T use the nvi-provided curses routines."
+ac_help="$ac_help
+ --disable-db DON'T use the nvi-provided DB routines."
+ac_help="$ac_help
+ --disable-re DON'T use the nvi-provided RE routines."
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.7"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=../common/main.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='echo $CPP $CPPFLAGS 1>&5;
+$CPP $CPPFLAGS'
+ac_compile='echo ${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5;
+${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5'
+ac_link='echo ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5;
+${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5'
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ for ac_prog in ginstall installbsd scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ # OSF/1 installbsd also uses dspmsg, but is usable.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_ifs"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Make sure we can run config.sub.
+if $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`$ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`$ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+if test "$program_transform_name" = s,x,x,; then
+ program_transform_name=
+else
+ # Double any \ or $. echo might interpret backslashes.
+ cat <<\EOF_SED > conftestsed
+s,\\,\\\\,g; s,\$,$$,g
+EOF_SED
+ program_transform_name="`echo $program_transform_name|sed -f conftestsed`"
+ rm -f conftestsed
+fi
+test "$program_prefix" != NONE &&
+ program_transform_name="s,^,${program_prefix},; $program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s,\$\$,${program_suffix},; $program_transform_name"
+
+# sed with no file args requires a program.
+test "$program_transform_name" = "" && program_transform_name="s,x,x,"
+
+
+echo $ac_n "checking if --enable-debug option specified""... $ac_c" 1>&6
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+ vi_cv_debug="yes"
+else
+ vi_cv_debug="no"
+fi
+
+if test "$vi_cv_debug" = yes; then
+ cat >> confdefs.h <<\EOF
+#define DEBUG 1
+EOF
+
+ OPTFLAG=${OPTFLAG-"-g"}
+ no_op_OPTFLAG=${no_op_OPTFLAG-"-g"}
+fi
+echo "$ac_t""$vi_cv_debug" 1>&6
+
+
+
+case "$host_os" in
+aix3.2.5) OPTFLAG=${OPTFLAG-"-O"};;
+aix4.1*) CFLAGS=${CFLAGS-"-qstrict"}
+ OPTFLAG=${OPTFLAG-"-O3"};;
+aux*) CPPFLAGS=${CPPFLAGS-"-ZP -D_BSD_SOURCE -D_SYSV_SOURCE -D_AUX_SOURCE"}
+ LDFLAGS=${LDFLAGS-"-ZP"}
+ OPTFLAG=${OPTFLAG-"-O"};;
+bsd4.4) OPTFLAG=${OPTFLAG-"-O2"};;
+bsdi*) CC=${CC-"shlicc"}
+ OPTFLAG=${OPTFLAG-"-O2"};;
+irix6*) OPTFLAG=${OPTFLAG-"-O2"};;
+irix*) OPTFLAG=${OPTFLAG-"-O2"};;
+lynxos*) # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ if test "${CFLAGS+set}" != set; then
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_gcc_g=yes
+else
+ ac_cv_prog_gcc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6
+ if test $ac_cv_prog_gcc_g = yes; then
+ CFLAGS="-g -O"
+ else
+ CFLAGS="-O"
+ fi
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+ echo $ac_n "checking for GNU C (gcc) version 2.x""... $ac_c" 1>&6
+ ac_cv_gcc_vers=`${CC-cc} -v 2>&1 | \
+ grep "gcc version " | sed 's/.*version //'`
+ ac_cv_gcc_major=`echo "$ac_cv_gcc_vers" | sed 's/\..*//'`
+ if test "$ac_cv_gcc_major" = "2" ; then
+ echo "$ac_t""yes" 1>&6
+ else
+ echo "$ac_t""no" 1>&6
+ echo "Fatal error: Nvi requires gcc 2.x to build on LynxOS."
+ echo "See build/README.LynxOS for more information."
+ exit 1
+ fi;;
+nextstep3) CPPFLAGS=${CPPFLAGS-"-w -pipe -posix"}
+ LDFLAGS=${LDFLAGS-"-posix"}
+ OPTFLAG=${OPTFLAG-"-O9"};;
+osf*) CFLAGS=${CFLAGS-"-Olimit 1000"};;
+solaris*) no_op_OPTFLAG=${no_op_OPTFLAG-""};;
+sunos*) no_op_OPTFLAG=${no_op_OPTFLAG-""};;
+esac
+
+
+CC=${CC-cc}
+
+
+OPTFLAG=${OPTFLAG-"-O"}
+
+
+no_op_OPTFLAG=${no_op_OPTFLAG-"$OPTFLAG"}
+
+case "$host_os" in
+bsdi2.1) LIBS=${LIBS-"-lipc"};;
+dgux*) LIBS=${LIBS-"-ldgc"};;
+irix6*) LIBS=${LIBS-"-lbsd"};;
+irix*) LIBS=${LIBS-"-lc_s -lbsd"};;
+isc*) LIBS=${LIBS-"-lcposix -linet"};;
+netbsd1*) LIBS=${LIBS-"-lcrypt"};;
+ptx*) LIBS=${LIBS-"-lseq -linet -lsocket"};;
+sco3.2*) LIBS=${LIBS-"-lsocket"};;
+sinix*) LIBS=${LIBS-"-lelf -lc"};;
+solaris*) LIBS=${LIBS-"-lsocket -lnsl -ldl"}
+ RLIBS=yes;;
+wgs*) LIBS=${LIBS-"-lnsl"};;
+esac
+
+case "$host_os" in
+aux*) LIBOBJS="getopt.o strpbrk.o $LIBOBJS";;
+esac
+
+case "$host_os" in
+ultrix*) cat >> confdefs.h <<\EOF
+#define HAVE_BROKEN_VDISABLE 1
+EOF
+;;
+esac
+
+CPPFLAGS="$ADDCPPFLAGS $CPPFLAGS"
+
+LDFLAGS="$ADDLDFLAGS $LDFLAGS"
+
+LIBS="$ADDLIBS $LIBS"
+
+# If we cannot run a trivial program, we must be cross compiling.
+echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_c_cross'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_cross=yes
+else
+cat > conftest.$ac_ext <<EOF
+#line 839 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_c_cross=no
+else
+ ac_cv_c_cross=yes
+fi
+fi
+rm -fr conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_cross" 1>&6
+cross_compiling=$ac_cv_c_cross
+
+echo $ac_n "checking whether the compiler ($CC $CFLAGS $LDFLAGS) actually works""... $ac_c" 1>&6
+
+ ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='echo $CPP $CPPFLAGS 1>&5;
+$CPP $CPPFLAGS'
+ac_compile='echo ${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5;
+${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5'
+ac_link='echo ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5;
+${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5'
+
+ if test "$cross_compiling" = yes; then
+ cat > conftest.$ac_ext <<EOF
+#line 869 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ am_cv_prog_cc_works=yes
+else
+ rm -rf conftest*
+ am_cv_prog_cc_works=no
+fi
+rm -f conftest*
+
+else
+cat > conftest.$ac_ext <<EOF
+#line 888 "configure"
+#include "confdefs.h"
+main() { exit(0); }
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ am_cv_prog_cc_works=yes
+else
+ am_cv_prog_cc_works=no
+fi
+fi
+rm -fr conftest*
+
+case "$am_cv_prog_cc_works" in
+ *no) { echo "configure: error: Installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } ;;
+ *yes) ;;
+esac
+echo "$ac_t""yes" 1>&6
+
+
+PATH="$PATH:/usr/bin:/usr/sbin:/sbin:/etc:/usr/etc:/usr/lib:/usr/ucblib:"
+
+# Extract the first word of "sh", so it can be a program name with args.
+set dummy sh; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_shell'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_shell" in
+ /*)
+ ac_cv_path_vi_cv_path_shell="$vi_cv_path_shell" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_shell="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_shell" && ac_cv_path_vi_cv_path_shell="no"
+ ;;
+esac
+fi
+vi_cv_path_shell="$ac_cv_path_vi_cv_path_shell"
+if test -n "$vi_cv_path_shell"; then
+ echo "$ac_t""$vi_cv_path_shell" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$vi_cv_path_shell" = no; then
+ echo "Fatal error: the shell utility not found."
+ exit 1
+fi
+
+# Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_sendmail'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_sendmail" in
+ /*)
+ ac_cv_path_vi_cv_path_sendmail="$vi_cv_path_sendmail" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_sendmail="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_sendmail" && ac_cv_path_vi_cv_path_sendmail="no"
+ ;;
+esac
+fi
+vi_cv_path_sendmail="$ac_cv_path_vi_cv_path_sendmail"
+if test -n "$vi_cv_path_sendmail"; then
+ echo "$ac_t""$vi_cv_path_sendmail" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$vi_cv_path_sendmail" = no; then
+ echo "WARNING: The sendmail utility was not found!"
+ echo "WARNING: Users will not be told of saved files."
+fi
+
+
+for ac_prog in perl5 perl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_perl'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_perl" in
+ /*)
+ ac_cv_path_vi_cv_path_perl="$vi_cv_path_perl" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_perl="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+vi_cv_path_perl="$ac_cv_path_vi_cv_path_perl"
+if test -n "$vi_cv_path_perl"; then
+ echo "$ac_t""$vi_cv_path_perl" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$vi_cv_path_perl" && break
+done
+test -n "$vi_cv_path_perl" || vi_cv_path_perl="no"
+
+
+
+echo $ac_n "checking for preserve directory""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_path_preserve'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ dirlist="/var/preserve /var/tmp /usr/tmp"
+ vi_cv_path_preserve=no
+ for i in $dirlist; do
+ if test -d $i/vi.recover; then
+ vi_cv_path_preserve=$i/vi.recover
+ break;
+ fi
+ done
+ if test "$vi_cv_path_preserve" = no; then
+ for i in $dirlist; do
+ if test -d $i -a -w $i; then
+ vi_cv_path_preserve=$i/vi.recover
+ break;
+ fi
+ done
+
+ fi
+fi
+
+if test "$vi_cv_path_preserve" = no; then
+ echo "Fatal error: no writeable preserve directory found."
+ exit 1
+fi
+echo "$ac_t""$vi_cv_path_preserve" 1>&6
+
+# Extract the first word of "chmod", so it can be a program name with args.
+set dummy chmod; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_chmod'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_chmod" in
+ /*)
+ ac_cv_path_vi_cv_path_chmod="$vi_cv_path_chmod" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_chmod="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_chmod" && ac_cv_path_vi_cv_path_chmod="missing_chmod"
+ ;;
+esac
+fi
+vi_cv_path_chmod="$ac_cv_path_vi_cv_path_chmod"
+if test -n "$vi_cv_path_chmod"; then
+ echo "$ac_t""$vi_cv_path_chmod" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_cp'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_cp" in
+ /*)
+ ac_cv_path_vi_cv_path_cp="$vi_cv_path_cp" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_cp="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_cp" && ac_cv_path_vi_cv_path_cp="missing_cp"
+ ;;
+esac
+fi
+vi_cv_path_cp="$ac_cv_path_vi_cv_path_cp"
+if test -n "$vi_cv_path_cp"; then
+ echo "$ac_t""$vi_cv_path_cp" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_ln'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_ln" in
+ /*)
+ ac_cv_path_vi_cv_path_ln="$vi_cv_path_ln" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_ln="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_ln" && ac_cv_path_vi_cv_path_ln="missing_ln"
+ ;;
+esac
+fi
+vi_cv_path_ln="$ac_cv_path_vi_cv_path_ln"
+if test -n "$vi_cv_path_ln"; then
+ echo "$ac_t""$vi_cv_path_ln" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "mkdir", so it can be a program name with args.
+set dummy mkdir; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_mkdir'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_mkdir" in
+ /*)
+ ac_cv_path_vi_cv_path_mkdir="$vi_cv_path_mkdir" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_mkdir="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_mkdir" && ac_cv_path_vi_cv_path_mkdir="missing_mkdir"
+ ;;
+esac
+fi
+vi_cv_path_mkdir="$ac_cv_path_vi_cv_path_mkdir"
+if test -n "$vi_cv_path_mkdir"; then
+ echo "$ac_t""$vi_cv_path_mkdir" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_rm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_rm" in
+ /*)
+ ac_cv_path_vi_cv_path_rm="$vi_cv_path_rm" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_rm="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_rm" && ac_cv_path_vi_cv_path_rm="missing_rm"
+ ;;
+esac
+fi
+vi_cv_path_rm="$ac_cv_path_vi_cv_path_rm"
+if test -n "$vi_cv_path_rm"; then
+ echo "$ac_t""$vi_cv_path_rm" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_vi_cv_path_strip'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$vi_cv_path_strip" in
+ /*)
+ ac_cv_path_vi_cv_path_strip="$vi_cv_path_strip" # Let the user override the test with a path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_vi_cv_path_strip="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_path_vi_cv_path_strip" && ac_cv_path_vi_cv_path_strip="missing_strip"
+ ;;
+esac
+fi
+vi_cv_path_strip="$ac_cv_path_vi_cv_path_strip"
+if test -n "$vi_cv_path_strip"; then
+ echo "$ac_t""$vi_cv_path_strip" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1252 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1266 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+ :
+fi
+
+if test "x$with_x" = xno; then
+ no_x=yes
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ no_x=
+ else
+if eval "test \"`echo '$''{'ac_cv_path_x'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+no_x=yes
+rm -fr conftestdir
+if mkdir conftestdir; then
+ cd conftestdir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat > Imakefile <<'EOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ no_x=
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration.
+ case "$ac_im_incroot" in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+ esac
+ case "$ac_im_usrlibdir" in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftestdir
+fi
+
+if test "$no_x" = yes; then
+test -z "$x_direct_test_library" && x_direct_test_library=Xt
+test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+cat > conftest.$ac_ext <<EOF
+#line 1352 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ no_x= ac_x_includes=
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ for ac_dir in \
+ /usr/X11R6/include \
+ /usr/X11R5/include \
+ /usr/X11R4/include \
+ \
+ /usr/include/X11R6 \
+ /usr/include/X11R5 \
+ /usr/include/X11R4 \
+ \
+ /usr/local/X11R6/include \
+ /usr/local/X11R5/include \
+ /usr/local/X11R4/include \
+ \
+ /usr/local/include/X11R6 \
+ /usr/local/include/X11R5 \
+ /usr/local/include/X11R4 \
+ \
+ /usr/X11/include \
+ /usr/include/X11 \
+ /usr/local/X11/include \
+ /usr/local/include/X11 \
+ \
+ /usr/X386/include \
+ /usr/x386/include \
+ /usr/XFree86/include/X11 \
+ \
+ /usr/include \
+ /usr/local/include \
+ /usr/unsupported/include \
+ /usr/athena/include \
+ /usr/local/x11r5/include \
+ /usr/lpp/Xamples/include \
+ \
+ /usr/openwin/include \
+ /usr/openwin/share/include \
+ ; \
+ do
+ if test -r "$ac_dir/$x_direct_test_include"; then
+ no_x= ac_x_includes=$ac_dir
+ break
+ fi
+ done
+fi
+rm -f conftest*
+
+# Check for the libraries.
+# See if we find them without any special options.
+# Don't add to $LIBS permanently.
+ac_save_LIBS="$LIBS"
+LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1415 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS" no_x= ac_x_libraries=
+else
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+ /usr/X11R6/lib \
+ /usr/X11R5/lib \
+ /usr/X11R4/lib \
+ \
+ /usr/lib/X11R6 \
+ /usr/lib/X11R5 \
+ /usr/lib/X11R4 \
+ \
+ /usr/local/X11R6/lib \
+ /usr/local/X11R5/lib \
+ /usr/local/X11R4/lib \
+ \
+ /usr/local/lib/X11R6 \
+ /usr/local/lib/X11R5 \
+ /usr/local/lib/X11R4 \
+ \
+ /usr/X11/lib \
+ /usr/lib/X11 \
+ /usr/local/X11/lib \
+ /usr/local/lib/X11 \
+ \
+ /usr/X386/lib \
+ /usr/x386/lib \
+ /usr/XFree86/lib/X11 \
+ \
+ /usr/lib \
+ /usr/local/lib \
+ /usr/unsupported/lib \
+ /usr/athena/lib \
+ /usr/local/x11r5/lib \
+ /usr/lpp/Xamples/lib \
+ \
+ /usr/openwin/lib \
+ /usr/openwin/share/lib \
+ ; \
+do
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+ no_x= ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest*
+
+fi
+if test "$no_x" = yes; then
+ ac_cv_path_x="no_x=yes"
+else
+ ac_cv_path_x="no_x= ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+ fi
+ eval "$ac_cv_path_x"
+fi # $with_x != no
+
+if test "$no_x" = yes; then
+ echo "$ac_t""no" 1>&6
+else
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ ac_cv_path_x="no_x= ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+
+if test "$no_x" != yes; then
+ if test "X$x_libraries" != "X"; then
+ if test "X$RLIBS" = "Xyes"; then
+ XLIBS="-R$x_libraries -L$x_libraries $XLIBS"
+ else
+ XLIBS="-L$x_libraries $XLIBS"
+ fi
+ fi
+ XLIBS="$XLIBS -lX11"
+ if test "X$x_includes" != "X"; then
+ XINCS="-I$x_includes"
+ fi
+fi
+
+
+
+echo $ac_n "checking if --enable-perlinterp option specified""... $ac_c" 1>&6
+# Check whether --enable-perlinterp or --disable-perlinterp was given.
+if test "${enable_perlinterp+set}" = set; then
+ enableval="$enable_perlinterp"
+ vi_cv_perlinterp="yes"
+else
+ vi_cv_perlinterp="no"
+fi
+
+echo "$ac_t""$vi_cv_perlinterp" 1>&6
+if test "$vi_cv_perlinterp" = "yes"; then
+ if test "$vi_cv_path_perl" = no; then
+ echo "Fatal error: no perl5 utility found."
+ exit 1
+ fi
+ $vi_cv_path_perl -e 'require 5.002' || {
+ echo "Fatal error: perl5 must be version 5.002 or later."
+ exit 1
+ }
+ $vi_cv_path_perl -e 'close(STDERR);require 5.003_01' &&
+ cat >> confdefs.h <<\EOF
+#define HAVE_PERL_5_003_01 1
+EOF
+
+
+ eval `$vi_cv_path_perl -V:shrpenv`
+ if test "X$shrpenv" = "XUNKNOWN"; then # pre 5.003_04
+ shrpenv=""
+ fi
+ vi_cv_perllib=`$vi_cv_path_perl -MConfig -e 'print $Config{privlib}'`
+ perlcppflags=`$vi_cv_path_perl -Mlib=$srcdir -MExtUtils::Embed \
+ -e 'ccflags;perl_inc'`
+ if test "X$perlcppflags" != "X"; then
+ CPPFLAGS="$perlcppflags $CPPFLAGS"
+ fi
+ perllibs=`cd $srcdir;$vi_cv_path_perl -MExtUtils::Embed \
+ -e 'ldopts'`
+ if test "X$perllibs" != "X"; then
+ LIBS="$perllibs $LIBS"
+ fi
+ perlldflags=`cd $srcdir;$vi_cv_path_perl -MExtUtils::Embed \
+ -e 'ccdlflags'`
+ if test "X$perlldflags" != "X"; then
+ LDFLAGS="$perlldflags $LDFLAGS"
+ fi
+ LIBOBJS="perl.o perlsfio.o $LIBOBJS"
+ cat >> confdefs.h <<\EOF
+#define HAVE_PERL_INTERP 1
+EOF
+
+fi
+
+
+
+echo $ac_n "checking if --enable-tknvi option specified""... $ac_c" 1>&6
+# Check whether --enable-tknvi or --disable-tknvi was given.
+if test "${enable_tknvi+set}" = set; then
+ enableval="$enable_tknvi"
+ vi_cv_tknvi="yes"
+else
+ vi_cv_tknvi="no"
+fi
+
+echo "$ac_t""$vi_cv_tknvi" 1>&6
+if test "$vi_cv_tknvi" = "yes"; then
+ tknvi=tknvi
+ TKLIBS="-ltk -ltcl -lm $XLIBS $LIBS"
+fi
+
+echo $ac_n "checking if --enable-tclinterp option specified""... $ac_c" 1>&6
+# Check whether --enable-tclinterp or --disable-tclinterp was given.
+if test "${enable_tclinterp+set}" = set; then
+ enableval="$enable_tclinterp"
+ vi_cv_tclinterp="yes"
+else
+ vi_cv_tclinterp="no"
+fi
+
+echo "$ac_t""$vi_cv_tclinterp" 1>&6
+if test "$vi_cv_tclinterp" = "yes"; then
+ LIBOBJS="tcl.o $LIBOBJS"
+ LIBS="-ltk -ltcl -lm $XLIBS $LIBS"
+ cat >> confdefs.h <<\EOF
+#define HAVE_TCL_INTERP 1
+EOF
+
+fi
+
+if test "$vi_cv_tknvi" = "yes" || test "$vi_cv_tclinterp" = "yes"; then
+ echo $ac_n "checking for -ltcl""... $ac_c" 1>&6
+ac_lib_var=`echo tcl | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltcl -ltk -lm $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1611 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+main()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ vi_cv_tkfatal="no"
+else
+ echo "$ac_t""no" 1>&6
+vi_cv_tkfatal="yes"
+fi
+
+ if test "$vi_cv_tkfatal" = "yes"; then
+ echo "Fatal error: no Tk/Tcl library; see the section"
+ echo "ADDING LIBRARIES AND INCLUDE FILES in the README file."
+ exit 1
+ fi
+fi
+
+if test "$vi_cv_tclinterp" = yes || test "$vi_cv_perlinterp" = yes; then
+ LIBOBJS="api.o $LIBOBJS"
+fi
+
+echo $ac_n "checking for -ltermlib""... $ac_c" 1>&6
+ac_lib_var=`echo termlib | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltermlib $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1657 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+tgetent()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ vi_cv_termlib=-ltermlib
+else
+ echo "$ac_t""no" 1>&6
+vi_cv_termlib=no
+fi
+
+if test "$vi_cv_termlib" = no; then
+ echo $ac_n "checking for -ltermcap""... $ac_c" 1>&6
+ac_lib_var=`echo termcap | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltermcap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1693 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+tgetent()
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ vi_cv_termlib=-ltermcap
+else
+ echo "$ac_t""no" 1>&6
+vi_cv_termlib=no
+fi
+
+fi
+if test "$vi_cv_termlib" != no; then
+ LIBS="$vi_cv_termlib $LIBS"
+fi
+
+echo $ac_n "checking if --disable-curses option specified""... $ac_c" 1>&6
+# Check whether --enable-curses or --disable-curses was given.
+if test "${enable_curses+set}" = set; then
+ enableval="$enable_curses"
+ vi_cv_curses="other curses"
+else
+ vi_cv_curses="bundled curses"
+fi
+
+echo "$ac_t""$vi_cv_curses" 1>&6
+case "$vi_cv_curses" in
+"bundled curses")
+ CPPFLAGS="-I\$(srcdir)/curses $CPPFLAGS"
+ cobjs="\$(COBJS)";;
+"other curses")
+ LIBS="-lcurses $LIBS";;
+esac
+
+echo $ac_n "checking for sys/mman.h""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_include_sys_mman'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1748 "configure"
+#include "confdefs.h"
+#include <sys/mman.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ vi_cv_include_sys_mman=yes
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ vi_cv_include_sys_mman=no
+fi
+rm -f conftest*
+fi
+
+if test "$vi_cv_include_sys_mman" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_MMAN_H 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_include_sys_mman" 1>&6
+
+echo $ac_n "checking for sys/select.h""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_include_sys_select'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1778 "configure"
+#include "confdefs.h"
+#include <sys/select.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ vi_cv_include_sys_select=yes
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ vi_cv_include_sys_select=no
+fi
+rm -f conftest*
+fi
+
+if test "$vi_cv_include_sys_select" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_SELECT_H 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_include_sys_select" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1808 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1830 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1848 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1869 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for ssize_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_type_ssize_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1904 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "ssize_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_ssize_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_ssize_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_ssize_t" 1>&6
+if test $ac_cv_type_ssize_t = no; then
+ cat >> confdefs.h <<\EOF
+#define ssize_t int
+EOF
+
+fi
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1937 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() { return 0; }
+int t() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1953 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() { return 0; }
+int t() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_c_bigendian=yes
+else
+ rm -rf conftest*
+ ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 1982 "configure"
+#include "confdefs.h"
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_c_bigendian=no
+else
+ ac_cv_c_bigendian=yes
+fi
+fi
+rm -fr conftest*
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+ cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2019 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2093 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+int main() { return 0; }
+int t() {
+struct stat s; s.st_blksize;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_struct_st_blksize=yes
+else
+ rm -rf conftest*
+ ac_cv_struct_st_blksize=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6
+if test $ac_cv_struct_st_blksize = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ST_BLKSIZE 1
+EOF
+
+fi
+
+echo $ac_n "checking for mode_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2126 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "mode_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_mode_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_mode_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_mode_t" 1>&6
+if test $ac_cv_type_mode_t = no; then
+ cat >> confdefs.h <<\EOF
+#define mode_t int
+EOF
+
+fi
+
+echo $ac_n "checking for off_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2157 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "off_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_off_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_off_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_off_t" 1>&6
+if test $ac_cv_type_off_t = no; then
+ cat >> confdefs.h <<\EOF
+#define off_t long
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2188 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "pid_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2219 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "size_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2250 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() { return 0; }
+int t() {
+struct tm *tp; tp->tm_sec;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_struct_tm=time.h
+else
+ rm -rf conftest*
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_struct_tm" 1>&6
+if test $ac_cv_struct_tm = sys/time.h; then
+ cat >> confdefs.h <<\EOF
+#define TM_IN_SYS_TIME 1
+EOF
+
+fi
+
+
+ for ac_func in bsearch gethostname getopt memchr memcpy memmove memset
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2286 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in bsearch gethostname getopt memchr memcpy memmove memset
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2337 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+
+done
+
+ for ac_func in mkstemp mmap snprintf strdup strerror strpbrk strtol
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2386 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in mkstemp mmap snprintf strdup strerror strpbrk strtol
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2437 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+
+done
+
+ for ac_func in strtoul vsnprintf
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2486 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in strtoul vsnprintf
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2537 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+
+done
+
+
+for ac_func in select
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2587 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in setenv
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2638 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ need_env=no
+else
+ echo "$ac_t""no" 1>&6
+need_env=yes
+fi
+done
+
+for ac_func in strsep
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2690 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+ need_strsep=no
+else
+ echo "$ac_t""no" 1>&6
+need_strsep=yes
+fi
+done
+
+for ac_func in unsetenv
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2742 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+need_env=yes
+fi
+done
+
+
+for ac_func in valloc getpagesize
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2795 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+echo $ac_n "checking for working mmap""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_mmap'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_mmap=no
+else
+cat > conftest.$ac_ext <<EOF
+#line 2847 "configure"
+#include "confdefs.h"
+
+/* Thanks to Mike Haertel and Jim Avera for this test. */
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#ifndef HAVE_GETPAGESIZE
+# include <sys/param.h>
+# ifdef EXEC_PAGESIZE
+# define getpagesize() EXEC_PAGESIZE
+# else
+# ifdef NBPG
+# define getpagesize() NBPG * CLSIZE
+# ifndef CLSIZE
+# define CLSIZE 1
+# endif
+# else
+# ifdef NBPC
+# define getpagesize() NBPC
+# else
+# define getpagesize() PAGESIZE /* SVR4 */
+# endif
+# endif
+# endif
+#endif
+
+#ifndef HAVE_VALLOC
+# define valloc malloc
+#endif
+
+#ifdef __cplusplus
+extern "C" { void *valloc(unsigned), *malloc(unsigned); }
+#else
+char *valloc(), *malloc();
+#endif
+
+int
+main()
+{
+ char *buf1, *buf2, *buf3;
+ int i = getpagesize(), j;
+ int i2 = i * 2;
+ int fd;
+
+ buf1 = (char *)valloc(i2);
+ buf2 = (char *)valloc(i);
+ buf3 = (char *)malloc(i2);
+ for (j = 0; j < i2; ++j)
+ *(buf1 + j) = rand();
+ fd = open("conftestmmap", O_CREAT | O_RDWR, 0666);
+ write(fd, buf1, i2);
+ mmap(buf2, i, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, fd, 0);
+ for (j = 0; j < i; ++j)
+ if (*(buf1 + j) != *(buf2 + j))
+ exit(1);
+ lseek(fd, (long)i, 0);
+ read(fd, buf2, i); /* read into mapped memory -- file should not change */
+ /* (it does in i386 SVR4.0 - Jim Avera, jima@netcom.com) */
+ lseek(fd, (long)0, 0);
+ read(fd, buf3, i2);
+ for (j = 0; j < i2; ++j)
+ if (*(buf1 + j) != *(buf3 + j))
+ exit(1);
+ exit(0);
+}
+
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_func_mmap=yes
+else
+ ac_cv_func_mmap=no
+fi
+fi
+rm -fr conftest*
+fi
+
+echo "$ac_t""$ac_cv_func_mmap" 1>&6
+if test $ac_cv_func_mmap = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_MMAP 1
+EOF
+
+fi
+
+ac_safe=`echo "vfork.h" | tr './\055' '___'`
+echo $ac_n "checking for vfork.h""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2940 "configure"
+#include "confdefs.h"
+#include <vfork.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VFORK_H 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for working vfork""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ echo $ac_n "checking for vfork""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2976 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vfork(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+char vfork();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vfork) || defined (__stub___vfork)
+choke me
+#else
+vfork();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_vfork=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_vfork=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'vfork`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+cat > conftest.$ac_ext <<EOF
+#line 3017 "configure"
+#include "confdefs.h"
+/* Thanks to Paul Eggert for this test. */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent.
+ The compiler is told about this with #include <vfork.h>,
+ but some compilers (e.g. gcc -O) don't grok <vfork.h>.
+ Test for this by using a static variable whose address
+ is put into a register that is clobbered by the vfork. */
+static
+#ifdef __cplusplus
+sparc_address_test (int arg)
+#else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0)
+ perror ("vfork");
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+main() {
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test ();
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems.
+ This test uses lots of local variables, at least
+ as many local variables as main has allocated so far
+ including compiler temporaries. 4 locals are enough for
+ gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe.
+ A buggy compiler should reuse the register of parent
+ for one of the local variables, since it will think that
+ parent can't possibly be used any more in this routine.
+ Assigning to the local variable will thus munge parent
+ in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3),
+ vfork doesn't separate parent from child file descriptors.
+ If the child closes a descriptor before it execs or exits,
+ this munges the parent's descriptor as well.
+ Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ exit(
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_func_vfork=yes
+else
+ ac_cv_func_vfork=no
+fi
+fi
+rm -fr conftest*
+fi
+
+echo "$ac_t""$ac_cv_func_vfork" 1>&6
+if test $ac_cv_func_vfork = no; then
+ cat >> confdefs.h <<\EOF
+#define vfork fork
+EOF
+
+fi
+
+
+if test "$need_env" = yes; then
+ LIBOBJS="env.o $LIBOBJS"
+fi
+
+if test "$need_strsep" = yes; then
+ LIBOBJS="strsep.o $LIBOBJS"
+fi
+
+echo $ac_n "checking for fcntl/flock""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_lock'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ vi_cv_lock=none
+ case "$host_os" in
+ dgux*);;
+ irix*);;
+ *)
+ cat > conftest.$ac_ext <<EOF
+#line 3147 "configure"
+#include "confdefs.h"
+#include <fcntl.h>
+int main() { return 0; }
+int t() {
+flock(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_lock=flock
+fi
+rm -f conftest*
+;;
+ esac
+ if test "$vi_cv_lock" = none; then
+ cat > conftest.$ac_ext <<EOF
+#line 3164 "configure"
+#include "confdefs.h"
+#include <fcntl.h>
+int main() { return 0; }
+int t() {
+fcntl(0, F_SETLK, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_lock=fcntl
+fi
+rm -f conftest*
+
+ fi
+fi
+
+
+if test "$vi_cv_lock" = flock; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_LOCK_FLOCK 1
+EOF
+
+fi
+if test "$vi_cv_lock" = fcntl; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_LOCK_FCNTL 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_lock" 1>&6
+
+echo $ac_n "checking for ftruncate/chsize""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_ftruncate'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3201 "configure"
+#include "confdefs.h"
+#include <unistd.h>
+int main() { return 0; }
+int t() {
+ftruncate(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_ftruncate=ftruncate
+else
+ rm -rf conftest*
+ cat > conftest.$ac_ext <<EOF
+#line 3215 "configure"
+#include "confdefs.h"
+#include <unistd.h>
+int main() { return 0; }
+int t() {
+chsize(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_ftruncate=chsize
+else
+ rm -rf conftest*
+ vi_cv_ftruncate=no
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_ftruncate" = ftruncate; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_FTRUNCATE_FTRUNCATE 1
+EOF
+
+fi
+if test "$vi_cv_ftruncate" = chsize; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_FTRUNCATE_CHSIZE 1
+EOF
+
+fi
+if test "$vi_cv_ftruncate" = no; then
+ echo
+ echo "Fatal error: no file truncation system call."
+ exit 1
+fi
+echo "$ac_t""$vi_cv_ftruncate" 1>&6
+
+echo $ac_n "checking for tigetstr/tigetnum""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_have_curses_tigetstr'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3261 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+tigetstr(0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_tigetstr=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_tigetstr=no
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_have_curses_tigetstr" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_TIGETSTR 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_have_curses_tigetstr" 1>&6
+
+if test "$vi_cv_curses" = "bundled curses"; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_BSD_CURSES 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_ADDNSTR 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_IDLOK 1
+EOF
+
+else
+ echo $ac_n "checking for addnstr""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_addnstr'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3307 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+addnstr(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_addnstr=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_addnstr=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_addnstr" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_ADDNSTR 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_addnstr" 1>&6
+
+ echo $ac_n "checking for beep""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_beep'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3339 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+beep();
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_beep=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_beep=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_beep" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_BEEP 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_beep" 1>&6
+
+ echo $ac_n "checking for flash""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_flash'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3371 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+flash();
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_flash=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_flash=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_flash" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_FLASH 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_flash" 1>&6
+
+ echo $ac_n "checking for idlok""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_idlok'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3403 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+idlok(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_idlok=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_idlok=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_idlok" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_IDLOK 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_idlok" 1>&6
+
+ echo $ac_n "checking for keypad""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_keypad'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3435 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+keypad(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_keypad=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_keypad=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_keypad" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_KEYPAD 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_keypad" 1>&6
+
+ echo $ac_n "checking for newterm""... $ac_c" 1>&6
+ if eval "test \"`echo '$''{'vi_cv_have_curses_newterm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3467 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+newterm(0, 0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_newterm=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_newterm=no
+fi
+rm -f conftest*
+
+fi
+
+ if test "$vi_cv_have_curses_newterm" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_NEWTERM 1
+EOF
+
+ fi
+ echo "$ac_t""$vi_cv_have_curses_newterm" 1>&6
+
+ if test "$vi_cv_have_curses_newterm" = no; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_BSD_CURSES 1
+EOF
+
+ fi
+fi
+
+echo $ac_n "checking for setupterm""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_have_curses_setupterm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3507 "configure"
+#include "confdefs.h"
+#include <curses.h>
+int main() { return 0; }
+int t() {
+setupterm(0, 0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_have_curses_setupterm=yes
+else
+ rm -rf conftest*
+ vi_cv_have_curses_setupterm=no
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_have_curses_setupterm" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_CURSES_SETUPTERM 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_have_curses_setupterm" 1>&6
+
+echo $ac_n "checking for broken gettimeofday system call""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_gettimeofday'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3539 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+int main() { return 0; }
+int t() {
+gettimeofday(0, 0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_gettimeofday=okay
+else
+ rm -rf conftest*
+ vi_cv_gettimeofday=broken
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_gettimeofday" = broken; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_BROKEN_GETTIMEOFDAY 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_gettimeofday" 1>&6
+
+echo $ac_n "checking for System V pty calls""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_sys5_pty'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3572 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+grantpt(0);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_sys5_pty=yes
+else
+ rm -rf conftest*
+ vi_cv_sys5_pty=no
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_sys5_pty" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS5_PTY 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_sys5_pty" 1>&6
+
+echo $ac_n "checking for revoke system call""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_revoke'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3604 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+revoke("a");
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ vi_cv_revoke=yes
+else
+ rm -rf conftest*
+ vi_cv_revoke=no
+fi
+rm -f conftest*
+
+fi
+
+if test "$vi_cv_revoke" = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_REVOKE 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_revoke" 1>&6
+
+echo $ac_n "checking for int type sprintf return value""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_sprintf_count'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3639 "configure"
+#include "confdefs.h"
+main(){char buf[20]; exit(sprintf(buf, "XXX") != 3);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_sprintf_count=yes
+else
+ vi_cv_sprintf_count=no
+fi
+fi
+rm -fr conftest*
+fi
+
+if test "$vi_cv_sprintf_count" = no; then
+ cat >> confdefs.h <<\EOF
+#define SPRINTF_RET_CHARPNT 1
+EOF
+
+fi
+echo "$ac_t""$vi_cv_sprintf_count" 1>&6
+
+echo $ac_n "checking if --disable-db option specified""... $ac_c" 1>&6
+# Check whether --enable-db or --disable-db was given.
+if test "${enable_db+set}" = set; then
+ enableval="$enable_db"
+ vi_cv_db_lib="other DB"
+else
+ vi_cv_db_lib="bundled DB"
+fi
+
+echo "$ac_t""$vi_cv_db_lib" 1>&6
+case "$vi_cv_db_lib" in
+"bundled DB")
+ CPPFLAGS="-I\$(srcdir)/db/include $CPPFLAGS"
+ LIBOBJS="\$(DBOBJS) $LIBOBJS";;
+"other DB")
+ ;;
+esac
+
+echo $ac_n "checking if --disable-re option specified""... $ac_c" 1>&6
+# Check whether --enable-re or --disable-re was given.
+if test "${enable_re+set}" = set; then
+ enableval="$enable_re"
+ vi_cv_re_lib="other RE"
+else
+ vi_cv_re_lib="bundled RE"
+fi
+
+echo "$ac_t""$vi_cv_re_lib" 1>&6
+case "$vi_cv_re_lib" in
+"bundled RE")
+ CPPFLAGS="-I\$(srcdir)/regex $CPPFLAGS"
+ LIBOBJS="\$(REOBJS) $LIBOBJS";;
+"other RE")
+ ;;
+esac
+
+
+echo $ac_n "checking for u_char""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_uchar'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3703 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_char foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_uchar=yes
+else
+ rm -rf conftest*
+ vi_cv_uchar=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_uchar" 1>&6
+if test "$vi_cv_uchar" = no; then
+ u_char_decl="typedef unsigned char u_char;"
+fi
+
+
+echo $ac_n "checking for u_short""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_ushort'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3733 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_short foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_ushort=yes
+else
+ rm -rf conftest*
+ vi_cv_ushort=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_ushort" 1>&6
+if test "$vi_cv_ushort" = no; then
+ u_short_decl="typedef unsigned short u_short;"
+fi
+
+
+echo $ac_n "checking for u_int""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_uint'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3763 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_int foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_uint=yes
+else
+ rm -rf conftest*
+ vi_cv_uint=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_uint" 1>&6
+if test "$vi_cv_uint" = no; then
+ u_int_decl="typedef unsigned int u_int;"
+fi
+
+
+echo $ac_n "checking for u_long""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_ulong'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3793 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_long foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_ulong=yes
+else
+ rm -rf conftest*
+ vi_cv_ulong=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_ulong" 1>&6
+if test "$vi_cv_ulong" = no; then
+ u_long_decl="typedef unsigned long u_long;"
+fi
+
+
+echo $ac_n "checking for u_int8_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_uint8'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3823 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_int8_t foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_uint8=yes
+else
+ rm -rf conftest*
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3840 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(unsigned char) != 1);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_uint8="unsigned char"
+else
+ vi_cv_uint8=no
+fi
+fi
+rm -fr conftest*
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_uint8" 1>&6
+if test "$vi_cv_uint8" = no; then
+ echo
+ echo "Fatal error: no unsigned, 8-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint8" != yes; then
+ u_int8_decl="typedef $vi_cv_uint8 u_int8_t;"
+fi
+
+
+echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_uint16'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3873 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_int16_t foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_uint16=yes
+else
+ rm -rf conftest*
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3890 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(unsigned short) != 2);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_uint16="unsigned short"
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3902 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(unsigned int) != 2);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_uint16="unsigned int"
+else
+ vi_cv_uint16=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+rm -fr conftest*
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_uint16" 1>&6
+if test "$vi_cv_uint16" = no; then
+ echo
+ echo "Fatal error: no unsigned, 16-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint16" != yes; then
+ u_int16_decl="typedef $vi_cv_uint16 u_int16_t;"
+fi
+
+
+echo $ac_n "checking for int16_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_int16'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3938 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+int16_t foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_int16=yes
+else
+ rm -rf conftest*
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3955 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(short) != 2);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_int16="short"
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 3967 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(int) != 2);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_int16="int"
+else
+ vi_cv_int16=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+rm -fr conftest*
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_int16" 1>&6
+if test "$vi_cv_int16" = no; then
+ echo
+ echo "Fatal error: no signed, 16-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_int16" != yes; then
+ int16_decl="typedef $vi_cv_int16 int16_t;"
+fi
+
+
+echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_uint32'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 4003 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+u_int32_t foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_uint32=yes
+else
+ rm -rf conftest*
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 4020 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(unsigned int) != 4);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_uint32="unsigned int"
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 4032 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(unsigned long) != 4);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_uint32="unsigned long"
+else
+ vi_cv_uint32=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+rm -fr conftest*
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_uint32" 1>&6
+if test "$vi_cv_uint32" = no; then
+ echo
+ echo "Fatal error: no unsigned, 32-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint32" != yes; then
+ u_int32_decl="typedef $vi_cv_uint32 u_int32_t;"
+fi
+
+
+echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'vi_cv_int32'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 4068 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+int32_t foo;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ vi_cv_int32=yes
+else
+ rm -rf conftest*
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 4085 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(int) != 4);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_int32="int"
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+cat > conftest.$ac_ext <<EOF
+#line 4097 "configure"
+#include "confdefs.h"
+main(){exit(sizeof(long) != 4);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ vi_cv_int32="long"
+else
+ vi_cv_int32=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+rm -fr conftest*
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$vi_cv_int32" 1>&6
+if test "$vi_cv_int32" = no; then
+ echo
+ echo "Fatal error: no signed, 32-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_int32" != yes; then
+ int32_decl="typedef $vi_cv_int32 int32_t;"
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \
+ >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.7"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile port.h:port.h.in
+ pathnames.h:pathnames.h.in recover:recover.in config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@CC@%$CC%g
+s%@OPTFLAG@%$OPTFLAG%g
+s%@no_op_OPTFLAG@%$no_op_OPTFLAG%g
+s%@vi_cv_path_shell@%$vi_cv_path_shell%g
+s%@vi_cv_path_sendmail@%$vi_cv_path_sendmail%g
+s%@vi_cv_path_perl@%$vi_cv_path_perl%g
+s%@vi_cv_path_preserve@%$vi_cv_path_preserve%g
+s%@vi_cv_path_chmod@%$vi_cv_path_chmod%g
+s%@vi_cv_path_cp@%$vi_cv_path_cp%g
+s%@vi_cv_path_ln@%$vi_cv_path_ln%g
+s%@vi_cv_path_mkdir@%$vi_cv_path_mkdir%g
+s%@vi_cv_path_rm@%$vi_cv_path_rm%g
+s%@vi_cv_path_strip@%$vi_cv_path_strip%g
+s%@CPP@%$CPP%g
+s%@XINCS@%$XINCS%g
+s%@shrpenv@%$shrpenv%g
+s%@vi_cv_perllib@%$vi_cv_perllib%g
+s%@tknvi@%$tknvi%g
+s%@TKLIBS@%$TKLIBS%g
+s%@cobjs@%$cobjs%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@u_char_decl@%$u_char_decl%g
+s%@u_short_decl@%$u_short_decl%g
+s%@u_int_decl@%$u_int_decl%g
+s%@u_long_decl@%$u_long_decl%g
+s%@u_int8_decl@%$u_int8_decl%g
+s%@u_int16_decl@%$u_int16_decl%g
+s%@int16_decl@%$int16_decl%g
+s%@u_int32_decl@%$u_int32_decl%g
+s%@int32_decl@%$int32_decl%g
+
+CEOF
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile port.h:port.h.in
+ pathnames.h:pathnames.h.in recover:recover.in"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust relative srcdir, etc. for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file
+fi; done
+rm -f conftest.subs
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+CONFIG_HEADERS=${CONFIG_HEADERS-"config.h"}
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ cp $ac_given_srcdir/$ac_file_in conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+# Maximum number of lines to put in a single here document.
+ac_max_here_lines=12
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/contrib/nvi/build/configure.in b/contrib/nvi/build/configure.in
new file mode 100644
index 000000000000..cb8e4637d957
--- /dev/null
+++ b/contrib/nvi/build/configure.in
@@ -0,0 +1,725 @@
+dnl @(#)configure.in 8.134 (Berkeley) 10/15/96
+
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(../common/main.c)
+AC_CONFIG_HEADER(config.h)
+
+dnl Configure setup.
+AC_PROG_INSTALL()
+AC_CANONICAL_HOST
+AC_ARG_PROGRAM()
+
+dnl If the user wants a debugging environment, set OPTFLAG now. (Some
+dnl compilers won't mix optimizing and debug flags.)
+AC_MSG_CHECKING(if --enable-debug option specified)
+AC_ARG_ENABLE(debug,
+ [ --enable-debug Build a debugging version.],
+ [vi_cv_debug="yes"], [vi_cv_debug="no"])
+if test "$vi_cv_debug" = yes; then
+ AC_DEFINE(DEBUG)
+ OPTFLAG=${OPTFLAG-"-g"}
+ no_op_OPTFLAG=${no_op_OPTFLAG-"-g"}
+fi
+AC_MSG_RESULT($vi_cv_debug)
+
+dnl This is where we handle stuff that autoconf can't handle.
+dnl XXX
+dnl Don't override anything if it's already set from the environment.
+
+dnl Compiler, preprocessor and load flags.
+dnl AUX: -ZP disables _BSD_SOURCE et al, but enables POSIX at link time.
+dnl LynxOS: We check for gcc 2.x or better, the gcc 1 that was shipped with
+dnl LynxOS historically wasn't good enough.
+AC_SUBST(CPPFLAGS)
+case "$host_os" in
+aix3.2.5) OPTFLAG=${OPTFLAG-"-O"};;
+aix4.1*) CFLAGS=${CFLAGS-"-qstrict"}
+ OPTFLAG=${OPTFLAG-"-O3"};;
+aux*) CPPFLAGS=${CPPFLAGS-"-ZP -D_BSD_SOURCE -D_SYSV_SOURCE -D_AUX_SOURCE"}
+ LDFLAGS=${LDFLAGS-"-ZP"}
+ OPTFLAG=${OPTFLAG-"-O"};;
+bsd4.4) OPTFLAG=${OPTFLAG-"-O2"};;
+bsdi*) CC=${CC-"shlicc"}
+ OPTFLAG=${OPTFLAG-"-O2"};;
+irix6*) OPTFLAG=${OPTFLAG-"-O2"};;
+irix*) OPTFLAG=${OPTFLAG-"-O2"};;
+lynxos*) AC_PROG_CC()
+ AC_MSG_CHECKING([for GNU C (gcc) version 2.x])
+ ac_cv_gcc_vers=`${CC-cc} -v 2>&1 | \
+ grep "gcc version " | sed 's/.*version //'`
+ ac_cv_gcc_major=`echo "$ac_cv_gcc_vers" | sed 's/\..*//'`
+ if test "$ac_cv_gcc_major" = "2" ; then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ echo "Fatal error: Nvi requires gcc 2.x to build on LynxOS."
+ echo "See build/README.LynxOS for more information."
+ exit 1
+ fi;;
+nextstep3) CPPFLAGS=${CPPFLAGS-"-w -pipe -posix"}
+ LDFLAGS=${LDFLAGS-"-posix"}
+ OPTFLAG=${OPTFLAG-"-O9"};;
+osf*) CFLAGS=${CFLAGS-"-Olimit 1000"};;
+solaris*) no_op_OPTFLAG=${no_op_OPTFLAG-""};;
+sunos*) no_op_OPTFLAG=${no_op_OPTFLAG-""};;
+esac
+
+dnl The default compiler is cc.
+AC_SUBST(CC)
+CC=${CC-cc}
+
+dnl The default OPTFLAG is -O
+AC_SUBST(OPTFLAG)
+OPTFLAG=${OPTFLAG-"-O"}
+
+dnl The SunOS/Solaris compiler can't optimize vi/v_txt.c; the symptom is
+dnl that the command 35i==<esc> turns into an infinite loop.
+AC_SUBST(no_op_OPTFLAG)
+no_op_OPTFLAG=${no_op_OPTFLAG-"$OPTFLAG"}
+
+dnl Libraries.
+case "$host_os" in
+bsdi2.1) LIBS=${LIBS-"-lipc"};;
+dgux*) LIBS=${LIBS-"-ldgc"};;
+irix6*) LIBS=${LIBS-"-lbsd"};;
+irix*) LIBS=${LIBS-"-lc_s -lbsd"};;
+isc*) LIBS=${LIBS-"-lcposix -linet"};;
+netbsd1*) LIBS=${LIBS-"-lcrypt"};;
+ptx*) LIBS=${LIBS-"-lseq -linet -lsocket"};;
+sco3.2*) LIBS=${LIBS-"-lsocket"};;
+sinix*) LIBS=${LIBS-"-lelf -lc"};;
+solaris*) LIBS=${LIBS-"-lsocket -lnsl -ldl"}
+ RLIBS=yes;;
+wgs*) LIBS=${LIBS-"-lnsl"};;
+esac
+
+dnl A/UX has a broken getopt(3), strpbrk(3).
+case "$host_os" in
+aux*) LIBOBJS="getopt.o strpbrk.o $LIBOBJS";;
+esac
+
+dnl Ultrix has a broken POSIX.1 VDISABLE value.
+case "$host_os" in
+ultrix*) AC_DEFINE(HAVE_BROKEN_VDISABLE);;
+esac
+
+dnl The user may have additional CPP information.
+CPPFLAGS="$ADDCPPFLAGS $CPPFLAGS"
+
+dnl The user may have additional load line information.
+LDFLAGS="$ADDLDFLAGS $LDFLAGS"
+
+dnl The user may have additional library information.
+LIBS="$ADDLIBS $LIBS"
+
+dnl Check to see if it's going to work.
+AM_SANITY_CHECK_CC
+
+dnl Checks for programs.
+PATH="$PATH:/usr/bin:/usr/sbin:/sbin:/etc:/usr/etc:/usr/lib:/usr/ucblib:"
+
+dnl Check for the shell path.
+AC_PATH_PROG(vi_cv_path_shell, sh, no)
+if test "$vi_cv_path_shell" = no; then
+ echo "Fatal error: the shell utility not found."
+ exit 1
+fi
+
+dnl Check for the sendmail path.
+AC_PATH_PROG(vi_cv_path_sendmail, sendmail, no)
+if test "$vi_cv_path_sendmail" = no; then
+ echo "WARNING: The sendmail utility was not found!"
+ echo "WARNING: Users will not be told of saved files."
+fi
+
+dnl Check for the perl5/perl path.
+AC_SUBST(vi_cv_path_perl)
+AC_PATH_PROGS(vi_cv_path_perl, perl5 perl, no)
+
+dnl Check for the "preserve" path.
+dnl Historically, nvi has used /var/tmp/vi.recover. The Linux filesystem
+dnl standard (FSSTND) uses /var/preserve; we add the vi.recover directory
+dnl beneath it so that we don't have name collisions with other editors.
+dnl Other systems have /var/preserve as well, so we test first for an already
+dnl existing name, and then use the first one that's writeable.
+AC_SUBST(vi_cv_path_preserve)
+AC_MSG_CHECKING(for preserve directory)
+AC_CACHE_VAL(vi_cv_path_preserve, [dnl
+ dirlist="/var/preserve /var/tmp /usr/tmp"
+ vi_cv_path_preserve=no
+ for i in $dirlist; do
+ if test -d $i/vi.recover; then
+ vi_cv_path_preserve=$i/vi.recover
+ break;
+ fi
+ done
+ if test "$vi_cv_path_preserve" = no; then
+ for i in $dirlist; do
+ if test -d $i -a -w $i; then
+ vi_cv_path_preserve=$i/vi.recover
+ break;
+ fi
+ done
+
+ fi])
+if test "$vi_cv_path_preserve" = no; then
+ echo "Fatal error: no writeable preserve directory found."
+ exit 1
+fi
+AC_MSG_RESULT($vi_cv_path_preserve)
+
+dnl Check for programs used for installation
+AC_PATH_PROG(vi_cv_path_chmod, chmod, missing_chmod)
+AC_PATH_PROG(vi_cv_path_cp, cp, missing_cp)
+AC_PATH_PROG(vi_cv_path_ln, ln, missing_ln)
+AC_PATH_PROG(vi_cv_path_mkdir, mkdir, missing_mkdir)
+AC_PATH_PROG(vi_cv_path_rm, rm, missing_rm)
+AC_PATH_PROG(vi_cv_path_strip, strip, missing_strip)
+
+dnl Checks for libraries.
+dnl Find the X libraries and includes.
+AC_PATH_X
+AC_SUBST(XINCS)
+if test "$no_x" != yes; then
+ if test "X$x_libraries" != "X"; then
+ if test "X$RLIBS" = "Xyes"; then
+ XLIBS="-R$x_libraries -L$x_libraries $XLIBS"
+ else
+ XLIBS="-L$x_libraries $XLIBS"
+ fi
+ fi
+ XLIBS="$XLIBS -lX11"
+ if test "X$x_includes" != "X"; then
+ XINCS="-I$x_includes"
+ fi
+fi
+
+dnl If the user wants a Perl interpreter in nvi, load it.
+AC_SUBST(shrpenv)
+AC_SUBST(vi_cv_perllib)
+AC_MSG_CHECKING(if --enable-perlinterp option specified)
+AC_ARG_ENABLE(perlinterp,
+ [ --enable-perlinterp Include a Perl interpreter in vi.],
+ [vi_cv_perlinterp="yes"], [vi_cv_perlinterp="no"])
+AC_MSG_RESULT($vi_cv_perlinterp)
+if test "$vi_cv_perlinterp" = "yes"; then
+ if test "$vi_cv_path_perl" = no; then
+ echo "Fatal error: no perl5 utility found."
+ exit 1
+ fi
+ $vi_cv_path_perl -e 'require 5.002' || {
+ echo "Fatal error: perl5 must be version 5.002 or later."
+ exit 1
+ }
+ $vi_cv_path_perl -e 'close(STDERR);require 5.003_01' &&
+ AC_DEFINE(HAVE_PERL_5_003_01)
+
+ eval `$vi_cv_path_perl -V:shrpenv`
+ if test "X$shrpenv" = "XUNKNOWN"; then # pre 5.003_04
+ shrpenv=""
+ fi
+ vi_cv_perllib=`$vi_cv_path_perl -MConfig -e 'print $Config{privlib}'`
+ perlcppflags=`$vi_cv_path_perl -Mlib=$srcdir -MExtUtils::Embed \
+ -e 'ccflags;perl_inc'`
+ if test "X$perlcppflags" != "X"; then
+ CPPFLAGS="$perlcppflags $CPPFLAGS"
+ fi
+ perllibs=`cd $srcdir;$vi_cv_path_perl -MExtUtils::Embed \
+ -e 'ldopts'`
+ if test "X$perllibs" != "X"; then
+ LIBS="$perllibs $LIBS"
+ fi
+ perlldflags=`cd $srcdir;$vi_cv_path_perl -MExtUtils::Embed \
+ -e 'ccdlflags'`
+ if test "X$perlldflags" != "X"; then
+ LDFLAGS="$perlldflags $LDFLAGS"
+ fi
+ LIBOBJS="perl.o perlsfio.o $LIBOBJS"
+ AC_DEFINE(HAVE_PERL_INTERP)
+fi
+
+dnl If the user wants a Tk/Tcl front-end for nvi, build it.
+AC_SUBST(tknvi)
+AC_SUBST(TKLIBS)
+AC_MSG_CHECKING(if --enable-tknvi option specified)
+AC_ARG_ENABLE(tknvi,
+ [ --enable-tknvi Build a Tk/Tcl front-end for vi.],
+ [vi_cv_tknvi="yes"], [vi_cv_tknvi="no"])
+AC_MSG_RESULT($vi_cv_tknvi)
+if test "$vi_cv_tknvi" = "yes"; then
+ tknvi=tknvi
+ TKLIBS="-ltk -ltcl -lm $XLIBS $LIBS"
+fi
+
+dnl If the user wants a Tk/Tcl interpreter in nvi, load it.
+AC_MSG_CHECKING(if --enable-tclinterp option specified)
+AC_ARG_ENABLE(tclinterp,
+ [ --enable-tclinterp Include a Tk/Tcl interpreter in vi.],
+ [vi_cv_tclinterp="yes"], [vi_cv_tclinterp="no"])
+AC_MSG_RESULT($vi_cv_tclinterp)
+if test "$vi_cv_tclinterp" = "yes"; then
+ LIBOBJS="tcl.o $LIBOBJS"
+ LIBS="-ltk -ltcl -lm $XLIBS $LIBS"
+ AC_DEFINE(HAVE_TCL_INTERP)
+fi
+
+dnl Make sure that we can find a Tk/Tcl library.
+if test "$vi_cv_tknvi" = "yes" || test "$vi_cv_tclinterp" = "yes"; then
+ AC_CHECK_LIB(tcl, main,
+ [vi_cv_tkfatal="no"], [vi_cv_tkfatal="yes"], -ltk -lm)
+ if test "$vi_cv_tkfatal" = "yes"; then
+ echo "Fatal error: no Tk/Tcl library; see the section"
+ echo "ADDING LIBRARIES AND INCLUDE FILES in the README file."
+ exit 1
+ fi
+fi
+
+dnl Both Tcl/Tk and Perl interpreters need the vi api code.
+if test "$vi_cv_tclinterp" = yes || test "$vi_cv_perlinterp" = yes; then
+ LIBOBJS="api.o $LIBOBJS"
+fi
+
+dnl Check for the termcap/termlib library. Compile in nvi's curses routines
+dnl unless the user specifies otherwise. These two checks must occur in the
+dnl current order, and -lcurses must be loaded before -ltermcap/-ltermlib.
+AC_CHECK_LIB(termlib, tgetent,
+ [vi_cv_termlib=-ltermlib], [vi_cv_termlib=no])
+if test "$vi_cv_termlib" = no; then
+ AC_CHECK_LIB(termcap, tgetent,
+ [vi_cv_termlib=-ltermcap], [vi_cv_termlib=no])
+fi
+if test "$vi_cv_termlib" != no; then
+ LIBS="$vi_cv_termlib $LIBS"
+fi
+AC_SUBST(cobjs)
+AC_MSG_CHECKING(if --disable-curses option specified)
+AC_ARG_ENABLE(curses,
+ [ --disable-curses DON'T use the nvi-provided curses routines.],
+ [vi_cv_curses="other curses"], [vi_cv_curses="bundled curses"])
+AC_MSG_RESULT($vi_cv_curses)
+case "$vi_cv_curses" in
+"bundled curses")
+ CPPFLAGS="-I\$(srcdir)/curses $CPPFLAGS"
+ cobjs="\$(COBJS)";;
+"other curses")
+ LIBS="-lcurses $LIBS";;
+esac
+
+dnl Checks for header files.
+AC_MSG_CHECKING(for sys/mman.h)
+AC_CACHE_VAL(vi_cv_include_sys_mman, [dnl
+AC_TRY_CPP([#include <sys/mman.h>],
+ [vi_cv_include_sys_mman=yes], [vi_cv_include_sys_mman=no])])
+if test "$vi_cv_include_sys_mman" = yes; then
+ AC_DEFINE(HAVE_SYS_MMAN_H)
+fi
+AC_MSG_RESULT($vi_cv_include_sys_mman)
+
+AC_MSG_CHECKING(for sys/select.h)
+AC_CACHE_VAL(vi_cv_include_sys_select, [dnl
+AC_TRY_CPP([#include <sys/select.h>],
+ [vi_cv_include_sys_select=yes], [vi_cv_include_sys_select=no])])
+if test "$vi_cv_include_sys_select" = yes; then
+ AC_DEFINE(HAVE_SYS_SELECT_H)
+fi
+AC_MSG_RESULT($vi_cv_include_sys_select)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_CHECK_TYPE(ssize_t, int)
+AC_C_BIGENDIAN
+AC_C_CONST
+AC_STRUCT_ST_BLKSIZE
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+
+dnl Checks for library functions.
+ AC_CHECK_FUNCS(bsearch gethostname getopt memchr memcpy memmove memset)
+AC_REPLACE_FUNCS(bsearch gethostname getopt memchr memcpy memmove memset)
+ AC_CHECK_FUNCS(mkstemp mmap snprintf strdup strerror strpbrk strtol)
+AC_REPLACE_FUNCS(mkstemp mmap snprintf strdup strerror strpbrk strtol)
+ AC_CHECK_FUNCS(strtoul vsnprintf)
+AC_REPLACE_FUNCS(strtoul vsnprintf)
+
+AC_CHECK_FUNCS(select)
+AC_CHECK_FUNCS(setenv, [need_env=no], [need_env=yes])
+AC_CHECK_FUNCS(strsep, [need_strsep=no], [need_strsep=yes])
+AC_CHECK_FUNCS(unsetenv,, [need_env=yes])
+
+AC_FUNC_MMAP
+AC_FUNC_VFORK
+
+dnl If we needed setenv or unsetenv, add in the clib/env.c replacement file.
+if test "$need_env" = yes; then
+ LIBOBJS="env.o $LIBOBJS"
+fi
+
+dnl If we need strsep, add it and define it so we get a prototype.
+if test "$need_strsep" = yes; then
+ LIBOBJS="strsep.o $LIBOBJS"
+fi
+
+dnl Check for fcntl/flock
+dnl Use flock preferentially, since it has cleaner semantics and won't
+dnl hang up the editor.
+dnl XXX
+dnl Ultrix has a broken fcntl, but a working flock.
+dnl IRIX and DGUX have a broken flock, but working fcntl.
+AC_MSG_CHECKING(for fcntl/flock)
+AC_CACHE_VAL(vi_cv_lock, [dnl
+ vi_cv_lock=none
+ case "$host_os" in
+ dgux*);;
+ irix*);;
+ *)
+ AC_TRY_LINK([#include <fcntl.h>], [flock(0, 0);],
+ [vi_cv_lock=flock]);;
+ esac
+ if test "$vi_cv_lock" = none; then
+ AC_TRY_LINK([#include <fcntl.h>], [fcntl(0, F_SETLK, 0);],
+ [vi_cv_lock=fcntl])
+ fi])
+
+if test "$vi_cv_lock" = flock; then
+ AC_DEFINE(HAVE_LOCK_FLOCK)
+fi
+if test "$vi_cv_lock" = fcntl; then
+ AC_DEFINE(HAVE_LOCK_FCNTL)
+fi
+AC_MSG_RESULT($vi_cv_lock)
+
+dnl Check for ftruncate/chsize
+AC_MSG_CHECKING(for ftruncate/chsize)
+AC_CACHE_VAL(vi_cv_ftruncate, [dnl
+AC_TRY_LINK([#include <unistd.h>], [ftruncate(0, 0);],
+ [vi_cv_ftruncate=ftruncate],
+AC_TRY_LINK([#include <unistd.h>], [chsize(0, 0);],
+ [vi_cv_ftruncate=chsize], [vi_cv_ftruncate=no]))])
+if test "$vi_cv_ftruncate" = ftruncate; then
+ AC_DEFINE(HAVE_FTRUNCATE_FTRUNCATE)
+fi
+if test "$vi_cv_ftruncate" = chsize; then
+ AC_DEFINE(HAVE_FTRUNCATE_CHSIZE)
+fi
+if test "$vi_cv_ftruncate" = no; then
+ echo
+ echo "Fatal error: no file truncation system call."
+ exit 1
+fi
+AC_MSG_RESULT($vi_cv_ftruncate)
+
+dnl Check for the tigetstr/tigetnum functions.
+AC_MSG_CHECKING(for tigetstr/tigetnum)
+AC_CACHE_VAL(vi_cv_have_curses_tigetstr, [dnl
+AC_TRY_LINK([#include <curses.h>], [tigetstr(0);],
+ [vi_cv_have_curses_tigetstr=yes],
+ [vi_cv_have_curses_tigetstr=no])])
+if test "$vi_cv_have_curses_tigetstr" = yes; then
+ AC_DEFINE(HAVE_CURSES_TIGETSTR)
+fi
+AC_MSG_RESULT($vi_cv_have_curses_tigetstr)
+
+dnl Check for potentially missing curses functions in system or user-specified
+dnl libraries. We also have to guess at whether the specified library is a
+dnl BSD or System V style curses. Use the newterm function, all System V
+dnl curses implementations have it, none, as far as I know, of the BSD ones do.
+if test "$vi_cv_curses" = "bundled curses"; then
+ AC_DEFINE(HAVE_BSD_CURSES)
+ AC_DEFINE(HAVE_CURSES_ADDNSTR)
+ AC_DEFINE(HAVE_CURSES_IDLOK)
+else
+ dnl Check for the addnstr function.
+ AC_MSG_CHECKING(for addnstr)
+ AC_CACHE_VAL(vi_cv_have_curses_addnstr, [dnl
+ AC_TRY_LINK([#include <curses.h>], [addnstr(0, 0);],
+ [vi_cv_have_curses_addnstr=yes],
+ [vi_cv_have_curses_addnstr=no])])
+ if test "$vi_cv_have_curses_addnstr" = yes; then
+ AC_DEFINE(HAVE_CURSES_ADDNSTR)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_addnstr)
+
+ dnl Check for the beep function.
+ AC_MSG_CHECKING(for beep)
+ AC_CACHE_VAL(vi_cv_have_curses_beep, [dnl
+ AC_TRY_LINK([#include <curses.h>], [beep();],
+ [vi_cv_have_curses_beep=yes],
+ [vi_cv_have_curses_beep=no])])
+ if test "$vi_cv_have_curses_beep" = yes; then
+ AC_DEFINE(HAVE_CURSES_BEEP)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_beep)
+
+ dnl Check for the flash function.
+ AC_MSG_CHECKING(for flash)
+ AC_CACHE_VAL(vi_cv_have_curses_flash, [dnl
+ AC_TRY_LINK([#include <curses.h>], [flash();],
+ [vi_cv_have_curses_flash=yes],
+ [vi_cv_have_curses_flash=no])])
+ if test "$vi_cv_have_curses_flash" = yes; then
+ AC_DEFINE(HAVE_CURSES_FLASH)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_flash)
+
+ dnl Check for the idlok function.
+ AC_MSG_CHECKING(for idlok)
+ AC_CACHE_VAL(vi_cv_have_curses_idlok, [dnl
+ AC_TRY_LINK([#include <curses.h>], [idlok(0, 0);],
+ [vi_cv_have_curses_idlok=yes],
+ [vi_cv_have_curses_idlok=no])])
+ if test "$vi_cv_have_curses_idlok" = yes; then
+ AC_DEFINE(HAVE_CURSES_IDLOK)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_idlok)
+
+ dnl Check for the keypad function.
+ AC_MSG_CHECKING(for keypad)
+ AC_CACHE_VAL(vi_cv_have_curses_keypad, [dnl
+ AC_TRY_LINK([#include <curses.h>], [keypad(0, 0);],
+ [vi_cv_have_curses_keypad=yes],
+ [vi_cv_have_curses_keypad=no])])
+ if test "$vi_cv_have_curses_keypad" = yes; then
+ AC_DEFINE(HAVE_CURSES_KEYPAD)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_keypad)
+
+ dnl Check for the newterm function.
+ AC_MSG_CHECKING(for newterm)
+ AC_CACHE_VAL(vi_cv_have_curses_newterm, [dnl
+ AC_TRY_LINK([#include <curses.h>], [newterm(0, 0, 0);],
+ [vi_cv_have_curses_newterm=yes],
+ [vi_cv_have_curses_newterm=no])])
+ if test "$vi_cv_have_curses_newterm" = yes; then
+ AC_DEFINE(HAVE_CURSES_NEWTERM)
+ fi
+ AC_MSG_RESULT($vi_cv_have_curses_newterm)
+
+ if test "$vi_cv_have_curses_newterm" = no; then
+ AC_DEFINE(HAVE_BSD_CURSES)
+ fi
+fi
+
+dnl Check for the setupterm function. We make this check regardless of
+dnl using the system library, because it may be part of the underlying
+dnl termcap/termlib support, and we want to use the local one.
+AC_MSG_CHECKING(for setupterm)
+AC_CACHE_VAL(vi_cv_have_curses_setupterm, [dnl
+AC_TRY_LINK([#include <curses.h>], [setupterm(0, 0, 0);],
+ [vi_cv_have_curses_setupterm=yes],
+ [vi_cv_have_curses_setupterm=no])])
+if test "$vi_cv_have_curses_setupterm" = yes; then
+ AC_DEFINE(HAVE_CURSES_SETUPTERM)
+fi
+AC_MSG_RESULT($vi_cv_have_curses_setupterm)
+
+dnl Some moron decided to drop off an argument from the gettimeofday call,
+dnl without changing the name.
+AC_MSG_CHECKING(for broken gettimeofday system call)
+AC_CACHE_VAL(vi_cv_gettimeofday, [dnl
+AC_TRY_LINK([#include <sys/types.h>
+#include <sys/time.h>], [gettimeofday(0, 0);],
+ [vi_cv_gettimeofday=okay], [vi_cv_gettimeofday=broken])])
+if test "$vi_cv_gettimeofday" = broken; then
+ AC_DEFINE(HAVE_BROKEN_GETTIMEOFDAY)
+fi
+AC_MSG_RESULT($vi_cv_gettimeofday)
+
+dnl Check for which version of openpty to use, System V or Berkeley.
+AC_MSG_CHECKING(for System V pty calls)
+AC_CACHE_VAL(vi_cv_sys5_pty, [dnl
+AC_TRY_LINK(, [grantpt(0);],
+ [vi_cv_sys5_pty=yes], [vi_cv_sys5_pty=no])])
+if test "$vi_cv_sys5_pty" = yes; then
+ AC_DEFINE(HAVE_SYS5_PTY)
+fi
+AC_MSG_RESULT($vi_cv_sys5_pty)
+
+dnl Check for the revoke system call.
+AC_MSG_CHECKING(for revoke system call)
+AC_CACHE_VAL(vi_cv_revoke, [dnl
+AC_TRY_LINK(, [revoke("a");],
+ [vi_cv_revoke=yes], [vi_cv_revoke=no])])
+if test "$vi_cv_revoke" = yes; then
+ AC_DEFINE(HAVE_REVOKE)
+fi
+AC_MSG_RESULT($vi_cv_revoke)
+
+dnl Some versions of sprintf return a pointer to the first argument instead
+dnl of a character count. We assume that the return value of snprintf and
+dnl vsprintf etc. will be the same as sprintf, and check the easy one.
+AC_MSG_CHECKING(for int type sprintf return value)
+AC_CACHE_VAL(vi_cv_sprintf_count, [dnl
+AC_TRY_RUN([main(){char buf[20]; exit(sprintf(buf, "XXX") != 3);}],
+ [vi_cv_sprintf_count=yes], [vi_cv_sprintf_count=no])])
+if test "$vi_cv_sprintf_count" = no; then
+ AC_DEFINE(SPRINTF_RET_CHARPNT)
+fi
+AC_MSG_RESULT($vi_cv_sprintf_count)
+
+dnl We compile in nvi's DB routines unless the user specifies otherwise.
+AC_MSG_CHECKING(if --disable-db option specified)
+AC_ARG_ENABLE(db,
+ [ --disable-db DON'T use the nvi-provided DB routines.],
+ [vi_cv_db_lib="other DB"], [vi_cv_db_lib="bundled DB"])
+AC_MSG_RESULT($vi_cv_db_lib)
+case "$vi_cv_db_lib" in
+"bundled DB")
+ CPPFLAGS="-I\$(srcdir)/db/include $CPPFLAGS"
+ LIBOBJS="\$(DBOBJS) $LIBOBJS";;
+"other DB")
+ ;;
+esac
+
+dnl We compile in nvi's RE routines unless the user specifies otherwise.
+AC_MSG_CHECKING(if --disable-re option specified)
+AC_ARG_ENABLE(re,
+ [ --disable-re DON'T use the nvi-provided RE routines.],
+ [vi_cv_re_lib="other RE"], [vi_cv_re_lib="bundled RE"])
+AC_MSG_RESULT($vi_cv_re_lib)
+case "$vi_cv_re_lib" in
+"bundled RE")
+ CPPFLAGS="-I\$(srcdir)/regex $CPPFLAGS"
+ LIBOBJS="\$(REOBJS) $LIBOBJS";;
+"other RE")
+ ;;
+esac
+
+dnl Check for the standard shorthand types.
+AC_SUBST(u_char_decl)
+AC_MSG_CHECKING(for u_char)
+AC_CACHE_VAL(vi_cv_uchar, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_char foo;,
+ [vi_cv_uchar=yes], [vi_cv_uchar=no])])
+AC_MSG_RESULT($vi_cv_uchar)
+if test "$vi_cv_uchar" = no; then
+ u_char_decl="typedef unsigned char u_char;"
+fi
+
+AC_SUBST(u_short_decl)
+AC_MSG_CHECKING(for u_short)
+AC_CACHE_VAL(vi_cv_ushort, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_short foo;,
+ [vi_cv_ushort=yes], [vi_cv_ushort=no])])
+AC_MSG_RESULT($vi_cv_ushort)
+if test "$vi_cv_ushort" = no; then
+ u_short_decl="typedef unsigned short u_short;"
+fi
+
+AC_SUBST(u_int_decl)
+AC_MSG_CHECKING(for u_int)
+AC_CACHE_VAL(vi_cv_uint, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_int foo;,
+ [vi_cv_uint=yes], [vi_cv_uint=no])])
+AC_MSG_RESULT($vi_cv_uint)
+if test "$vi_cv_uint" = no; then
+ u_int_decl="typedef unsigned int u_int;"
+fi
+
+AC_SUBST(u_long_decl)
+AC_MSG_CHECKING(for u_long)
+AC_CACHE_VAL(vi_cv_ulong, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_long foo;,
+ [vi_cv_ulong=yes], [vi_cv_ulong=no])])
+AC_MSG_RESULT($vi_cv_ulong)
+if test "$vi_cv_ulong" = no; then
+ u_long_decl="typedef unsigned long u_long;"
+fi
+
+dnl DB/Vi use specific integer sizes.
+AC_SUBST(u_int8_decl)
+AC_MSG_CHECKING(for u_int8_t)
+AC_CACHE_VAL(vi_cv_uint8, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_int8_t foo;,
+ [vi_cv_uint8=yes],
+AC_TRY_RUN([main(){exit(sizeof(unsigned char) != 1);}],
+ [vi_cv_uint8="unsigned char"], [vi_cv_uint8=no]))])
+AC_MSG_RESULT($vi_cv_uint8)
+if test "$vi_cv_uint8" = no; then
+ echo
+ echo "Fatal error: no unsigned, 8-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint8" != yes; then
+ u_int8_decl="typedef $vi_cv_uint8 u_int8_t;"
+fi
+
+AC_SUBST(u_int16_decl)
+AC_MSG_CHECKING(for u_int16_t)
+AC_CACHE_VAL(vi_cv_uint16, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_int16_t foo;,
+ [vi_cv_uint16=yes],
+AC_TRY_RUN([main(){exit(sizeof(unsigned short) != 2);}],
+ [vi_cv_uint16="unsigned short"],
+AC_TRY_RUN([main(){exit(sizeof(unsigned int) != 2);}],
+ [vi_cv_uint16="unsigned int"], [vi_cv_uint16=no])))])
+AC_MSG_RESULT($vi_cv_uint16)
+if test "$vi_cv_uint16" = no; then
+ echo
+ echo "Fatal error: no unsigned, 16-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint16" != yes; then
+ u_int16_decl="typedef $vi_cv_uint16 u_int16_t;"
+fi
+
+AC_SUBST(int16_decl)
+AC_MSG_CHECKING(for int16_t)
+AC_CACHE_VAL(vi_cv_int16, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], int16_t foo;,
+ [vi_cv_int16=yes],
+AC_TRY_RUN([main(){exit(sizeof(short) != 2);}],
+ [vi_cv_int16="short"],
+AC_TRY_RUN([main(){exit(sizeof(int) != 2);}],
+ [vi_cv_int16="int"], [vi_cv_int16=no])))])
+AC_MSG_RESULT($vi_cv_int16)
+if test "$vi_cv_int16" = no; then
+ echo
+ echo "Fatal error: no signed, 16-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_int16" != yes; then
+ int16_decl="typedef $vi_cv_int16 int16_t;"
+fi
+
+AC_SUBST(u_int32_decl)
+AC_MSG_CHECKING(for u_int32_t)
+AC_CACHE_VAL(vi_cv_uint32, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], u_int32_t foo;,
+ [vi_cv_uint32=yes],
+AC_TRY_RUN([main(){exit(sizeof(unsigned int) != 4);}],
+ [vi_cv_uint32="unsigned int"],
+AC_TRY_RUN([main(){exit(sizeof(unsigned long) != 4);}],
+ [vi_cv_uint32="unsigned long"], [vi_cv_uint32=no])))])
+AC_MSG_RESULT($vi_cv_uint32)
+if test "$vi_cv_uint32" = no; then
+ echo
+ echo "Fatal error: no unsigned, 32-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_uint32" != yes; then
+ u_int32_decl="typedef $vi_cv_uint32 u_int32_t;"
+fi
+
+AC_SUBST(int32_decl)
+AC_MSG_CHECKING(for int32_t)
+AC_CACHE_VAL(vi_cv_int32, [dnl
+AC_TRY_COMPILE([#include <sys/types.h>], int32_t foo;,
+ [vi_cv_int32=yes],
+AC_TRY_RUN([main(){exit(sizeof(int) != 4);}],
+ [vi_cv_int32="int"],
+AC_TRY_RUN([main(){exit(sizeof(long) != 4);}],
+ [vi_cv_int32="long"], [vi_cv_int32=no])))])
+AC_MSG_RESULT($vi_cv_int32)
+if test "$vi_cv_int32" = no; then
+ echo
+ echo "Fatal error: no signed, 32-bit integral type."
+ exit 1
+fi
+if test "$vi_cv_int32" != yes; then
+ int32_decl="typedef $vi_cv_int32 int32_t;"
+fi
+
+AC_OUTPUT(Makefile port.h:port.h.in
+ pathnames.h:pathnames.h.in recover:recover.in)
diff --git a/contrib/nvi/build/distrib b/contrib/nvi/build/distrib
new file mode 100644
index 000000000000..45dbe52f4049
--- /dev/null
+++ b/contrib/nvi/build/distrib
@@ -0,0 +1,84 @@
+#! /bin/sh
+# @(#)distrib 8.11 (Berkeley) 10/23/96
+
+# Clean
+#make -f Makefile.in clean
+#rm -f configure config.h.in
+
+# Build autoconf structure.
+echo "Running autoheader"
+autoheader 2>&1 | sed '/warning: AC_TRY_RUN called without default/d'
+chmod 444 config.h.in
+echo "Running autoconf"
+autoconf 2>&1 | sed '/warning: AC_TRY_RUN called without default/d'
+chmod 555 configure config.guess config.sub install-sh
+
+# Build include files.
+f=../include/cl_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../cl/*.c > $f
+chmod 444 $f
+
+f=../include/com_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../clib/*.c ../common/*.c > $f
+chmod 444 $f
+
+f=../include/ex_def.h
+echo "Building $f"
+rm -f $f
+awk -f ../ex/ex.awk ../ex/ex_cmd.c > $f
+chmod 444 $f
+
+f=../include/ex_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../ex/*.c > $f
+chmod 444 $f
+
+if [ -d ../ip ]; then
+ f=../include/ip_extern.h
+ echo "Building $f"
+ rm -f $f
+ sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../ip/*.c > $f
+ chmod 444 $f
+fi
+
+f=../include/options_def.h
+echo "Building $f"
+rm -f $f
+awk -f ../common/options.awk ../common/options.c > $f
+chmod 444 $f
+
+f=../include/perl_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../perl_api/*.xs ../perl_api/*.c > $f
+chmod 444 $f
+
+f=../include/tcl_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../tcl_api/*.c > $f
+chmod 444 $f
+
+f=../include/tk_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../tk/*.c > $f
+chmod 444 $f
+
+f=../include/vi_extern.h
+echo "Building $f"
+rm -f $f
+sed -n "s/^ \* PUBLIC: \(.*\)/\1/p" ../vi/*.c > $f
+chmod 444 $f
+
+# Build tags files.
+echo "Building tags files"
+rm -f tags
+ctags -w -d ../cl/*.[ch] ../common/*.[ch] ../ex/*.[ch] ../perl_api/*.[ch] \
+ ../tcl_api/*.[ch] ../tk/*.[ch] ../vi/*.[ch]
+chmod 444 tags
diff --git a/contrib/nvi/build/install-sh b/contrib/nvi/build/install-sh
new file mode 100755
index 000000000000..ab74c882e923
--- /dev/null
+++ b/contrib/nvi/build/install-sh
@@ -0,0 +1,238 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/contrib/nvi/build/pathnames.h.in b/contrib/nvi/build/pathnames.h.in
new file mode 100644
index 000000000000..09cf97460ae8
--- /dev/null
+++ b/contrib/nvi/build/pathnames.h.in
@@ -0,0 +1,45 @@
+/* @(#)pathnames.h.in 8.4 (Berkeley) 6/26/96 */
+
+#ifndef _PATH_BSHELL
+#define _PATH_BSHELL "@vi_cv_path_shell@"
+#endif
+
+#ifndef _PATH_EXRC
+#define _PATH_EXRC ".exrc"
+#endif
+
+#ifndef _PATH_MSGCAT
+#define _PATH_MSGCAT "./"
+#endif
+
+#ifndef _PATH_NEXRC
+#define _PATH_NEXRC ".nexrc"
+#endif
+
+#ifndef _PATH_PRESERVE
+#define _PATH_PRESERVE "@vi_cv_path_preserve@"
+#endif
+
+#ifndef _PATH_SYSV_PTY
+#define _PATH_SYSV_PTY "/dev/ptmx"
+#endif
+
+#ifndef _PATH_SENDMAIL
+#define _PATH_SENDMAIL "@vi_cv_path_sendmail@"
+#endif
+
+#ifndef _PATH_SYSEXRC
+#define _PATH_SYSEXRC "/etc/vi.exrc"
+#endif
+
+#ifndef _PATH_TAGS
+#define _PATH_TAGS "tags"
+#endif
+
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp"
+#endif
+
+#ifndef _PATH_TTY
+#define _PATH_TTY "/dev/tty"
+#endif
diff --git a/contrib/nvi/build/port.h.in b/contrib/nvi/build/port.h.in
new file mode 100644
index 000000000000..6696848ecc7d
--- /dev/null
+++ b/contrib/nvi/build/port.h.in
@@ -0,0 +1,185 @@
+/* @(#)port.h.in 8.13 (Berkeley) 6/12/96 */
+
+/*
+ * Declare the basic types, if they aren't already declared. Named and
+ * some system's db.h files protect them with __BIT_TYPES_DEFINED__.
+ */
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+@u_int8_decl@
+@int16_decl@
+@u_int16_decl@
+@int32_decl@
+@u_int32_decl@
+#endif
+
+@u_char_decl@
+@u_short_decl@
+@u_int_decl@
+@u_long_decl@
+
+/*
+ * XXX
+ * Handle function prototypes. This steps on name space that vi doesn't
+ * control, but all of the other solutions are worse.
+ */
+#undef __P
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* ANSI C prototypes */
+#else
+#define __P(protos) () /* K&R C preprocessor */
+#endif
+
+/*
+ * XXX
+ * Some versions of System V changed the number of arguments to gettimeofday
+ * without changing the name.
+ */
+#ifdef HAVE_BROKEN_GETTIMEOFDAY
+#define gettimeofday(tv, tz) gettimeofday(tv)
+#endif
+
+/*
+ * XXX
+ * If we don't have mmap, we fake it with read and write, but we'll
+ * still need the header information.
+ */
+#ifndef HAVE_SYS_MMAN_H
+#define MAP_SHARED 1 /* share changes */
+#define MAP_PRIVATE 2 /* changes are private */
+#define PROT_READ 0x1 /* pages can be read */
+#define PROT_WRITE 0x2 /* pages can be written */
+#define PROT_EXEC 0x4 /* pages can be executed */
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 names for file descriptors.
+ */
+#ifndef STDERR_FILENO
+#define STDIN_FILENO 0 /* ANSI C #defines */
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 names for seek settings.
+ */
+#ifndef SEEK_END
+#define SEEK_SET 0 /* POSIX 1003.1 seek values */
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+/*
+ * Hack _POSIX_VDISABLE to \377 since Ultrix doesn't honor _POSIX_VDISABLE
+ * (treats it as ^@). The symptom is that the ^@ keystroke immediately
+ * drops core.
+ */
+#ifdef HAVE_BROKEN_VDISABLE
+#undef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ((unsigned char)'\377')
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 tty disabling character.
+ */
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE 0 /* Some systems used 0. */
+#endif
+
+/*
+ * XXX
+ * 4.4BSD extension to only set the software termios bits.
+ */
+#ifndef TCSASOFT /* 4.4BSD extension. */
+#define TCSASOFT 0
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 maximum path length.
+ */
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+/*
+ * XXX
+ * MIN, MAX, historically in <sys/param.h>
+ */
+#ifndef MAX
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+/*
+ * XXX
+ * "DB" isn't always portable, and we want the private information.
+ */
+#define DB L__DB
+#undef pgno_t /* IRIX has its own version. */
+#define pgno_t L__db_pgno_t
+
+/*
+ * XXX
+ * 4.4BSD extension to provide lock values in the open(2) call.
+ */
+#ifndef O_EXLOCK
+#define O_EXLOCK 0
+#endif
+
+#ifndef O_SHLOCK
+#define O_SHLOCK 0
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 bad file format errno.
+ */
+#ifndef EFTYPE
+#define EFTYPE EINVAL
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.2 RE length limit.
+ */
+#ifndef _POSIX2_RE_DUP_MAX
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+
+/*
+ * XXX
+ * 4.4BSD extension to determine if a program dropped core from the exit
+ * status.
+ */
+#ifndef WCOREDUMP
+#define WCOREDUMP(a) 0
+#endif
+
+/*
+ * XXX
+ * Endian-ness of the machine.
+ */
+#if !defined(LITTLE_ENDIAN)
+#define LITTLE_ENDIAN 1234
+#endif
+#if !defined(BIG_ENDIAN)
+#define BIG_ENDIAN 4321
+#endif
+#if !defined(BYTE_ORDER)
+#if WORDS_BIGENDIAN == 1
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
diff --git a/contrib/nvi/build/recover.in b/contrib/nvi/build/recover.in
new file mode 100644
index 000000000000..cfaf75f2c287
--- /dev/null
+++ b/contrib/nvi/build/recover.in
@@ -0,0 +1,49 @@
+#!/bin/sh -
+#
+# @(#)recover.in 8.8 (Berkeley) 10/10/96
+#
+# Script to recover nvi edit sessions.
+
+RECDIR="@vi_cv_path_preserve@"
+SENDMAIL="@vi_cv_path_sendmail@"
+
+echo 'Recovering nvi editor sessions.'
+
+# Check editor backup files.
+vibackup=`echo $RECDIR/vi.*`
+if [ "$vibackup" != "$RECDIR/vi.*" ]; then
+ for i in $vibackup; do
+ # Only test files that are readable.
+ if test ! -r $i; then
+ continue
+ fi
+
+ # Unmodified nvi editor backup files either have the
+ # execute bit set or are zero length. Delete them.
+ if test -x $i -o ! -s $i; then
+ rm $i
+ fi
+ done
+fi
+
+# It is possible to get incomplete recovery files, if the editor crashes
+# at the right time.
+virecovery=`echo $RECDIR/recover.*`
+if [ "$virecovery" != "$RECDIR/recover.*" ]; then
+ for i in $virecovery; do
+ # Only test files that are readable.
+ if test ! -r $i; then
+ continue
+ fi
+
+ # Delete any recovery files that are zero length, corrupted,
+ # or that have no corresponding backup file. Else send mail
+ # to the user.
+ recfile=`awk '/^X-vi-recover-path:/{print $2}' < $i`
+ if test -n "$recfile" -a -s "$recfile"; then
+ $SENDMAIL -t < $i
+ else
+ rm $i
+ fi
+ done
+fi
diff --git a/contrib/nvi/build/spell.ok b/contrib/nvi/build/spell.ok
new file mode 100644
index 000000000000..fc103f43602d
--- /dev/null
+++ b/contrib/nvi/build/spell.ok
@@ -0,0 +1,58 @@
+ADDCPPFLAGS
+ADDLDFLAGS
+ADDLIBS
+CPPFLAGS
+FreeBSD
+LDFLAGS
+LIBS
+Lite
+NVI
+NVI'S
+NetBSD
+Nvi
+POSIX
+Perl
+README
+Tcl
+Tk
+asnvi
+asvi
+autoconf
+bindir
+cd
+contrib
+csh
+datadir
+datafiles
+db
+distclean
+env
+filesystem
+foo
+gcc
+ksh
+lcurses
+ldb
+lm
+lperl
+ltcl
+ltermcap
+ltermlib
+ltk
+mandir
+mkdir
+ncurses
+nex
+nvi
+nview
+perl
+perlinterp
+setenv
+sh
+tcl
+tclinterp
+tcsh
+terminfo
+tknvi
+usr
+vi
diff --git a/contrib/nvi/catalog/Makefile b/contrib/nvi/catalog/Makefile
new file mode 100644
index 000000000000..1044051ff389
--- /dev/null
+++ b/contrib/nvi/catalog/Makefile
@@ -0,0 +1,84 @@
+# @(#)Makefile 8.29 (Berkeley) 10/19/96
+
+CAT= dutch english french german ru_SU.KOI8-R spanish swedish
+FILES= ../cl/*.c ../common/*.c ../ex/*.c ../tk/*.c ../vi/*.c
+
+all: dump ${CAT}
+
+${CAT}: english.base
+ @echo "... $@"; \
+ rm -f $@; \
+ sort -u $@.base | \
+ awk '{ \
+ if ($$1 == 1) { \
+ print "\nMESSAGE NUMBER 1 IS NOT LEGAL"; \
+ exit 1; \
+ } \
+ if (++nline > $$1) { \
+ print "DUPLICATE MESSAGE NUMBER " $$1; \
+ exit 1; \
+ } \
+ for (; nline < $$1; ++nline) \
+ print ""; \
+ print $0; \
+ }' | \
+ sed -e '1s/^/VI_MESSAGE_CATALOG/' \
+ -e '/"/s/^[^"]*"//' \
+ -e '1!s/"$$/X/' > $@; \
+ chmod 444 $@; \
+ if grep DUPLICATE $@ > /dev/null; then \
+ grep DUPLICATE $@; \
+ fi; \
+ if grep 'NOT LEGAL' $@ > /dev/null; then \
+ grep 'NOT LEGAL' $@; \
+ fi
+
+CHK= dutch.check english.check french.check german.check \
+ ru_SU.KOI8-R.check spanish.check swedish.check
+check: ${CHK}
+${CHK}: ${CAT}
+ @echo "... $@"; \
+ f=`basename $@ .check`; \
+ (echo "Unused message id's (this is okay):"; \
+ awk '{ \
+ while (++nline < $$1) \
+ printf "%03d\n", nline; \
+ }' < $$f.base; \
+ echo =========================; \
+ echo "MISSING ERROR MESSAGES (Please add!):"; \
+ awk '{print $$1}' < $$f.base | sort -u > __ck1; \
+ awk '{print $$1}' < english.base | sort -u > __ck2; \
+ comm -13 __ck1 __ck2; \
+ echo =========================; \
+ echo "Extra error messages (just delete them):"; \
+ comm -23 __ck1 __ck2; \
+ echo =========================; \
+ echo "MESSAGES WITH THE SAME MESSAGE ID's (FIX!):"; \
+ for j in \
+ `sed '/^$$/d' < $$f.base | sort -u | \
+ awk '{print $$1}' | uniq -d`; do \
+ egrep $$j $$f.base; \
+ done; \
+ echo =========================; \
+ echo "Duplicate messages, both id and message (this is okay):"; \
+ sed '/^$$/d' < $$f.base | sort | uniq -c | \
+ awk '$$1 != 1 { print $$0 }' | sort -n; \
+ echo =========================; \
+ echo "Duplicate messages, just message (this is okay):"; \
+ sed '/^$$/d' < $$f | sort | uniq -c | \
+ awk '$$1 != 1 { print $$0 }' | sort -n; \
+ echo =========================) > $@
+
+english.base: dump ${FILES} #Makefile
+ ./dump ${FILES} |\
+ sed -e '/|/!d' \
+ -e 's/|/ "/' \
+ -e 's/^"//' \
+ -e 's/\\"/"/g' |\
+ sort -n > $@
+
+dump: dump.c
+ ${CC} -O -o dump dump.c
+
+clean:
+ rm -f dump dump.o ${CAT} english.base *.check __ck1 __ck2
diff --git a/contrib/nvi/catalog/README b/contrib/nvi/catalog/README
new file mode 100644
index 000000000000..15a706358869
--- /dev/null
+++ b/contrib/nvi/catalog/README
@@ -0,0 +1,166 @@
+# @(#)README 8.4 (Berkeley) 11/22/94
+
+Generally, all non-system error and informational messages in nvi are
+catalog messages, i.e. they can be tailored to a specific langauge.
+Command strings, usage strings, system errors and other "known text"
+are not. It would certainly be possible to internationalize all the
+text strings in nvi, but it's unclear that it's the right thing to do.
+
+First, there's no portable way to do message catalogs. The System V
+scheme is a reasonable choice, but none of the 4BSD derived systems
+support it. So, catalogs are completely implemented within nvi, and
+don't require any library support.
+
+Message catalogs in nvi are fairly simple. Every catalog message
+consists of two parts -- an initial number followed by a pipe (`|')
+character, followed by the English text for the message. For example:
+
+ msgq(sp, M_ERR, "001|This is an error message");
+
+would be a typical message.
+
+When the msgq() routine is called, if the user has specified a message
+catalog and the format string (the third argument) has a leading number,
+then it is converted to a record number, and that record is retrieved
+from the message catalog and used as a replacement format string. If
+the record can't be retrieved for any reason, the English text is displayed
+instead.
+
+Each message format string MUST map into the English format string, i.e.
+it can't display more or different arguments than the English one.
+
+For example:
+
+ msgq(sp, M_ERR, "002|Error: %d %x", arg1, arg2);
+
+is a format string that displays two arguments. It is possible, however,
+to reorder the arguments or to not display all of them. The convention
+nvi uses is the System V printf(3) convention, i.e. "%[0-9]*$" is the name
+of a specific, numbered argument. For example:
+
+ msgq(sp, M_ERR, "002|Error: %2$d %1$x", arg1, arg2);
+
+displays the arguments in reverse order.
+
+If the system supports this convention in its library printf routines
+(as specified by the test #define NL_ARGMAX), nvi uses those routines.
+Otherwise, there is some serious magic going on in common/msg.c to make
+this all work.
+
+Arguments to the msgq function are required to contain ONLY printable
+characters. No further translation is done by the msgq routine before
+displaying the message on the screen. For example, in the msgq call:
+
+ msgq(sp, M_ERR, "003|File: %s", file_name);
+
+"file_name" must contain only printable characters. The routine
+msg_print() returns a printable version of a string in allocated
+memory. For example:
+
+ char *p;
+
+ p = msg_print(sp, file_name);
+ msgq(sp, M_ERR, M("003", "File: %s"), p);
+ FREE_SPACE(sp, p, 0);
+
+makes sure that "file_name" is printable before calling the msgq
+routine.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+The message catalogs themselves are maintained in two files. The first
+is the "base file" which contains two fields, a record number and the
+message itself. All base files are named using the convention
+"vi_<language>.base", e.g. the English one is "vi_english.base". For
+example:
+
+ 002 "Unable to create temporary file"
+ 003 "Warning: %s is not a regular file"
+ 004 "%s already locked, session is read-only"
+ 005 "%s: remove"
+ 006 "%s: close"
+ 007 "%s: remove"
+ 008 "%s: remove"
+ 009 "Read-only file, not written; use ! to override"
+ 010 "Read-only file, not written"
+
+are the first few lines of the current vi_english.base file. Note that
+message #1 is missing -- the first message of each catalog is a special
+one, so that nvi can recognize message catalog files. It's added by the
+Makefile script that creates the second version of the message catalog.
+
+The second file is the file used by nvi to access messages, and is a list
+of the messages, one per line:
+
+ VI_MESSAGE_CATALOG
+ Unable to create temporary fileX
+ Warning: %s is not a regular fileX
+ %s already locked, session is read-onlyX
+ %s: removeX
+ %s: closeX
+ %s: removeX
+ %s: removeX
+ Read-only file, not written; use ! to overrideX
+ Read-only file, not writtenX
+
+Note that all messages have had a trailing 'X' character appended. This
+is to provide nvi a place to store a trailing nul for the message so that
+C library routines that expect one won't be disappointed.
+
+These files are named for their language, e.g. "vi_english". The second
+files are automatically created from the first files.
+
+To create a new catalog for nvi:
+
+Copy the file vi_english.base to a file that you can modify , e.g. "cp
+vi_english.base vi_german.base". For each of the messages in the file,
+replace the message with the string that you want to use. To find out
+what the arguments to a message are, I'm afraid you'll have to search
+the source code for the message number. You can find them fairly quickly
+by doing:
+
+ cd ..; egrep '123\|' */*.[chys]
+
+I'm sorry that there's not an easier way, but I couldn't think of
+anything that wasn't a lot of work.
+
+If, for some reason, you don't have the file vi_english.base, or you
+have new sources for which you want to create a new base catalog, you
+can create it by running the command "make english" in the catalog
+directory.
+
+Once you've translated all of the strings, then add your catalog to the
+"CAT=" line of the Makefile, and run the command "make catalog". This
+will create the second (and corresponding) file for each file named
+<language>.base.
+
+Don't worry about missing line numbers, i.e. base files that look like:
+
+ 005 Message number 5.
+ 007 Message number 7.
+
+This simply means that a message was deleted during the course of nvi's
+development. It will be taken care of automatically when you create
+the second form of the file.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+If you add new messages to the nvi sources, you can check your work by
+doing "make english; make check". The "make check" target lists unused
+message numbers, duplicate message numbers, and duplicate messages.
+Unused message numbers are only useful if you are condensing messages.
+Duplicate message numbers are a serious problem and have to be fixed.
+Duplicate messages are only interesting if a message appears often enough
+that it's worth creating a routine so that the string is only need in
+a single place.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+To select a catalog when running nvi, set the "msgcat" option. If the
+value of this option ends with a '/', it is treated as the name of a
+directory that contains a message catalog "vi_XXXX", where XXXX is the
+value of the LANG environmental variable, if it's set, or the value of
+the LC_MESSAGES environmental variable if it's not. If neither of those
+environmental variables are set, or if the option doesn't end in a '/',
+the option is treated as the full path name of the message catalog to use.
+
+If any messages are missing from the catalog, the backup text (English)
+is used instead.
diff --git a/contrib/nvi/catalog/dump.c b/contrib/nvi/catalog/dump.c
new file mode 100644
index 000000000000..0b3cd26520d5
--- /dev/null
+++ b/contrib/nvi/catalog/dump.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 1993, 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dump.c 8.1 (Berkeley) 8/31/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+
+static void
+parse(fp)
+ FILE *fp;
+{
+ int ch, s1, s2, s3;
+
+#define TESTD(s) { \
+ if ((s = getc(fp)) == EOF) \
+ return; \
+ if (!isdigit(s)) \
+ continue; \
+}
+#define TESTP { \
+ if ((ch = getc(fp)) == EOF) \
+ return; \
+ if (ch != '|') \
+ continue; \
+}
+#define MOVEC(t) { \
+ do { \
+ if ((ch = getc(fp)) == EOF) \
+ return; \
+ } while (ch != (t)); \
+}
+ for (;;) {
+ MOVEC('"');
+ TESTD(s1);
+ TESTD(s2);
+ TESTD(s3);
+ TESTP;
+ putchar('"');
+ putchar(s1);
+ putchar(s2);
+ putchar(s3);
+ putchar('|');
+ for (;;) { /* dump to end quote. */
+ if ((ch = getc(fp)) == EOF)
+ return;
+ putchar(ch);
+ if (ch == '"')
+ break;
+ if (ch == '\\') {
+ if ((ch = getc(fp)) == EOF)
+ return;
+ putchar(ch);
+ }
+ }
+ putchar('\n');
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+
+ for (; *argv != NULL; ++argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ perror(*argv);
+ exit (1);
+ }
+ parse(fp);
+ (void)fclose(fp);
+ }
+ exit (0);
+}
diff --git a/contrib/nvi/catalog/dutch b/contrib/nvi/catalog/dutch
new file mode 100644
index 000000000000..de9fb603bf8e
--- /dev/null
+++ b/contrib/nvi/catalog/dutch
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+regel te langX
+kan regel %lu niet verwijderenX
+kan niet toevoegen aan regel %luX
+kan niet invoegen vooraan regel %luX
+kan regel %lu niet opslaanX
+kan laatste regel niet lezenX
+Fout: kan regel %lu niet vindenX
+log bestandX
+Er vindt geen logging plaats, kan wijzigingen niet ongedaan makenX
+geen wijzigingen om ongedaan te makenX
+Er vindt geen logging plaats, kan wijzigingen niet ongedaan makenX
+Er vindt geen logging plaats, herhaling niet mogelijkX
+geen wijzigingen om te herhalenX
+%s/%d: schrijven naar log misluktX
+Vi's standaard invoer en uitvoer moeten aan een terminal gekoppeld zijnX
+Merk %s: niet gezetX
+Merk %s: de regel is verwijderdX
+Merk %s: de cursor positie bestaat niet meerX
+Fout: X
+nieuw bestandX
+naam veranderdX
+gewijzigdX
+ongewijzigdX
+NIET BEVEILIGDX
+niet schrijfbaarX
+regel %lu uit %lu [%ld%%]X
+leeg bestandX
+regel %luX
+Het bestand %s is geen message catalogX
+Niet in staat om de standaard %s optie in te stellenX
+Gebruik: %sX
+set: optie %s onbekend: 'set all' laat alle opties zienX
+set: [no]%s optie kan geen waarde hebbenX
+set: %s optie moet een waarde hebbenX
+set: %s optie: %sX
+set: %s optie: %s: getal is te grootX
+set: %s optie: %s is een ongeldige waardeX
+set: %s optie moet een waarde hebbenX
+Te weinig kolommen op het scherm, minder dan %dX
+Aantal kolommen te groot, meer dan %dX
+Te weinig regels op het scherm, minder dan %dX
+Aantal regels te groot, meer dan %dX
+De lisp optie is niet ondersteundX
+messages niet uitgeschakeld: %sX
+messages niet geactiveerd: %sX
+
+De paragraph optie moet karakter paren bevattenX
+De section optie moet karakter paren bevattenX
+
+
+
+De standaard buffer is leegX
+Buffer %s is leegX
+Bestanden met newlines in de naam kunnen niet hersteld wordenX
+Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie misluktX
+Bestand wordt gecopieerd voor herstel...X
+Herstel mechanisme werkt niet: %sX
+Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie misluktX
+Kon bestand niet veilig stellen: %sX
+Bestand wordt gecopieerd voor herstel...X
+Informatie met betrekking tot gebruiker nummer %u niet gevondenX
+Kan herstel bestand niet beveiligenX
+herstel buffer overgelopenX
+herstel bestandX
+%s: verminkt herstel bestandX
+%s: verminkt herstel bestandX
+U heeft geen bestand genaamd %s te herstellenX
+U kan eerdere versies van dit bestand herstellenX
+U kan nog meer bestanden herstellenX
+kan geen email versturen: %sX
+Bestand leeg; niets om te doorzoekenX
+Einde van het bestand bereikt zonder dat het patroon gevonden isX
+Geen vorig zoek patroonX
+Patroon niet gevondenX
+Begin van het bestand bereikt zonder dat het patroon gevonden isX
+Zoek-operatie omgeslagenX
+Bezig met zoeken...X
+Geen niet-printbaar karakter gevondenX
+Onbekend commandoX
+
+Commando niet beschikbaar in ex modeX
+Aantal mag niet nul zijnX
+%s: ongeldige regel aanduidingX
+Interne fout in syntax tabel (%s: %s)X
+Gebruik: %sX
+%s: tijdelijke buffer niet vrijgegevenX
+Vlag offset voor regel 1X
+Vlag offset voorbij bestands eindeX
+bestand/scherm veranderd tijdens uitvoeren van @ in een blokX
+bestand/scherm veranderd tijdens uitvoeren van globaal/v commandoX
+Ex commando mislukt: rest van commando(s) genegeerdX
+Ex commando mislukt: gemappede toetsen genegeerdX
+Het tweede adres is kleiner dan het eersteX
+Geen merk naam opgegevenX
+\\ niet gevolgd door / of ?X
+Referentie aan een regel nummer kleiner dan 0X
+Het %s commando is onbekendX
+Adres waarde te grootX
+Adres waarde te kleinX
+Ongeldige adres combinatieX
+Ongeldig adres: slechts %lu regels in het bestand aanwezigX
+Ongeldig adres: het bestand is leegX
+Het %s commando staat het adres 0 niet toeX
+Geen afkortingen om weer te gevenX
+Afkortingen moeten eindigen met een "woord" letterX
+Afkortingen mogen geen tabulaties of spaties bevattenX
+Afkortingen mogen geen woord/niet-woord karakters mengen, behalve aan het eindeX
+"%s" is geen afkortingX
+Vi commando mislukt: gemappede toetsen genegeerdX
+Dit is het laatste bestandX
+Dit is het eerste bestandX
+Dit is het eerste bestandX
+lijst met bestanden is leegX
+Geen voorgaand commando om "!" te vervangenX
+Geen bestandsnaam voor %%X
+Geen bestandsnaam voor #X
+Fout: execl: %sX
+I/O fout: %sX
+Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forcerenX
+Kan uw home directory niet vindenX
+Nieuwe huidige directory: %sX
+Geen cut buffers aanwezigX
+Het %s commando kan niet gebruikt worden in een globaal of v commandoX
+%s/%s: niet gelezen: noch U noch root is de eigenaarX
+%s/%s: niet gelezen: U bent niet de eigenaarX
+%s/%s: niet gelezen: kan gewijzigd worden door andere gebruikersX
+%s: niet gelezen: noch U noch root is de eigenaar"X
+%s: niet gelezen: U bent niet de eigenaarX
+%s: niet gelezen: kan gewijzigd worden door andere gebruikersX
+Geen volgende regel om samen te voegenX
+Geen input map entriesX
+Geen command map entriesX
+Het %s karakter kan niet ge-remapped wordenX
+"%s" is niet gemappedX
+Merk naam moet een enkel karakter zijnX
+%s bestaat al, niet weggeschreven; gebruik ! om het te forcerenX
+Nieuw .exrc bestand: %s. X
+doel regel ligt in het blokX
+Het open commando vereist dat de open optie actief isX
+Het open commando is nog niet ondersteundX
+Kan dit bestand niet veilig stellenX
+Bestand veilig gesteldX
+%s resulteert in te veel bestandsnamenX
+Alleen echte bestanden en named pipes kunnen gelezen wordenX
+%s: lees beveiliging niet beschikbaarX
+Bezig met lezen...X
+%s: %lu regels, %lu karaktersX
+Geen achtergrond schermen aanwezigX
+Het script commando is alleen beschikbaar in vi modeX
+Geen comando om uit te voerenX
+shiftwidth optie op 0 gezetX
+Count te grootX
+Count te kleinX
+Reguliere expressie opgegeven; r vlag heeft geen betekenisX
+De #, l en p vlaggen kunnen niet gecombineerd worden met de c vlag in vi modeX
+Geen match gevondenX
+Geen voorafgaande tag aanwezigX
+Minder dan %s elementen op de tags stapel; gebruik :display t[ags]X
+Geen bestand genaamd %s op de tags stapel; gebruik :display t[ags]X
+Kies Enter om door te gaan: X
+%s: tag niet gevondenX
+%s: verminkte tag in %sX
+%s: Het regel nummer van deze tag is voorbij het einde van het bestandX
+De tags stapel is leegX
+%s: zoek patroon niet gevondenX
+%d andere bestanden te wijzigenX
+Buffer %s is leegX
+Bevestig wijziging? [n]X
+OnderbrokenX
+Geen voorafgaande buffer om uit te voerenX
+Geen vorige reguliere expressieX
+Het %s commando vereist dat er een bestand geladen isX
+Gebruik: %sX
+Het visual commando vereist dat de open optie actief isX
+
+Leeg bestandX
+Geen voorafgaand F, f, T of t zoek commandoX
+%s niet gevondenX
+Geen voorafgaand bestand te bewerkenX
+Cursor niet op een getalX
+Getal wordt te grootX
+Getal wordt te kleinX
+Geen overeenkomstig karakter op deze regelX
+Overeenkomstig karakter niet gevondenX
+Geen karakters te vervangenX
+Geen ander scherm aanwezigX
+Karakters achter het zoek patroon, de regel offset, en/of het z commandoX
+Geen voorafgaand zoek patroonX
+Zoekopdracht na omslag teruggekeerd op originele positieX
+Afkorting overschrijdt expansie limiet: karakters genegeerdX
+Ongeldig karakter; quote to enterX
+Reeds aan het begin van de invoerX
+Niet meer karakters te verwijderenX
+Verplaatsing voorbij het einde van het bestandX
+Verplaatsing voorbij het einde van de regelX
+Cursor niet verplaatstX
+Reeds aan het begin van het bestandX
+Verplaatsing voorbij het begin van het bestandX
+Reeds in de eerste kolomX
+Buffers moeten voor het commando opgegeven wordenX
+Reeds bij het einde van het bestandX
+Reeds bij het einde van de regelX
+%s is geen vi commandoX
+Gebruik: %sX
+Geen karakters te verwijderenX
+Het Q commando vereist de ex terminal interfaceX
+Geen commando om te herhalenX
+Het bestand is leegX
+%s mag niet gebruikt worden als een verplaatsings commandoX
+Al in commando modeX
+Cursor niet in een woordX
+
+Windows optie waarde is te groot, maximum is %uX
+ToevoegenX
+VeranderenX
+CommandoX
+InvoegenX
+VervangenX
+Verplaatsing voorbij het eind van het schermX
+Verplaatsing voorbij het begin van het schermX
+Scherm moet meer dan %d regels hebben om het te kunnen splitsenX
+Er zijn geen achtergrond schermenX
+Er is geen achtergrond scherm waarin U bestand %s aan het bewerken bentX
+U kan uw enige scherm niet in de achtergrond zettenX
+Het scherm kan slechts verkleind worden tot %d regelsX
+Het scherm kan niet kleinerX
+Het scherm kan niet groterX
+
+Dit scherm kan niet gesuspend wordenX
+Onderbroken: gemappede toetsen genegeerdX
+vi: tijdelijke buffer niet vrijgegevenX
+Deze terminal heeft geen %s toetsX
+Er kan slechts een buffer opgegeven wordenX
+Getal groter dan %luX
+OnderbrokenX
+Aanmaken van tijdelijk bestand is misluktX
+Waarschuwing: %s is geen regulier bestandX
+%s is al geopend, bestand is in deze sessie niet schrijfbaarX
+%s: verwijdering misluktX
+%s: sluiting misluktX
+%s: verwijdering misluktX
+%s: verwijdering misluktX
+Bestand niet schrijfbaar, niet weggeschreven; gebruik ! om het te forcerenX
+Bestand niet schrijfbaar, niet weggeschrevenX
+%s bestaat al, niet weggeschreven; gebruik ! om het te forcerenX
+%s bestaat al, niet weggeschrevenX
+Gebruik ! om een incompleet bestand weg te schrijvenX
+Bestand incompleet, niet weggeschrevenX
+%s: bestand op disk nieuwer dan deze versie; gebruik ! om het te forcerenX
+%s: bestand op disk nieuwer dan deze versieX
+%s: schrijf beveiliging niet beschikbaarX
+Bezig met schrijven...X
+%s: WAARSCHUWING: BESTAND INCOMPLEETX
+Reeds op de eerste tag van deze groepX
+%s: nieuw bestand: %lu regels, %lu karaktersX
+%s: %lu regels, %lu karaktersX
+%s resulteert in te veel bestandsnamenX
+%s: geen normaal bestandX
+%s: U bent niet de eigenaarX
+%s: kan gewijzigd worden door andere gebruikersX
+Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forcerenX
+Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik :edit! om het te forcerenX
+Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forcerenX
+Tijdelijk bestand; exit negeert wijzigingenX
+Bestand niet schrijfbaar, wijzigingen niet automatisch weggeschrevenX
+log opnieuw gestartX
+Bevestig? [ynq]X
+Druk op een toets om door te gaan: X
+Druk op een toets om door te gaan [: voor meer ex commandos]: X
+Druk op een toets om door te gaan [q om te stoppen]: X
+Deze vorm van %s vereist de ex terminal interfaceX
+Entering ex input mode.X
+Commando mislukt, nog geen bestand geladen.X
+ doorgaan?X
+Onverwacht character eventX
+Onverwacht end-of-file eventX
+Geen match gevonden voor dit patroonX
+Onverwacht interrupt eventX
+Onverwacht quit eventX
+Onverwacht repaint eventX
+Reeds op de laatste tag van deze groepX
+Het %s command vereist de ex terminal interfaceX
+Deze vorm van %s is niet ondersteund als de secure edit optie actief isX
+Onverwacht string eventX
+Onverwacht timeout eventX
+Onverwacht write eventX
+
+Shell expansies zijn niet ondersteund als de secure edit optie actief isX
+Het %s commando is niet ondersteund als de secure edit optie actief isX
+set: %s mag niet uitgezet wordenX
+Scherm te klein.X
+toegevoegdX
+gewijzigdX
+verwijderdX
+samengevoegdX
+verplaatstX
+verschovenX
+gebufferdX
+regelX
+regelsX
+Vi was niet geladen met een Tcl interpreterX
+Bestand gewijzigd sinds het de laatste keer weg is geschreven.X
+Shell expansie misluktX
+Geen %s edit optie opgegevenX
+Vi was niet geladen met een Perl interpreterX
+Geen ex commando om uit te voerenX
+Kies <CR> om commando uit te voeren, :q om te stoppenX
+Gebruik "cscope help" voor uitlegX
+Nog geen cscope connectie aanwezigX
+%s: onbekend zoek type: gebruik een van %sX
+%d: onbekende cscope sessieX
+set: de %s optie mag nooit aangezet wordenX
+set: de %s optie mag nooit op 0 gezet wordenX
+%s: toegevoegd: %lu regels, %lu karaktersX
+Onverwacht resize eventX
+%d bestanden te wijzigenX
diff --git a/contrib/nvi/catalog/dutch.base b/contrib/nvi/catalog/dutch.base
new file mode 100644
index 000000000000..2d8667e6d460
--- /dev/null
+++ b/contrib/nvi/catalog/dutch.base
@@ -0,0 +1,307 @@
+002 "regel te lang"
+003 "kan regel %lu niet verwijderen"
+004 "kan niet toevoegen aan regel %lu"
+005 "kan niet invoegen vooraan regel %lu"
+006 "kan regel %lu niet opslaan"
+007 "kan laatste regel niet lezen"
+008 "Fout: kan regel %lu niet vinden"
+009 "log bestand"
+010 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken"
+011 "geen wijzigingen om ongedaan te maken"
+012 "Er vindt geen logging plaats, kan wijzigingen niet ongedaan maken"
+013 "Er vindt geen logging plaats, herhaling niet mogelijk"
+014 "geen wijzigingen om te herhalen"
+015 "%s/%d: schrijven naar log mislukt"
+016 "Vi's standaard invoer en uitvoer moeten aan een terminal gekoppeld zijn"
+017 "Merk %s: niet gezet"
+018 "Merk %s: de regel is verwijderd"
+019 "Merk %s: de cursor positie bestaat niet meer"
+020 "Fout: "
+021 "nieuw bestand"
+022 "naam veranderd"
+023 "gewijzigd"
+024 "ongewijzigd"
+025 "NIET BEVEILIGD"
+026 "niet schrijfbaar"
+027 "regel %lu uit %lu [%ld%%]"
+028 "leeg bestand"
+029 "regel %lu"
+030 "Het bestand %s is geen message catalog"
+031 "Niet in staat om de standaard %s optie in te stellen"
+032 "Gebruik: %s"
+033 "set: optie %s onbekend: 'set all' laat alle opties zien"
+034 "set: [no]%s optie kan geen waarde hebben"
+035 "set: %s optie moet een waarde hebben"
+036 "set: %s optie: %s"
+037 "set: %s optie: %s: getal is te groot"
+038 "set: %s optie: %s is een ongeldige waarde"
+039 "set: %s optie moet een waarde hebben"
+040 "Te weinig kolommen op het scherm, minder dan %d"
+041 "Aantal kolommen te groot, meer dan %d"
+042 "Te weinig regels op het scherm, minder dan %d"
+043 "Aantal regels te groot, meer dan %d"
+044 "De lisp optie is niet ondersteund"
+045 "messages niet uitgeschakeld: %s"
+046 "messages niet geactiveerd: %s"
+048 "De paragraph optie moet karakter paren bevatten"
+049 "De section optie moet karakter paren bevatten"
+053 "De standaard buffer is leeg"
+054 "Buffer %s is leeg"
+055 "Bestanden met newlines in de naam kunnen niet hersteld worden"
+056 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt"
+057 "Bestand wordt gecopieerd voor herstel..."
+058 "Herstel mechanisme werkt niet: %s"
+059 "Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie mislukt"
+060 "Kon bestand niet veilig stellen: %s"
+061 "Bestand wordt gecopieerd voor herstel..."
+062 "Informatie met betrekking tot gebruiker nummer %u niet gevonden"
+063 "Kan herstel bestand niet beveiligen"
+064 "herstel buffer overgelopen"
+065 "herstel bestand"
+066 "%s: verminkt herstel bestand"
+067 "%s: verminkt herstel bestand"
+068 "U heeft geen bestand genaamd %s te herstellen"
+069 "U kan eerdere versies van dit bestand herstellen"
+070 "U kan nog meer bestanden herstellen"
+071 "kan geen email versturen: %s"
+072 "Bestand leeg; niets om te doorzoeken"
+073 "Einde van het bestand bereikt zonder dat het patroon gevonden is"
+074 "Geen vorig zoek patroon"
+075 "Patroon niet gevonden"
+076 "Begin van het bestand bereikt zonder dat het patroon gevonden is"
+077 "Zoek-operatie omgeslagen"
+078 "Bezig met zoeken..."
+079 "Geen niet-printbaar karakter gevonden"
+080 "Onbekend commando"
+082 "Commando niet beschikbaar in ex mode"
+083 "Aantal mag niet nul zijn"
+084 "%s: ongeldige regel aanduiding"
+085 "Interne fout in syntax tabel (%s: %s)"
+086 "Gebruik: %s"
+087 "%s: tijdelijke buffer niet vrijgegeven"
+088 "Vlag offset voor regel 1"
+089 "Vlag offset voorbij bestands einde"
+090 "bestand/scherm veranderd tijdens uitvoeren van @ in een blok"
+091 "bestand/scherm veranderd tijdens uitvoeren van globaal/v commando"
+092 "Ex commando mislukt: rest van commando(s) genegeerd"
+093 "Ex commando mislukt: gemappede toetsen genegeerd"
+094 "Het tweede adres is kleiner dan het eerste"
+095 "Geen merk naam opgegeven"
+096 "\\ niet gevolgd door / of ?"
+097 "Referentie aan een regel nummer kleiner dan 0"
+098 "Het %s commando is onbekend"
+099 "Adres waarde te groot"
+100 "Adres waarde te klein"
+101 "Ongeldige adres combinatie"
+102 "Ongeldig adres: slechts %lu regels in het bestand aanwezig"
+103 "Ongeldig adres: het bestand is leeg"
+104 "Het %s commando staat het adres 0 niet toe"
+105 "Geen afkortingen om weer te geven"
+106 "Afkortingen moeten eindigen met een "woord" letter"
+107 "Afkortingen mogen geen tabulaties of spaties bevatten"
+108 "Afkortingen mogen geen woord/niet-woord karakters mengen, behalve aan het einde"
+109 ""%s" is geen afkorting"
+110 "Vi commando mislukt: gemappede toetsen genegeerd"
+111 "Dit is het laatste bestand"
+112 "Dit is het eerste bestand"
+113 "Dit is het eerste bestand"
+114 "lijst met bestanden is leeg"
+115 "Geen voorgaand commando om "!" te vervangen"
+116 "Geen bestandsnaam voor %%"
+117 "Geen bestandsnaam voor #"
+118 "Fout: execl: %s"
+119 "I/O fout: %s"
+120 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
+121 "Kan uw home directory niet vinden"
+122 "Nieuwe huidige directory: %s"
+123 "Geen cut buffers aanwezig"
+124 "Het %s commando kan niet gebruikt worden in een globaal of v commando"
+125 "%s/%s: niet gelezen: noch U noch root is de eigenaar"
+126 "%s/%s: niet gelezen: U bent niet de eigenaar"
+127 "%s/%s: niet gelezen: kan gewijzigd worden door andere gebruikers"
+128 "%s: niet gelezen: noch U noch root is de eigenaar""
+129 "%s: niet gelezen: U bent niet de eigenaar"
+130 "%s: niet gelezen: kan gewijzigd worden door andere gebruikers"
+131 "Geen volgende regel om samen te voegen"
+132 "Geen input map entries"
+133 "Geen command map entries"
+134 "Het %s karakter kan niet ge-remapped worden"
+135 ""%s" is niet gemapped"
+136 "Merk naam moet een enkel karakter zijn"
+137 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren"
+138 "Nieuw .exrc bestand: %s. "
+139 "doel regel ligt in het blok"
+140 "Het open commando vereist dat de open optie actief is"
+141 "Het open commando is nog niet ondersteund"
+142 "Kan dit bestand niet veilig stellen"
+143 "Bestand veilig gesteld"
+144 "%s resulteert in te veel bestandsnamen"
+145 "Alleen echte bestanden en named pipes kunnen gelezen worden"
+146 "%s: lees beveiliging niet beschikbaar"
+147 "Bezig met lezen..."
+148 "%s: %lu regels, %lu karakters"
+149 "Geen achtergrond schermen aanwezig"
+150 "Het script commando is alleen beschikbaar in vi mode"
+151 "Geen comando om uit te voeren"
+152 "shiftwidth optie op 0 gezet"
+153 "Count te groot"
+154 "Count te klein"
+155 "Reguliere expressie opgegeven; r vlag heeft geen betekenis"
+156 "De #, l en p vlaggen kunnen niet gecombineerd worden met de c vlag in vi mode"
+157 "Geen match gevonden"
+158 "Geen voorafgaande tag aanwezig"
+159 "Minder dan %s elementen op de tags stapel; gebruik :display t[ags]"
+160 "Geen bestand genaamd %s op de tags stapel; gebruik :display t[ags]"
+161 "Kies Enter om door te gaan: "
+162 "%s: tag niet gevonden"
+163 "%s: verminkte tag in %s"
+164 "%s: Het regel nummer van deze tag is voorbij het einde van het bestand"
+165 "De tags stapel is leeg"
+166 "%s: zoek patroon niet gevonden"
+167 "%d andere bestanden te wijzigen"
+168 "Buffer %s is leeg"
+169 "Bevestig wijziging? [n]"
+170 "Onderbroken"
+171 "Geen voorafgaande buffer om uit te voeren"
+172 "Geen vorige reguliere expressie"
+173 "Het %s commando vereist dat er een bestand geladen is"
+174 "Gebruik: %s"
+175 "Het visual commando vereist dat de open optie actief is"
+177 "Leeg bestand"
+178 "Geen voorafgaand F, f, T of t zoek commando"
+179 "%s niet gevonden"
+180 "Geen voorafgaand bestand te bewerken"
+181 "Cursor niet op een getal"
+182 "Getal wordt te groot"
+183 "Getal wordt te klein"
+184 "Geen overeenkomstig karakter op deze regel"
+185 "Overeenkomstig karakter niet gevonden"
+186 "Geen karakters te vervangen"
+187 "Geen ander scherm aanwezig"
+188 "Karakters achter het zoek patroon, de regel offset, en/of het z commando"
+189 "Geen voorafgaand zoek patroon"
+190 "Zoekopdracht na omslag teruggekeerd op originele positie"
+191 "Afkorting overschrijdt expansie limiet: karakters genegeerd"
+192 "Ongeldig karakter; quote to enter"
+193 "Reeds aan het begin van de invoer"
+194 "Niet meer karakters te verwijderen"
+195 "Verplaatsing voorbij het einde van het bestand"
+196 "Verplaatsing voorbij het einde van de regel"
+197 "Cursor niet verplaatst"
+198 "Reeds aan het begin van het bestand"
+199 "Verplaatsing voorbij het begin van het bestand"
+200 "Reeds in de eerste kolom"
+201 "Buffers moeten voor het commando opgegeven worden"
+202 "Reeds bij het einde van het bestand"
+203 "Reeds bij het einde van de regel"
+204 "%s is geen vi commando"
+205 "Gebruik: %s"
+206 "Geen karakters te verwijderen"
+207 "Het Q commando vereist de ex terminal interface"
+208 "Geen commando om te herhalen"
+209 "Het bestand is leeg"
+210 "%s mag niet gebruikt worden als een verplaatsings commando"
+211 "Al in commando mode"
+212 "Cursor niet in een woord"
+214 "Windows optie waarde is te groot, maximum is %u"
+215 "Toevoegen"
+216 "Veranderen"
+217 "Commando"
+218 "Invoegen"
+219 "Vervangen"
+220 "Verplaatsing voorbij het eind van het scherm"
+221 "Verplaatsing voorbij het begin van het scherm"
+222 "Scherm moet meer dan %d regels hebben om het te kunnen splitsen"
+223 "Er zijn geen achtergrond schermen"
+224 "Er is geen achtergrond scherm waarin U bestand %s aan het bewerken bent"
+225 "U kan uw enige scherm niet in de achtergrond zetten"
+226 "Het scherm kan slechts verkleind worden tot %d regels"
+227 "Het scherm kan niet kleiner"
+228 "Het scherm kan niet groter"
+230 "Dit scherm kan niet gesuspend worden"
+231 "Onderbroken: gemappede toetsen genegeerd"
+232 "vi: tijdelijke buffer niet vrijgegeven"
+233 "Deze terminal heeft geen %s toets"
+234 "Er kan slechts een buffer opgegeven worden"
+235 "Getal groter dan %lu"
+236 "Onderbroken"
+237 "Aanmaken van tijdelijk bestand is mislukt"
+238 "Waarschuwing: %s is geen regulier bestand"
+239 "%s is al geopend, bestand is in deze sessie niet schrijfbaar"
+240 "%s: verwijdering mislukt"
+241 "%s: sluiting mislukt"
+242 "%s: verwijdering mislukt"
+243 "%s: verwijdering mislukt"
+244 "Bestand niet schrijfbaar, niet weggeschreven; gebruik ! om het te forceren"
+245 "Bestand niet schrijfbaar, niet weggeschreven"
+246 "%s bestaat al, niet weggeschreven; gebruik ! om het te forceren"
+247 "%s bestaat al, niet weggeschreven"
+248 "Gebruik ! om een incompleet bestand weg te schrijven"
+249 "Bestand incompleet, niet weggeschreven"
+250 "%s: bestand op disk nieuwer dan deze versie; gebruik ! om het te forceren"
+251 "%s: bestand op disk nieuwer dan deze versie"
+252 "%s: schrijf beveiliging niet beschikbaar"
+253 "Bezig met schrijven..."
+254 "%s: WAARSCHUWING: BESTAND INCOMPLEET"
+255 "Reeds op de eerste tag van deze groep"
+256 "%s: nieuw bestand: %lu regels, %lu karakters"
+257 "%s: %lu regels, %lu karakters"
+258 "%s resulteert in te veel bestandsnamen"
+259 "%s: geen normaal bestand"
+260 "%s: U bent niet de eigenaar"
+261 "%s: kan gewijzigd worden door andere gebruikers"
+262 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
+263 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik :edit! om het te forceren"
+264 "Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forceren"
+265 "Tijdelijk bestand; exit negeert wijzigingen"
+266 "Bestand niet schrijfbaar, wijzigingen niet automatisch weggeschreven"
+267 "log opnieuw gestart"
+268 "Bevestig? [ynq]"
+269 "Druk op een toets om door te gaan: "
+270 "Druk op een toets om door te gaan [: voor meer ex commandos]: "
+271 "Druk op een toets om door te gaan [q om te stoppen]: "
+272 "Deze vorm van %s vereist de ex terminal interface"
+273 "Entering ex input mode."
+274 "Commando mislukt, nog geen bestand geladen."
+275 " doorgaan?"
+276 "Onverwacht character event"
+277 "Onverwacht end-of-file event"
+278 "Geen match gevonden voor dit patroon"
+279 "Onverwacht interrupt event"
+280 "Onverwacht quit event"
+281 "Onverwacht repaint event"
+282 "Reeds op de laatste tag van deze groep"
+283 "Het %s command vereist de ex terminal interface"
+284 "Deze vorm van %s is niet ondersteund als de secure edit optie actief is"
+285 "Onverwacht string event"
+286 "Onverwacht timeout event"
+287 "Onverwacht write event"
+289 "Shell expansies zijn niet ondersteund als de secure edit optie actief is"
+290 "Het %s commando is niet ondersteund als de secure edit optie actief is"
+291 "set: %s mag niet uitgezet worden"
+292 "Scherm te klein."
+293 "toegevoegd"
+294 "gewijzigd"
+295 "verwijderd"
+296 "samengevoegd"
+297 "verplaatst"
+298 "verschoven"
+299 "gebufferd"
+300 "regel"
+301 "regels"
+302 "Vi was niet geladen met een Tcl interpreter"
+303 "Bestand gewijzigd sinds het de laatste keer weg is geschreven."
+304 "Shell expansie mislukt"
+305 "Geen %s edit optie opgegeven"
+306 "Vi was niet geladen met een Perl interpreter"
+307 "Geen ex commando om uit te voeren"
+308 "Kies <CR> om commando uit te voeren, :q om te stoppen"
+309 "Gebruik "cscope help" voor uitleg"
+310 "Nog geen cscope connectie aanwezig"
+311 "%s: onbekend zoek type: gebruik een van %s"
+312 "%d: onbekende cscope sessie"
+313 "set: de %s optie mag nooit aangezet worden"
+314 "set: de %s optie mag nooit op 0 gezet worden"
+315 "%s: toegevoegd: %lu regels, %lu karakters"
+316 "Onverwacht resize event"
+317 "%d bestanden te wijzigen"
diff --git a/contrib/nvi/catalog/dutch.check b/contrib/nvi/catalog/dutch.check
new file mode 100644
index 000000000000..d651aec75cc2
--- /dev/null
+++ b/contrib/nvi/catalog/dutch.check
@@ -0,0 +1,37 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+213
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s bestaat al, niet weggeschreven; gebruik ! om het te forcerenX
+ 2 %s resulteert in te veel bestandsnamenX
+ 2 %s: %lu regels, %lu karaktersX
+ 2 %s: verminkt herstel bestandX
+ 2 Bestand wordt gecopieerd voor herstel...X
+ 2 Buffer %s is leegX
+ 2 Dit is het eerste bestandX
+ 2 Er vindt geen logging plaats, kan wijzigingen niet ongedaan makenX
+ 2 OnderbrokenX
+ 2 Wijzigingen kunnen niet ongedaan gemaakt worden als deze sessie misluktX
+ 2 gewijzigdX
+ 2 set: %s optie moet een waarde hebbenX
+ 3 %s: verwijdering misluktX
+ 3 Bestand gewijzigd sinds laatste schrijfactie; schrijf het weg of gebruik ! om het te forcerenX
+ 4 Gebruik: %sX
+=========================
diff --git a/contrib/nvi/catalog/dutch.owner b/contrib/nvi/catalog/dutch.owner
new file mode 100644
index 000000000000..27f47d30dbb3
--- /dev/null
+++ b/contrib/nvi/catalog/dutch.owner
@@ -0,0 +1 @@
+J.G. Vons <Gert-Jan.Vons@ocegr.fr>
diff --git a/contrib/nvi/catalog/english b/contrib/nvi/catalog/english
new file mode 100644
index 000000000000..f34451d9b6d5
--- /dev/null
+++ b/contrib/nvi/catalog/english
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+Line length overflowX
+unable to delete line %luX
+unable to append to line %luX
+unable to insert at line %luX
+unable to store line %luX
+unable to get last lineX
+Error: unable to retrieve line %luX
+Log fileX
+Logging not being performed, undo not possibleX
+No changes to undoX
+Logging not being performed, undo not possibleX
+Logging not being performed, roll-forward not possibleX
+No changes to re-doX
+%s/%d: log put errorX
+Vi's standard input and output must be a terminalX
+Mark %s: not setX
+Mark %s: the line was deletedX
+Mark %s: cursor position no longer existsX
+Error: X
+new fileX
+name changedX
+modifiedX
+unmodifiedX
+UNLOCKEDX
+readonlyX
+line %lu of %lu [%ld%%]X
+empty fileX
+line %luX
+The file %s is not a message catalogX
+Unable to set default %s optionX
+Usage: %sX
+set: no %s option: 'set all' gives all option valuesX
+set: [no]%s option doesn't take a valueX
+set: %s option isn't a booleanX
+set: %s option: %sX
+set: %s option: %s: value overflowX
+set: %s option: %s is an illegal numberX
+set: %s option isn't a booleanX
+Screen columns too small, less than %dX
+Screen columns too large, greater than %dX
+Screen lines too small, less than %dX
+Screen lines too large, greater than %dX
+The lisp option is not implementedX
+messages not turned off: %sX
+messages not turned on: %sX
+
+The paragraph option must be in two character groupsX
+The section option must be in two character groupsX
+
+
+
+The default buffer is emptyX
+Buffer %s is emptyX
+Files with newlines in the name are unrecoverableX
+Modifications not recoverable if the session failsX
+Copying file for recovery...X
+Preservation failed: %sX
+Modifications not recoverable if the session failsX
+File backup failed: %sX
+Copying file for recovery...X
+Information on user id %u not foundX
+Unable to lock recovery fileX
+Recovery file buffer overrunX
+Recovery fileX
+%s: malformed recovery fileX
+%s: malformed recovery fileX
+No files named %s, readable by you, to recoverX
+There are older versions of this file for you to recoverX
+There are other files for you to recoverX
+not sending email: %sX
+File empty; nothing to searchX
+Reached end-of-file without finding the patternX
+No previous search patternX
+Pattern not foundX
+Reached top-of-file without finding the patternX
+Search wrappedX
+Searching...X
+No non-printable character foundX
+Unknown command nameX
+
+%s: command not available in ex modeX
+Count may not be zeroX
+%s: bad line specificationX
+Internal syntax table error (%s: %s)X
+Usage: %sX
+%s: temporary buffer not releasedX
+Flag offset to before line 1X
+Flag offset past end-of-fileX
+@ with range running when the file/screen changedX
+Global/v command running when the file/screen changedX
+Ex command failed: pending commands discardedX
+Ex command failed: mapped keys discardedX
+The second address is smaller than the firstX
+No mark name suppliedX
+\\ not followed by / or ?X
+Reference to a line number less than 0X
+The %s command is unknownX
+Address value overflowX
+Address value underflowX
+Illegal address combinationX
+Illegal address: only %lu lines in the fileX
+Illegal address: the file is emptyX
+The %s command doesn't permit an address of 0X
+No abbreviations to displayX
+Abbreviations must end with a "word" characterX
+Abbreviations may not contain tabs or spacesX
+Abbreviations may not mix word/non-word characters, except at the endX
+"%s" is not an abbreviationX
+Vi command failed: mapped keys discardedX
+No more files to editX
+No previous files to editX
+No previous files to rewindX
+No file list to displayX
+No previous command to replace "!"X
+No filename to substitute for %%X
+No filename to substitute for #X
+Error: execl: %sX
+I/O error: %sX
+File modified since last complete write; write or use ! to overrideX
+Unable to find home directory locationX
+New current directory: %sX
+No cut buffers to displayX
+The %s command can't be used as part of a global or v commandX
+%s/%s: not sourced: not owned by you or rootX
+%s/%s: not sourced: not owned by youX
+%s/%s: not sourced: writeable by a user other than the ownerX
+%s: not sourced: not owned by you or rootX
+%s: not sourced: not owned by youX
+%s: not sourced: writeable by a user other than the ownerX
+No following lines to joinX
+No input map entriesX
+No command map entriesX
+The %s character may not be remappedX
+"%s" isn't currently mappedX
+Mark names must be a single characterX
+%s exists, not written; use ! to overrideX
+New exrc file: %sX
+Destination line is inside move rangeX
+The open command requires that the open option be setX
+The open command is not yet implementedX
+Preservation of this file not possibleX
+File preservedX
+%s: expanded into too many file namesX
+Only regular files and named pipes may be readX
+%s: read lock was unavailableX
+Reading...X
+%s: %lu lines, %lu charactersX
+No background screens to displayX
+The script command is only available in vi modeX
+No command to executeX
+shiftwidth option set to 0X
+Count overflowX
+Count underflowX
+Regular expression specified; r flag meaninglessX
+The #, l and p flags may not be combined with the c flag in vi modeX
+No match foundX
+No previous tag enteredX
+Less than %s entries on the tags stack; use :display t[ags]X
+No file %s on the tags stack to return to; use :display t[ags]X
+Press Enter to continue: X
+%s: tag not foundX
+%s: corrupted tag in %sX
+%s: the tag's line number is past the end of the fileX
+The tags stack is emptyX
+%s: search pattern not foundX
+%d more files to editX
+Buffer %s is emptyX
+Confirm change? [n]X
+InterruptedX
+No previous buffer to executeX
+No previous regular expressionX
+The %s command requires that a file have already been read inX
+Usage: %sX
+The visual command requires that the open option be setX
+
+Empty fileX
+No previous F, f, T or t searchX
+%s not foundX
+No previous file to editX
+Cursor not in a numberX
+Resulting number too largeX
+Resulting number too smallX
+No match character on this lineX
+Matching character not foundX
+No characters to replaceX
+No other screen to switch toX
+Characters after search string, line offset and/or z commandX
+No previous search patternX
+Search wrapped to original positionX
+Abbreviation exceeded expansion limit: characters discardedX
+Illegal character; quote to enterX
+Already at the beginning of the insertX
+No more characters to eraseX
+Movement past the end-of-fileX
+Movement past the end-of-lineX
+No cursor movement madeX
+Already at the beginning of the fileX
+Movement past the beginning of the fileX
+Already in the first columnX
+Buffers should be specified before the commandX
+Already at end-of-fileX
+Already at end-of-lineX
+%s isn't a vi commandX
+Usage: %sX
+No characters to deleteX
+The Q command requires the ex terminal interfaceX
+No command to repeatX
+The file is emptyX
+%s may not be used as a motion commandX
+Already in command modeX
+Cursor not in a wordX
+
+Windows option value is too large, max is %uX
+AppendX
+ChangeX
+CommandX
+InsertX
+ReplaceX
+Movement past the end-of-screenX
+Movement past the beginning-of-screenX
+Screen must be larger than %d lines to splitX
+There are no background screensX
+There's no background screen editing a file named %sX
+You may not background your only displayed screenX
+The screen can only shrink to %d rowsX
+The screen cannot shrinkX
+The screen cannot growX
+
+This screen may not be suspendedX
+Interrupted: mapped keys discardedX
+vi: temporary buffer not releasedX
+This terminal has no %s keyX
+Only one buffer may be specifiedX
+Number larger than %luX
+InterruptedX
+Unable to create temporary fileX
+Warning: %s is not a regular fileX
+%s already locked, session is read-onlyX
+%s: removeX
+%s: closeX
+%s: removeX
+%s: removeX
+Read-only file, not written; use ! to overrideX
+Read-only file, not writtenX
+%s exists, not written; use ! to overrideX
+%s exists, not writtenX
+Partial file, not written; use ! to overrideX
+Partial file, not writtenX
+%s: file modified more recently than this copy; use ! to overrideX
+%s: file modified more recently than this copyX
+%s: write lock was unavailableX
+Writing...X
+%s: WARNING: FILE TRUNCATEDX
+Already at the first tag of this groupX
+%s: new file: %lu lines, %lu charactersX
+%s: %lu lines, %lu charactersX
+%s expanded into too many file namesX
+%s: not a regular fileX
+%s: not owned by youX
+%s: accessible by a user other than the ownerX
+File modified since last complete write; write or use ! to overrideX
+File modified since last complete write; write or use :edit! to overrideX
+File modified since last complete write; write or use ! to overrideX
+File is a temporary; exit will discard modificationsX
+File readonly, modifications not auto-writtenX
+Log restartedX
+confirm? [ynq]X
+Press any key to continue: X
+Press any key to continue [: to enter more ex commands]: X
+Press any key to continue [q to quit]: X
+That form of %s requires the ex terminal interfaceX
+Entering ex input mode.X
+Command failed, no file read in yet.X
+ cont?X
+Unexpected character eventX
+Unexpected end-of-file eventX
+No matches for queryX
+Unexpected interrupt eventX
+Unexpected quit eventX
+Unexpected repaint eventX
+Already at the last tag of this groupX
+The %s command requires the ex terminal interfaceX
+That form of %s is not supported when the secure edit option is setX
+Unexpected string eventX
+Unexpected timeout eventX
+Unexpected write eventX
+
+Shell expansions not supported when the secure edit option is setX
+The %s command is not supported when the secure edit option is setX
+set: the %s option may not be turned offX
+Display too small.X
+addedX
+changedX
+deletedX
+joinedX
+movedX
+shiftedX
+yankedX
+lineX
+linesX
+Vi was not loaded with a Tcl interpreterX
+File modified since last write.X
+Shell expansion failedX
+No %s edit option specifiedX
+Vi was not loaded with a Perl interpreterX
+No ex command to executeX
+Enter <CR> to execute a command, :q to exitX
+Use "cscope help" for helpX
+No cscope connections runningX
+%s: unknown search type: use one of %sX
+%d: no such cscope sessionX
+set: the %s option may never be turned onX
+set: the %s option may never be set to 0X
+%s: appended: %lu lines, %lu charactersX
+Unexpected resize eventX
+%d files to editX
diff --git a/contrib/nvi/catalog/english.base b/contrib/nvi/catalog/english.base
new file mode 100644
index 000000000000..6d8d7bf60bbf
--- /dev/null
+++ b/contrib/nvi/catalog/english.base
@@ -0,0 +1,309 @@
+002 "Line length overflow"
+003 "unable to delete line %lu"
+004 "unable to append to line %lu"
+005 "unable to insert at line %lu"
+006 "unable to store line %lu"
+007 "unable to get last line"
+008 "Error: unable to retrieve line %lu"
+009 "Log file"
+010 "Logging not being performed, undo not possible"
+011 "No changes to undo"
+012 "Logging not being performed, undo not possible"
+013 "Logging not being performed, roll-forward not possible"
+014 "No changes to re-do"
+015 "%s/%d: log put error"
+016 "Vi's standard input and output must be a terminal"
+017 "Mark %s: not set"
+018 "Mark %s: the line was deleted"
+019 "Mark %s: cursor position no longer exists"
+020 "Error: "
+021 "new file"
+022 "name changed"
+023 "modified"
+024 "unmodified"
+025 "UNLOCKED"
+026 "readonly"
+027 "line %lu of %lu [%ld%%]"
+028 "empty file"
+029 "line %lu"
+030 "The file %s is not a message catalog"
+031 "Unable to set default %s option"
+032 "Usage: %s"
+033 "set: no %s option: 'set all' gives all option values"
+034 "set: [no]%s option doesn't take a value"
+035 "set: %s option isn't a boolean"
+036 "set: %s option: %s"
+037 "set: %s option: %s: value overflow"
+038 "set: %s option: %s is an illegal number"
+039 "set: %s option isn't a boolean"
+040 "Screen columns too small, less than %d"
+041 "Screen columns too large, greater than %d"
+042 "Screen lines too small, less than %d"
+043 "Screen lines too large, greater than %d"
+044 "The lisp option is not implemented"
+045 "messages not turned off: %s"
+046 "messages not turned on: %s"
+048 "The paragraph option must be in two character groups"
+049 "The section option must be in two character groups"
+053 "The default buffer is empty"
+054 "Buffer %s is empty"
+055 "Files with newlines in the name are unrecoverable"
+056 "Modifications not recoverable if the session fails"
+057 "Copying file for recovery..."
+058 "Preservation failed: %s"
+059 "Modifications not recoverable if the session fails"
+060 "File backup failed: %s"
+061 "Copying file for recovery..."
+062 "Information on user id %u not found"
+063 "Unable to lock recovery file"
+064 "Recovery file buffer overrun"
+065 "Recovery file"
+066 "%s: malformed recovery file"
+067 "%s: malformed recovery file"
+068 "No files named %s, readable by you, to recover"
+069 "There are older versions of this file for you to recover"
+070 "There are other files for you to recover"
+071 "not sending email: %s"
+072 "File empty; nothing to search"
+073 "Reached end-of-file without finding the pattern"
+074 "No previous search pattern"
+075 "Pattern not found"
+076 "Reached top-of-file without finding the pattern"
+077 "Search wrapped"
+078 "Searching..."
+079 "No non-printable character found"
+080 "Unknown command name"
+082 "%s: command not available in ex mode"
+083 "Count may not be zero"
+084 "%s: bad line specification"
+085 "Internal syntax table error (%s: %s)"
+086 "Usage: %s"
+087 "%s: temporary buffer not released"
+088 "Flag offset to before line 1"
+089 "Flag offset past end-of-file"
+090 "@ with range running when the file/screen changed"
+091 "Global/v command running when the file/screen changed"
+092 "Ex command failed: pending commands discarded"
+093 "Ex command failed: mapped keys discarded"
+094 "The second address is smaller than the first"
+095 "No mark name supplied"
+096 "\\ not followed by / or ?"
+097 "Reference to a line number less than 0"
+098 "The %s command is unknown"
+099 "Address value overflow"
+100 "Address value underflow"
+101 "Illegal address combination"
+102 "Illegal address: only %lu lines in the file"
+103 "Illegal address: the file is empty"
+104 "The %s command doesn't permit an address of 0"
+105 "No abbreviations to display"
+106 "Abbreviations must end with a "word" character"
+107 "Abbreviations may not contain tabs or spaces"
+108 "Abbreviations may not mix word/non-word characters, except at the end"
+109 ""%s" is not an abbreviation"
+110 "Vi command failed: mapped keys discarded"
+111 "No more files to edit"
+112 "No previous files to edit"
+113 "No previous files to rewind"
+114 "No file list to display"
+115 "No previous command to replace "!""
+116 "No filename to substitute for %%"
+117 "No filename to substitute for #"
+118 "Error: execl: %s"
+119 "I/O error: %s"
+120 "File modified since last complete write; write or use ! to override"
+121 "Unable to find home directory location"
+122 "New current directory: %s"
+123 "No cut buffers to display"
+124 "The %s command can't be used as part of a global or v command"
+125 "%s/%s: not sourced: not owned by you or root"
+126 "%s/%s: not sourced: not owned by you"
+127 "%s/%s: not sourced: writeable by a user other than the owner"
+128 "%s: not sourced: not owned by you or root"
+129 "%s: not sourced: not owned by you"
+130 "%s: not sourced: writeable by a user other than the owner"
+131 "No following lines to join"
+132 "No input map entries"
+133 "No command map entries"
+134 "The %s character may not be remapped"
+135 ""%s" isn't currently mapped"
+136 "Mark names must be a single character"
+137 "%s exists, not written; use ! to override"
+138 "New exrc file: %s"
+139 "Destination line is inside move range"
+140 "The open command requires that the open option be set"
+141 "The open command is not yet implemented"
+142 "Preservation of this file not possible"
+143 "File preserved"
+144 "%s: expanded into too many file names"
+145 "Only regular files and named pipes may be read"
+146 "%s: read lock was unavailable"
+147 "Reading..."
+148 "%s: %lu lines, %lu characters"
+149 "No background screens to display"
+150 "The script command is only available in vi mode"
+151 "No command to execute"
+152 "shiftwidth option set to 0"
+153 "Count overflow"
+154 "Count underflow"
+155 "Regular expression specified; r flag meaningless"
+156 "The #, l and p flags may not be combined with the c flag in vi mode"
+157 "No match found"
+158 "No previous tag entered"
+159 "Less than %s entries on the tags stack; use :display t[ags]"
+160 "No file %s on the tags stack to return to; use :display t[ags]"
+161 "Press Enter to continue: "
+162 "%s: tag not found"
+163 "%s: corrupted tag in %s"
+164 "%s: the tag's line number is past the end of the file"
+165 "The tags stack is empty"
+166 "%s: search pattern not found"
+167 "%d more files to edit"
+168 "Buffer %s is empty"
+169 "Confirm change? [n]"
+170 "Interrupted"
+171 "No previous buffer to execute"
+172 "No previous regular expression"
+173 "The %s command requires that a file have already been read in"
+174 "Usage: %s"
+175 "The visual command requires that the open option be set"
+177 "Empty file"
+178 "No previous F, f, T or t search"
+179 "%s not found"
+180 "No previous file to edit"
+181 "Cursor not in a number"
+182 "Resulting number too large"
+183 "Resulting number too small"
+184 "No match character on this line"
+185 "Matching character not found"
+186 "No characters to replace"
+187 "No other screen to switch to"
+188 "Characters after search string, line offset and/or z command"
+189 "No previous search pattern"
+190 "Search wrapped to original position"
+191 "Abbreviation exceeded expansion limit: characters discarded"
+192 "Illegal character; quote to enter"
+193 "Already at the beginning of the insert"
+194 "No more characters to erase"
+195 "Movement past the end-of-file"
+196 "Movement past the end-of-line"
+197 "No cursor movement made"
+198 "Already at the beginning of the file"
+199 "Movement past the beginning of the file"
+200 "Already in the first column"
+201 "Buffers should be specified before the command"
+202 "Already at end-of-file"
+203 "Already at end-of-line"
+204 "%s isn't a vi command"
+205 "Usage: %s"
+206 "No characters to delete"
+207 "The Q command requires the ex terminal interface"
+208 "No command to repeat"
+209 "The file is empty"
+209 "The file is empty"
+210 "%s may not be used as a motion command"
+211 "Already in command mode"
+212 "Cursor not in a word"
+214 "Windows option value is too large, max is %u"
+215 "Append"
+216 "Change"
+217 "Command"
+218 "Insert"
+219 "Replace"
+220 "Movement past the end-of-screen"
+221 "Movement past the beginning-of-screen"
+222 "Screen must be larger than %d lines to split"
+223 "There are no background screens"
+224 "There's no background screen editing a file named %s"
+225 "You may not background your only displayed screen"
+226 "The screen can only shrink to %d rows"
+227 "The screen cannot shrink"
+228 "The screen cannot grow"
+230 "This screen may not be suspended"
+231 "Interrupted: mapped keys discarded"
+232 "vi: temporary buffer not released"
+233 "This terminal has no %s key"
+234 "Only one buffer may be specified"
+235 "Number larger than %lu"
+236 "Interrupted"
+237 "Unable to create temporary file"
+238 "Warning: %s is not a regular file"
+239 "%s already locked, session is read-only"
+240 "%s: remove"
+241 "%s: close"
+242 "%s: remove"
+243 "%s: remove"
+244 "Read-only file, not written; use ! to override"
+245 "Read-only file, not written"
+246 "%s exists, not written; use ! to override"
+247 "%s exists, not written"
+248 "Partial file, not written; use ! to override"
+249 "Partial file, not written"
+250 "%s: file modified more recently than this copy; use ! to override"
+251 "%s: file modified more recently than this copy"
+252 "%s: write lock was unavailable"
+253 "Writing..."
+254 "%s: WARNING: FILE TRUNCATED"
+255 "Already at the first tag of this group"
+256 "%s: new file: %lu lines, %lu characters"
+257 "%s: %lu lines, %lu characters"
+258 "%s expanded into too many file names"
+259 "%s: not a regular file"
+260 "%s: not owned by you"
+261 "%s: accessible by a user other than the owner"
+262 "File modified since last complete write; write or use ! to override"
+263 "File modified since last complete write; write or use :edit! to override"
+264 "File modified since last complete write; write or use ! to override"
+265 "File is a temporary; exit will discard modifications"
+266 "File readonly, modifications not auto-written"
+267 "Log restarted"
+268 "confirm? [ynq]"
+269 "Press any key to continue: "
+270 "Press any key to continue [: to enter more ex commands]: "
+271 "Press any key to continue [q to quit]: "
+272 "That form of %s requires the ex terminal interface"
+273 "Entering ex input mode."
+274 "Command failed, no file read in yet."
+275 " cont?"
+276 "Unexpected character event"
+277 "Unexpected end-of-file event"
+278 "No matches for query"
+279 "Unexpected interrupt event"
+280 "Unexpected quit event"
+281 "Unexpected repaint event"
+282 "Already at the last tag of this group"
+283 "The %s command requires the ex terminal interface"
+284 "That form of %s is not supported when the secure edit option is set"
+285 "Unexpected string event"
+286 "Unexpected timeout event"
+287 "Unexpected write event"
+289 "Shell expansions not supported when the secure edit option is set"
+290 "The %s command is not supported when the secure edit option is set"
+291 "set: the %s option may not be turned off"
+292 "Display too small."
+293 "added"
+294 "changed"
+295 "deleted"
+296 "joined"
+297 "moved"
+298 "shifted"
+299 "yanked"
+300 "line"
+301 "lines"
+302 "Vi was not loaded with a Tcl interpreter"
+303 "File modified since last write."
+304 "Shell expansion failed"
+304 "Shell expansion failed"
+305 "No %s edit option specified"
+306 "Vi was not loaded with a Perl interpreter"
+307 "No ex command to execute"
+308 "Enter <CR> to execute a command, :q to exit"
+309 "Use "cscope help" for help"
+310 "No cscope connections running"
+311 "%s: unknown search type: use one of %s"
+312 "%d: no such cscope session"
+313 "set: the %s option may never be turned on"
+314 "set: the %s option may never be set to 0"
+315 "%s: appended: %lu lines, %lu characters"
+316 "Unexpected resize event"
+317 "%d files to edit"
diff --git a/contrib/nvi/catalog/english.check b/contrib/nvi/catalog/english.check
new file mode 100644
index 000000000000..51093785c87c
--- /dev/null
+++ b/contrib/nvi/catalog/english.check
@@ -0,0 +1,36 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+ 2 209 "The file is empty"
+ 2 304 "Shell expansion failed"
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s exists, not written; use ! to overrideX
+ 2 %s: %lu lines, %lu charactersX
+ 2 %s: malformed recovery fileX
+ 2 Buffer %s is emptyX
+ 2 Copying file for recovery...X
+ 2 InterruptedX
+ 2 Logging not being performed, undo not possibleX
+ 2 Modifications not recoverable if the session failsX
+ 2 No previous search patternX
+ 2 set: %s option isn't a booleanX
+ 3 %s: removeX
+ 3 File modified since last complete write; write or use ! to overrideX
+ 4 Usage: %sX
+=========================
diff --git a/contrib/nvi/catalog/english.owner b/contrib/nvi/catalog/english.owner
new file mode 100644
index 000000000000..034567ddd8b5
--- /dev/null
+++ b/contrib/nvi/catalog/english.owner
@@ -0,0 +1 @@
+Keith Bostic <bostic@cs.berkeley.edu>
diff --git a/contrib/nvi/catalog/french b/contrib/nvi/catalog/french
new file mode 100644
index 000000000000..a75c486ef9ba
--- /dev/null
+++ b/contrib/nvi/catalog/french
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+D‚passement de longueur de ligneX
+impossible de supprimer la ligne %luX
+impossible d'ajouter … la ligne %luX
+impossible d'ins‚rer devant la ligne %luX
+impossible de stocker la ligne %luX
+impossible d'obtenir la derniŠre ligneX
+Erreur : impossible de r‚cup‚rer la ligne %luX
+Fichier journalX
+Aucune connexion n'‚tant effectu‚e, impossible d'annulerX
+Aucune action … annulerX
+Aucune connexion n'‚tant effectu‚e, impossible d'annulerX
+Aucune connexion n'‚tant effectu‚e, reprise actualis‚e impossibleX
+Aucune action … refaireX
+%s/%d : Erreur d'‚criture de journalX
+L'entr‚e et la sortie Vi standards doivent ˆtre un terminalX
+Marque %s : non d‚finieX
+Marque %s : la ligne a ‚t‚ supprim‚eX
+Marque %s : la position du curseur n'existe plusX
+Erreur : X
+nouveau fichierX
+le nom a chang‚X
+modifi‚X
+non modifi‚X
+DEVERROUILLEX
+lecture seuleX
+ligne %lu de %lu [%ld%%]X
+fichier videX
+ligne %luX
+Ce fichier %s n'est pas un catalogue de messagesX
+Impossible de configurer option %s par d‚fautX
+Utilisation : %sX
+D‚finition : pas d'option %s : 'tout d‚finir' donne toutes les valeurs optionnellesX
+D‚finition : option [no]%s ne prend pas de valeurX
+D‚finition : l'option %s n'est pas bool‚enneX
+D‚finition : option %s : %sX
+D‚finition : option %s : %s : D‚passement de valeurX
+D‚finition : option %s : %s n'est pas un nombre valideX
+D‚finition : l'option %s n'est pas bool‚enneX
+Les colonnes de l'‚cran sont trop petites, inf‚rieures … %dX
+Les colonnes de l'‚cran sont trop grandes, sup‚rieures … %dX
+Les lignes de l'‚cran sont trop courtes, inf‚rieures … %dX
+Les lignes de l'‚cran sont trop longues, sup‚rieures … %dX
+L'option lisp n'est pas impl‚ment‚eX
+Les messages ne sont pas d‚sactiv‚s : %sX
+Les messages ne sont pas activ‚s : %sX
+
+L'option de paragraphe doit ˆtre en groupe de deux caractŠresX
+L'option de section doit ˆtre en groupe de deux caractŠresX
+
+
+
+Le tampon par d‚faut est videX
+Le tampon %s est videX
+Les fichiers dont le nom contient des caractŠres de saut de ligne sont irr‚cup‚rablesX
+Impossible de r‚cup‚rer les modifications si la session ‚choueX
+Copie en cours du fichier pour r‚cup‚ration...X
+La pr‚servation a ‚chou‚ : %sX
+Impossible de r‚cup‚rer les modifications si la session ‚choueX
+La sauvegarde du fichier a ‚chou‚ : %sX
+Copie en cours du fichier pour r‚cup‚ration...X
+Les renseignements sur l'identit‚ %u de l'utilisateur sont introuvablesX
+Impossible de verrouiller le fichier de r‚cup‚rationX
+D‚bordement de tampon du fichier de r‚cup‚rationX
+Fichier de r‚cup‚rationX
+%s : Fichier de r‚cup‚ration malform‚X
+%s : Fichier de r‚cup‚ration malform‚X
+Aucun fichier nomm‚ %s … r‚cup‚rer, que vous puissiez lireX
+Il existe des versions r‚cup‚rables ant‚rieures … ce fichierX
+Vous avez d'autres fichiers … r‚cup‚rerX
+pas d'envoi d'email : %sX
+Fichier vide, rien … rechercherX
+Fin de fichier atteinte sans trouver le motifX
+Pas de motif de recherche pr‚c‚dentX
+Motif introuvableX
+D‚but du fichier atteint sans trouver le motifX
+La recherche est revenue … son point de d‚partX
+Recherche en cours...X
+CaractŠre non-imprimable introuvableX
+Nom de commande inconnuX
+
+%s : Commande non disponible en ex modeX
+Le compte ne peut ˆtre z‚roX
+%s : mauvaise sp‚cification de ligneX
+Erreur de tableau de syntaxe interne (%s: %s)X
+Utilisation : %sX
+%s : tampon temporaire non lib‚r‚X
+D‚calage de drapeau hors de la ligne 1X
+D‚calage de drapeau hors de la fin du fichierX
+@ avec plage, en cours d'ex‚cution quand le fichier/l'‚cran a chang‚X
+Commande Global/v en cours d'ex‚cution quand le fichier/l'‚cran a chang‚X
+La commande ex a ‚chou‚ : commandes en attente abandonn‚esX
+La commande ex a ‚chou‚ : les touches affect‚es sont abandonn‚esX
+La deuxiŠme adresse est plus petite que la premiŠreX
+Aucun nom de marque fourniX
+\\ pas suivi par / ou ?X
+R‚f‚rence … un num‚ro de ligne inf‚rieure … 0X
+La commande %s est inconnueX
+D‚passement de la valeur adresseX
+D‚passement n‚gatif de la valeur adresseX
+Combinaison d'adresse non valideX
+Adresse non valide : il n'y a que des lignes %lu dans le fichierX
+Adresse non valide : le fichier est videX
+La commande %s ne permet pas une adresse de 0X
+Pas d'abr‚viations … afficherX
+Les abr‚viations doivent finir par un caractŠre"motX
+Les abr‚viations ne peuvent pas contenir de tabulations ni d'espacesX
+Les abr‚viations ne peuvent pas contenir un m‚lange de caractŠres mot/non-mot, sauf … la finX
+"%s" n'est pas une abr‚viationX
+La commande Vi a ‚chou‚ : Les touches affect‚es ont ‚t‚ abandonn‚esX
+Plus de fichiers … ‚diterX
+Pas de fichiers pr‚c‚dents … ‚diterX
+Pas de fichiers pr‚c‚dents … rembobinerX
+Pas de liste de fichiers … afficherX
+Pas de commande pr‚c‚dente … remplacer"!"X
+Pas de nom de fichier … substituer … %%X
+Pas de nom de fichier … substituer … #X
+Erreur : execl : %sX
+Erreur E/S : %sX
+Fichier modifi‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasserX
+Impossible de trouver l'emplacement du r‚pertoire d'origineX
+Nouveau r‚pertoire en cours : %sX
+Pas de tampon de coupure … afficherX
+La commande %s ne peut pas ˆtre utilis‚e … l'int‚rieur d'une commande globale ou commande vX
+%s/%s : ‚chec de source : ni vous ni le super-utilisateur n'ˆtes les propri‚taires X
+%s/%s : ‚chec de source : vous n'ˆtes pas le propri‚taireX
+%s/%s : ‚chec de source : peut ˆtre ‚crit par un utilisateur autre que le propri‚taireX
+%s : ‚chec de source : ni vous ni le super-utilisateur n'ˆtes les propri‚tairesX
+%s : ‚chec de source : vous n'ˆtes pas le propri‚taireX
+%s : ‚chec de source : peut ˆtre ‚crit par un utilisateur autre que le propri‚taireX
+Pas de lignes suivantes … joindreX
+Pas d'entr‚es de mappage d'entr‚eX
+Pas d'entr‚es de mappage de commandesX
+Le caractŠre %s ne peut pas ˆtre remapp‚X
+"%s" n'est pas actuellement mapp‚X
+Les noms de marque ne doivent avoir qu'un caractŠreX
+%s existe, non enregistr‚; utiliser ! pour outrepasserX
+Nouveau fichier exrc : %sX
+La ligne de destination est … l'int‚rieur de la plage … d‚placerX
+La commande ouverte n‚cessite que l'option ouverte soit d‚finieX
+La commande ouverte n'est pas encore impl‚ment‚eX
+La pr‚servation de ce fichier est impossibleX
+Fichier pr‚serv‚X
+%s: ‚tendu dans trop de noms de fichiersX
+Vous ne pouvez lire que les fichiers standards et les canaux de transmission nomm‚sX
+%s: Interdiction de lecture non disponibleX
+Lecture en cours...X
+%s: %lu lignes, %lu caractŠresX
+Pas d'‚crans d'arriŠre-plan … afficherX
+La commande script n'est disponible qu'en mode viX
+Pas de commande … ex‚cuterX
+Option de largeur de d‚calage d‚finie sur 0X
+Compter d‚passementX
+Compter d‚passement n‚gatifX
+Expression standard sp‚cifi‚e; drapeau r superfluX
+Vous ne pouvez pas en mode vi, combiner les drapeaux #, l et p avec le drapeau cX
+Aucune correspondance trouv‚eX
+Aucune marque pr‚c‚dente entr‚eX
+Moins de %s entr‚es dans la pile de marques ; utilisez t[ags]X
+Pas de fichier %s vers lequel retourner dans la pile de marques ; utiliser : affichage t[ags]X
+Appuyez sur Entr‚e pour continuer :X
+%s : marque introuvableX
+%s : marque corrompue en %sX
+%s : le num‚ro de ligne de la marque d‚passe la fin du fichierX
+La pile de marques est videX
+%s : motif de recherche introuvableX
+%d fichiers suppl‚mentaires … ‚diterX
+Le tampon %s est vide
+Confirmer les changements ? [n]X
+InterrompuX
+Pas de tampon pr‚c‚dent … ex‚cuterX
+Pas d'expression standard pr‚c‚denteX
+La commande %s n‚cessite qu'un fichier ait d‚j… ‚t‚ lu en m‚moireX
+Utilisation : %sX
+La commande visuelle n‚cessite que l'option ouverte soit d‚finieX
+
+Fichier videX
+Pas de recherche pr‚c‚dente F, f, T ou tX
+%s introuvableX
+Pas de fichier pr‚c‚dent … ‚diterX
+Le curseur n'est pas dans un nombreX
+Le nombre obtenu est trop grandX
+Le nombre obtenu est trop petitX
+Pas de correspondance de caractŠre sur cette ligneX
+CaractŠre correspondant introuvableX
+Pas de caractŠres … remplacerX
+Pas d'autre ‚cran vers lequel basculerX
+CaractŠres aprŠs la chaŒne de recherche, d‚calage de ligne et/ou commande zX
+Pas de motif de recherche pr‚c‚dentX
+La recherche est revenue … son point de d‚partX
+L'abr‚viation a d‚pass‚ la limite de l'expansion : caractŠres abandonn‚sX
+CaractŠre non valide ; guillemet pour saisirX
+D‚j… au d‚but de l'insertionX
+Plus de caractŠres … effacerX
+D‚placement hors de fin de fichierX
+D‚placement hors de fin de ligneX
+Aucun mouvement de curseur n'a ‚t‚ effectu‚X
+D‚j… au d‚but du fichierX
+D‚placement hors du d‚but du fichierX
+D‚j… dans la premiŠre colonneX
+Les tampons doivent ˆtre sp‚cifi‚s avant la commandeX
+D‚j… … la fin du fichierX
+D‚j… … la fin de la ligneX
+%s n'est pas une commande viX
+Utilisation : %sX
+Pas de caractŠres … supprimerX
+La commande Q n‚cessite une interface terminal exX
+Pas de commande … r‚p‚terX
+Le fichier est videX
+Vous ne pouvez pas utiliser %s comme commande de d‚placementX
+D‚j… en mode commandeX
+Le curseur n'est pas dans un motX
+
+Valeur optionnelle de fenˆtre trop grande, maximum est %uX
+AjouterX
+ChangerX
+CommandeX
+Ins‚rerX
+RemplacerX
+D‚placement hors de la fin d'‚cranX
+D‚placement hors du d‚but d'‚cranX
+L'‚cran doit ˆtre sup‚rieur … %d lignes pour se fractionnerX
+Il n'y a pas d'‚cran d'arriŠre-planX
+Il n'y a pas d'‚cran d'arriŠre-plan qui ‚dite un fichier nomm‚ %sX
+Vous ne pouvez pas mettre … l'arriŠre-plan votre seul ‚cran affich‚X
+L'‚cran ne peut ˆtre r‚duit qu'… %d rangsX
+L'‚cran n'est pas auto-r‚ductibleX
+L'‚cran n'est pas auto-extensibleX
+
+Vous ne pouvez pas mettre cet ‚cran en attenteX
+Interrompu : les touches affect‚es ont ‚t‚ abandonn‚esX
+vi : le tampon temporaire n' a pas ‚t‚ lib‚r‚X
+Ce terminal n'a pas de touche %sX
+Vous ne pouvez sp‚cifier qu'un seul tamponX
+Nombre sup‚rieur … %luX
+InterrompuX
+Impossible de cr‚er un fichier temporaireX
+Avertissement : %s n'est pas un fichier standardX
+%s d‚j… verrouill‚, session en lecture seuleX
+%s: supprimerX
+%s: fermerX
+%s: supprimerX
+%s: supprimerX
+Fichier en lecture seule, pas ‚crit, utiliser ! pour outrepasserX
+Fichier en lecture seule, pas ‚critX
+%s existe, pas ‚crit; utiliser ! pour outrepasserX
+%s existe, pas ‚critX
+Fichier partiel, pas ‚crit; utiliser ! pour outrepasserX
+Fichier partiel, pas ‚critX
+%s: fichier modifi‚ plus r‚cemment que cet exemplaire; utiliser ! pour outrepasserX
+%s: fichier modifi‚ plus r‚cemment que cet exemplaireX
+%s: interdiction d'‚criture non disponibleX
+Ecriture en cours...X
+%s: AVERTISSEMENT : FICHIER TRONQUEX
+PremiŠre marque de ce groupe d‚j… atteinteX
+%s: nouveau fichier : %lu lignes, %lu caractŠresX
+%s: %lu lignes, %lu caractŠresX
+%s ‚tendue … trop de noms de fichiersX
+%s: pas un fichier standardX
+%s: ne vous appartient pasX
+%s: accessible par un utilisateur autre que son propri‚taireX
+Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasser X
+Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser :edit! pour outrepasserX
+Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasserX
+Fichier temporaire ; quitter annulera les modificationsX
+Fichier en lecture seule ; les modifications ne sont pas ‚crites automatiquementX
+Journal red‚marr‚X
+confirmer ? [ynq]X
+Appuyez sur n'importe quelle touche pour continuer : X
+Appuyez sur n'importe quelle touche pour continuer [: pour entrer plus de commandes ex] : X
+Appuyez sur n'importe quelle touche pour continuer [q pour Quitter]: X
+Cette forme de %s n‚cessite l'interface de terminal exX
+Entr‚e de mode entr‚e ex.X
+La commande a ‚chou‚, aucun fichier n'a encore ‚t‚ lu.X
+cont?X
+Ev‚nement impr‚vu de caractŠreX
+Ev‚nement impr‚vu de fin-de-fichierX
+Pas de correspondances pour cette requˆteX
+Ev‚nement impr‚vu d'interruptionX
+Ev‚nement quitter impr‚vuX
+Ev‚nement impr‚vu de rafraŒchissementX
+La derniŠre marque de ce groupe a d‚j… ‚t‚ atteinteX
+La commande %s n‚cessite l'interface de terminal exX
+Cette forme de %s n'est pas reconnue quand l'option d'‚dition prot‚g‚e est activ‚eX
+Ev‚nement impr‚vu de chaŒneX
+Ev‚nement impr‚vu de d‚lai impartiX
+Ev‚nement d'‚criture impr‚vuX
+
+Les expansions du shell ne sont pas reconnues quand l'option d'‚dition prot‚g‚e est activ‚eX
+La commande %s n'est pas reconnue quand l'option d'‚dition prot‚g‚e est activ‚eX
+D‚finition : l'option %s ne peut pas ˆtre d‚sactiv‚eX
+Affichage trop petit.X
+ajout‚X
+chang‚X
+supprim‚X
+jointX
+d‚plac‚X
+d‚cal‚X
+coup‚X
+ligneX
+lignesX
+Vi n'a pas ‚t‚ charg‚ avec un interpr‚tateur TclX
+Ficher modifi‚ depuis le dernier enregistrement.X
+L'expansion du shell a ‚chou‚X
+Pas d'option d'‚dition %s sp‚cifi‚eX
+Vi n'a pas ‚t‚ charg‚ avec un interpr‚tateur PerlX
+Pas de commande ex … ex‚cuterX
+Entrez <CR> pour ex‚cuter une commande, :q pour quitterX
+Utiliser "cscope help" pour obtenir de l'aideX
+Aucune connexion cscope n'est lanc‚eX
+%s : type de recherche inconnu : utiliser un des %sX
+%d : Il n'existe pas de telle session cscopeX
+D‚finition : l'option %s ne peut jamais ˆtre activ‚eX
+D‚finition : l'option %s ne peut jamais ˆtre d‚finie sur 0X
+%s: joints : %lu lignes, %lu caractŠresX
+‚v‚nement impr‚vu de redimensionnementX
+%d fichiers … ‚diterX
diff --git a/contrib/nvi/catalog/french.base b/contrib/nvi/catalog/french.base
new file mode 100644
index 000000000000..db2d45f9b220
--- /dev/null
+++ b/contrib/nvi/catalog/french.base
@@ -0,0 +1,309 @@
+002 "D‚passement de longueur de ligne"
+003 "impossible de supprimer la ligne %lu"
+004 "impossible d'ajouter … la ligne %lu"
+005 "impossible d'ins‚rer devant la ligne %lu"
+006 "impossible de stocker la ligne %lu"
+007 "impossible d'obtenir la derniŠre ligne"
+008 "Erreur : impossible de r‚cup‚rer la ligne %lu"
+009 "Fichier journal"
+010 "Aucune connexion n'‚tant effectu‚e, impossible d'annuler"
+011 "Aucune action … annuler"
+012 "Aucune connexion n'‚tant effectu‚e, impossible d'annuler"
+013 "Aucune connexion n'‚tant effectu‚e, reprise actualis‚e impossible"
+014 "Aucune action … refaire"
+015 "%s/%d : Erreur d'‚criture de journal"
+016 "L'entr‚e et la sortie Vi standards doivent ˆtre un terminal"
+017 "Marque %s : non d‚finie"
+018 "Marque %s : la ligne a ‚t‚ supprim‚e"
+019 "Marque %s : la position du curseur n'existe plus"
+020 "Erreur : "
+021 "nouveau fichier"
+022 "le nom a chang‚"
+023 "modifi‚"
+024 "non modifi‚"
+025 "DEVERROUILLE"
+026 "lecture seule"
+027 "ligne %lu de %lu [%ld%%]"
+028 "fichier vide"
+029 "ligne %lu"
+030 "Ce fichier %s n'est pas un catalogue de messages"
+031 "Impossible de configurer option %s par d‚faut"
+032 "Utilisation : %s"
+033 "D‚finition : pas d'option %s : 'tout d‚finir' donne toutes les valeurs optionnelles"
+034 "D‚finition : option [no]%s ne prend pas de valeur"
+035 "D‚finition : l'option %s n'est pas bool‚enne"
+036 "D‚finition : option %s : %s"
+037 "D‚finition : option %s : %s : D‚passement de valeur"
+038 "D‚finition : option %s : %s n'est pas un nombre valide"
+039 "D‚finition : l'option %s n'est pas bool‚enne"
+040 "Les colonnes de l'‚cran sont trop petites, inf‚rieures … %d"
+041 "Les colonnes de l'‚cran sont trop grandes, sup‚rieures … %d"
+042 "Les lignes de l'‚cran sont trop courtes, inf‚rieures … %d"
+043 "Les lignes de l'‚cran sont trop longues, sup‚rieures … %d"
+044 "L'option lisp n'est pas impl‚ment‚e"
+045 "Les messages ne sont pas d‚sactiv‚s : %s"
+046 "Les messages ne sont pas activ‚s : %s"
+048 "L'option de paragraphe doit ˆtre en groupe de deux caractŠres"
+049 "L'option de section doit ˆtre en groupe de deux caractŠres"
+053 "Le tampon par d‚faut est vide"
+054 "Le tampon %s est vide"
+055 "Les fichiers dont le nom contient des caractŠres de saut de ligne sont irr‚cup‚rables"
+056 "Impossible de r‚cup‚rer les modifications si la session ‚choue"
+057 "Copie en cours du fichier pour r‚cup‚ration..."
+058 "La pr‚servation a ‚chou‚ : %s"
+059 "Impossible de r‚cup‚rer les modifications si la session ‚choue"
+060 "La sauvegarde du fichier a ‚chou‚ : %s"
+061 "Copie en cours du fichier pour r‚cup‚ration..."
+062 "Les renseignements sur l'identit‚ %u de l'utilisateur sont introuvables"
+063 "Impossible de verrouiller le fichier de r‚cup‚ration"
+064 "D‚bordement de tampon du fichier de r‚cup‚ration"
+065 "Fichier de r‚cup‚ration"
+066 "%s : Fichier de r‚cup‚ration malform‚"
+067 "%s : Fichier de r‚cup‚ration malform‚"
+068 "Aucun fichier nomm‚ %s … r‚cup‚rer, que vous puissiez lire"
+069 "Il existe des versions r‚cup‚rables ant‚rieures … ce fichier"
+070 "Vous avez d'autres fichiers … r‚cup‚rer"
+071 "pas d'envoi d'email : %s"
+072 "Fichier vide, rien … rechercher"
+073 "Fin de fichier atteinte sans trouver le motif"
+074 "Pas de motif de recherche pr‚c‚dent"
+075 "Motif introuvable"
+076 "D‚but du fichier atteint sans trouver le motif"
+077 "La recherche est revenue … son point de d‚part"
+078 "Recherche en cours..."
+079 "CaractŠre non-imprimable introuvable"
+080 "Nom de commande inconnu"
+082 "%s : Commande non disponible en ex mode"
+083 "Le compte ne peut ˆtre z‚ro"
+084 "%s : mauvaise sp‚cification de ligne"
+085 "Erreur de tableau de syntaxe interne (%s: %s)"
+086 "Utilisation : %s"
+087 "%s : tampon temporaire non lib‚r‚"
+088 "D‚calage de drapeau hors de la ligne 1"
+089 "D‚calage de drapeau hors de la fin du fichier"
+090 "@ avec plage, en cours d'ex‚cution quand le fichier/l'‚cran a chang‚"
+091 "Commande Global/v en cours d'ex‚cution quand le fichier/l'‚cran a chang‚"
+092 "La commande ex a ‚chou‚ : commandes en attente abandonn‚es"
+093 "La commande ex a ‚chou‚ : les touches affect‚es sont abandonn‚es"
+094 "La deuxiŠme adresse est plus petite que la premiŠre"
+095 "Aucun nom de marque fourni"
+096 "\\ pas suivi par / ou ?"
+097 "R‚f‚rence … un num‚ro de ligne inf‚rieure … 0"
+098 "La commande %s est inconnue"
+099 "D‚passement de la valeur adresse"
+100 "D‚passement n‚gatif de la valeur adresse"
+101 "Combinaison d'adresse non valide"
+102 "Adresse non valide : il n'y a que des lignes %lu dans le fichier"
+103 "Adresse non valide : le fichier est vide"
+104 "La commande %s ne permet pas une adresse de 0"
+105 "Pas d'abr‚viations … afficher"
+106 "Les abr‚viations doivent finir par un caractŠre"mot"
+107 "Les abr‚viations ne peuvent pas contenir de tabulations ni d'espaces"
+108 "Les abr‚viations ne peuvent pas contenir un m‚lange de caractŠres mot/non-mot, sauf … la fin"
+109 ""%s" n'est pas une abr‚viation"
+110 "La commande Vi a ‚chou‚ : Les touches affect‚es ont ‚t‚ abandonn‚es"
+111 "Plus de fichiers … ‚diter"
+112 "Pas de fichiers pr‚c‚dents … ‚diter"
+113 "Pas de fichiers pr‚c‚dents … rembobiner"
+114 "Pas de liste de fichiers … afficher"
+115 "Pas de commande pr‚c‚dente … remplacer"!""
+116 "Pas de nom de fichier … substituer … %%"
+117 "Pas de nom de fichier … substituer … #"
+118 "Erreur : execl : %s"
+119 "Erreur E/S : %s"
+120 "Fichier modifi‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasser"
+121 "Impossible de trouver l'emplacement du r‚pertoire d'origine"
+122 "Nouveau r‚pertoire en cours : %s"
+123 "Pas de tampon de coupure … afficher"
+124 "La commande %s ne peut pas ˆtre utilis‚e … l'int‚rieur d'une commande globale ou commande v"
+125 "%s/%s : ‚chec de source : ni vous ni le super-utilisateur n'ˆtes les propri‚taires "
+126 "%s/%s : ‚chec de source : vous n'ˆtes pas le propri‚taire"
+127 "%s/%s : ‚chec de source : peut ˆtre ‚crit par un utilisateur autre que le propri‚taire"
+128 "%s : ‚chec de source : ni vous ni le super-utilisateur n'ˆtes les propri‚taires"
+129 "%s : ‚chec de source : vous n'ˆtes pas le propri‚taire"
+130 "%s : ‚chec de source : peut ˆtre ‚crit par un utilisateur autre que le propri‚taire"
+131 "Pas de lignes suivantes … joindre"
+132 "Pas d'entr‚es de mappage d'entr‚e"
+133 "Pas d'entr‚es de mappage de commandes"
+134 "Le caractŠre %s ne peut pas ˆtre remapp‚"
+135 ""%s" n'est pas actuellement mapp‚"
+136 "Les noms de marque ne doivent avoir qu'un caractŠre"
+137 "%s existe, non enregistr‚; utiliser ! pour outrepasser"
+138 "Nouveau fichier exrc : %s"
+139 "La ligne de destination est … l'int‚rieur de la plage … d‚placer"
+140 "La commande ouverte n‚cessite que l'option ouverte soit d‚finie"
+141 "La commande ouverte n'est pas encore impl‚ment‚e"
+142 "La pr‚servation de ce fichier est impossible"
+143 "Fichier pr‚serv‚"
+144 "%s: ‚tendu dans trop de noms de fichiers"
+145 "Vous ne pouvez lire que les fichiers standards et les canaux de transmission nomm‚s"
+146 "%s: Interdiction de lecture non disponible"
+147 "Lecture en cours..."
+148 "%s: %lu lignes, %lu caractŠres"
+149 "Pas d'‚crans d'arriŠre-plan … afficher"
+150 "La commande script n'est disponible qu'en mode vi"
+151 "Pas de commande … ex‚cuter"
+152 "Option de largeur de d‚calage d‚finie sur 0"
+153 "Compter d‚passement"
+154 "Compter d‚passement n‚gatif"
+155 "Expression standard sp‚cifi‚e; drapeau r superflu"
+156 "Vous ne pouvez pas en mode vi, combiner les drapeaux #, l et p avec le drapeau c"
+157 "Aucune correspondance trouv‚e"
+158 "Aucune marque pr‚c‚dente entr‚e"
+159 "Moins de %s entr‚es dans la pile de marques ; utilisez t[ags]"
+160 "Pas de fichier %s vers lequel retourner dans la pile de marques ; utiliser : affichage t[ags]"
+161 "Appuyez sur Entr‚e pour continuer :"
+162 "%s : marque introuvable"
+163 "%s : marque corrompue en %s"
+164 "%s : le num‚ro de ligne de la marque d‚passe la fin du fichier"
+165 "La pile de marques est vide"
+166 "%s : motif de recherche introuvable"
+167 "%d fichiers suppl‚mentaires … ‚diter"
+168 "Le tampon %s est vide
+169 "Confirmer les changements ? [n]"
+170 "Interrompu"
+171 "Pas de tampon pr‚c‚dent … ex‚cuter"
+172 "Pas d'expression standard pr‚c‚dente"
+173 "La commande %s n‚cessite qu'un fichier ait d‚j… ‚t‚ lu en m‚moire"
+174 "Utilisation : %s"
+175 "La commande visuelle n‚cessite que l'option ouverte soit d‚finie"
+177 "Fichier vide"
+178 "Pas de recherche pr‚c‚dente F, f, T ou t"
+179 "%s introuvable"
+180 "Pas de fichier pr‚c‚dent … ‚diter"
+181 "Le curseur n'est pas dans un nombre"
+182 "Le nombre obtenu est trop grand"
+183 "Le nombre obtenu est trop petit"
+184 "Pas de correspondance de caractŠre sur cette ligne"
+185 "CaractŠre correspondant introuvable"
+186 "Pas de caractŠres … remplacer"
+187 "Pas d'autre ‚cran vers lequel basculer"
+188 "CaractŠres aprŠs la chaŒne de recherche, d‚calage de ligne et/ou commande z"
+189 "Pas de motif de recherche pr‚c‚dent"
+190 "La recherche est revenue … son point de d‚part"
+191 "L'abr‚viation a d‚pass‚ la limite de l'expansion : caractŠres abandonn‚s"
+192 "CaractŠre non valide ; guillemet pour saisir"
+193 "D‚j… au d‚but de l'insertion"
+194 "Plus de caractŠres … effacer"
+195 "D‚placement hors de fin de fichier"
+196 "D‚placement hors de fin de ligne"
+197 "Aucun mouvement de curseur n'a ‚t‚ effectu‚"
+198 "D‚j… au d‚but du fichier"
+199 "D‚placement hors du d‚but du fichier"
+200 "D‚j… dans la premiŠre colonne"
+201 "Les tampons doivent ˆtre sp‚cifi‚s avant la commande"
+202 "D‚j… … la fin du fichier"
+203 "D‚j… … la fin de la ligne"
+204 "%s n'est pas une commande vi"
+205 "Utilisation : %s"
+206 "Pas de caractŠres … supprimer"
+207 "La commande Q n‚cessite une interface terminal ex"
+208 "Pas de commande … r‚p‚ter"
+209 "Le fichier est vide"
+209 "Le fichier est vide"
+210 "Vous ne pouvez pas utiliser %s comme commande de d‚placement"
+211 "D‚j… en mode commande"
+212 "Le curseur n'est pas dans un mot"
+214 "Valeur optionnelle de fenˆtre trop grande, maximum est %u"
+215 "Ajouter"
+216 "Changer"
+217 "Commande"
+218 "Ins‚rer"
+219 "Remplacer"
+220 "D‚placement hors de la fin d'‚cran"
+221 "D‚placement hors du d‚but d'‚cran"
+222 "L'‚cran doit ˆtre sup‚rieur … %d lignes pour se fractionner"
+223 "Il n'y a pas d'‚cran d'arriŠre-plan"
+224 "Il n'y a pas d'‚cran d'arriŠre-plan qui ‚dite un fichier nomm‚ %s"
+225 "Vous ne pouvez pas mettre … l'arriŠre-plan votre seul ‚cran affich‚"
+226 "L'‚cran ne peut ˆtre r‚duit qu'… %d rangs"
+227 "L'‚cran n'est pas auto-r‚ductible"
+228 "L'‚cran n'est pas auto-extensible"
+230 "Vous ne pouvez pas mettre cet ‚cran en attente"
+231 "Interrompu : les touches affect‚es ont ‚t‚ abandonn‚es"
+232 "vi : le tampon temporaire n' a pas ‚t‚ lib‚r‚"
+233 "Ce terminal n'a pas de touche %s"
+234 "Vous ne pouvez sp‚cifier qu'un seul tampon"
+235 "Nombre sup‚rieur … %lu"
+236 "Interrompu"
+237 "Impossible de cr‚er un fichier temporaire"
+238 "Avertissement : %s n'est pas un fichier standard"
+239 "%s d‚j… verrouill‚, session en lecture seule"
+240 "%s: supprimer"
+241 "%s: fermer"
+242 "%s: supprimer"
+243 "%s: supprimer"
+244 "Fichier en lecture seule, pas ‚crit, utiliser ! pour outrepasser"
+245 "Fichier en lecture seule, pas ‚crit"
+246 "%s existe, pas ‚crit; utiliser ! pour outrepasser"
+247 "%s existe, pas ‚crit"
+248 "Fichier partiel, pas ‚crit; utiliser ! pour outrepasser"
+249 "Fichier partiel, pas ‚crit"
+250 "%s: fichier modifi‚ plus r‚cemment que cet exemplaire; utiliser ! pour outrepasser"
+251 "%s: fichier modifi‚ plus r‚cemment que cet exemplaire"
+252 "%s: interdiction d'‚criture non disponible"
+253 "Ecriture en cours..."
+254 "%s: AVERTISSEMENT : FICHIER TRONQUE"
+255 "PremiŠre marque de ce groupe d‚j… atteinte"
+256 "%s: nouveau fichier : %lu lignes, %lu caractŠres"
+257 "%s: %lu lignes, %lu caractŠres"
+258 "%s ‚tendue … trop de noms de fichiers"
+259 "%s: pas un fichier standard"
+260 "%s: ne vous appartient pas"
+261 "%s: accessible par un utilisateur autre que son propri‚taire"
+262 "Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasser "
+263 "Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser :edit! pour outrepasser"
+264 "Fichier modif‚ depuis la derniŠre ‚criture complŠte ; ‚crire ou utiliser ! pour outrepasser"
+265 "Fichier temporaire ; quitter annulera les modifications"
+266 "Fichier en lecture seule ; les modifications ne sont pas ‚crites automatiquement"
+267 "Journal red‚marr‚"
+268 "confirmer ? [ynq]"
+269 "Appuyez sur n'importe quelle touche pour continuer : "
+270 "Appuyez sur n'importe quelle touche pour continuer [: pour entrer plus de commandes ex] : "
+271 "Appuyez sur n'importe quelle touche pour continuer [q pour Quitter]: "
+272 "Cette forme de %s n‚cessite l'interface de terminal ex"
+273 "Entr‚e de mode entr‚e ex."
+274 "La commande a ‚chou‚, aucun fichier n'a encore ‚t‚ lu."
+275 "cont?"
+276 "Ev‚nement impr‚vu de caractŠre"
+277 "Ev‚nement impr‚vu de fin-de-fichier"
+278 "Pas de correspondances pour cette requˆte"
+279 "Ev‚nement impr‚vu d'interruption"
+280 "Ev‚nement quitter impr‚vu"
+281 "Ev‚nement impr‚vu de rafraŒchissement"
+282 "La derniŠre marque de ce groupe a d‚j… ‚t‚ atteinte"
+283 "La commande %s n‚cessite l'interface de terminal ex"
+284 "Cette forme de %s n'est pas reconnue quand l'option d'‚dition prot‚g‚e est activ‚e"
+285 "Ev‚nement impr‚vu de chaŒne"
+286 "Ev‚nement impr‚vu de d‚lai imparti"
+287 "Ev‚nement d'‚criture impr‚vu"
+289 "Les expansions du shell ne sont pas reconnues quand l'option d'‚dition prot‚g‚e est activ‚e"
+290 "La commande %s n'est pas reconnue quand l'option d'‚dition prot‚g‚e est activ‚e"
+291 "D‚finition : l'option %s ne peut pas ˆtre d‚sactiv‚e"
+292 "Affichage trop petit."
+293 "ajout‚"
+294 "chang‚"
+295 "supprim‚"
+296 "joint"
+297 "d‚plac‚"
+298 "d‚cal‚"
+299 "coup‚"
+300 "ligne"
+301 "lignes"
+302 "Vi n'a pas ‚t‚ charg‚ avec un interpr‚tateur Tcl"
+303 "Ficher modifi‚ depuis le dernier enregistrement."
+304 "L'expansion du shell a ‚chou‚"
+304 "L'expansion du shell a ‚chou‚"
+305 "Pas d'option d'‚dition %s sp‚cifi‚e"
+306 "Vi n'a pas ‚t‚ charg‚ avec un interpr‚tateur Perl"
+307 "Pas de commande ex … ex‚cuter"
+308 "Entrez <CR> pour ex‚cuter une commande, :q pour quitter"
+309 "Utiliser "cscope help" pour obtenir de l'aide"
+310 "Aucune connexion cscope n'est lanc‚e"
+311 "%s : type de recherche inconnu : utiliser un des %s"
+312 "%d : Il n'existe pas de telle session cscope"
+313 "D‚finition : l'option %s ne peut jamais ˆtre activ‚e"
+314 "D‚finition : l'option %s ne peut jamais ˆtre d‚finie sur 0"
+315 "%s: joints : %lu lignes, %lu caractŠres"
+316 "‚v‚nement impr‚vu de redimensionnement"
+317 "%d fichiers … ‚diter"
diff --git a/contrib/nvi/catalog/french.check b/contrib/nvi/catalog/french.check
new file mode 100644
index 000000000000..1e53d93cb446
--- /dev/null
+++ b/contrib/nvi/catalog/french.check
@@ -0,0 +1,34 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+ 2 209 "Le fichier est vide"
+ 2 304 "L'expansion du shell a ‚chou‚"
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s : Fichier de r‚cup‚ration malform‚X
+ 2 %s: %lu lignes, %lu caractŠresX
+ 2 Aucune connexion n'‚tant effectu‚e, impossible d'annulerX
+ 2 Copie en cours du fichier pour r‚cup‚ration...X
+ 2 D‚finition : l'option %s n'est pas bool‚enneX
+ 2 Impossible de r‚cup‚rer les modifications si la session ‚choueX
+ 2 InterrompuX
+ 2 La recherche est revenue … son point de d‚partX
+ 2 Pas de motif de recherche pr‚c‚dentX
+ 3 %s: supprimerX
+ 4 Utilisation : %sX
+=========================
diff --git a/contrib/nvi/catalog/german b/contrib/nvi/catalog/german
new file mode 100644
index 000000000000..0810ba6cf9b4
--- /dev/null
+++ b/contrib/nvi/catalog/german
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+Zeilenlaengen UeberlaufX
+kann Zeile %lu nicht loeschenX
+kann an Zeile %lu nicht anfuegenX
+kann in Zeile %lu nicht einfuegenX
+kann Zeile %lu nicht speichernX
+kann letzte Zeile nicht lesenX
+Fehler: kann Zeile %lu nicht wiederherstellenX
+ProtokolldateiX
+Keine Protokollierung aktiv, rueckgaengig machen nicht moeglichX
+Keine Aenderungen rueckgaengig zu machenX
+Keine Protokollierung aktiv, rueckgaengig machen nicht moeglichX
+Keine Protokollierung aktiv, Wiederholung von Aenderungen nicht moeglichX
+Keine Aenderungen zu wiederholenX
+%s/%d: ProtokollschreibfehlerX
+Vi's Standardein- und ausgabe muss ein Terminal seinX
+Marke %s: nicht gesetztX
+Marke %s: die Zeile wurde geloeschtX
+Marke %s: Cursorposition existiert nicht mehrX
+Fehler: X
+neue DateiX
+Name geaendertX
+geaendertX
+nicht geaendertX
+NICHT GELOCKEDX
+nur zum LesenX
+Zeile %lu von %lu [%ld%%]X
+leere DateiX
+Zeile %luX
+Die Datei %s ist kein MeldungskatalogX
+Setzen der Voreinstellung fuer %s Option nicht moeglichX
+Benutzung: %sX
+set: keine %s Option: 'set all' zeigt alle Optionen mit Werten anX
+set: der [no]%s Option kann kein Wert zugewiesen werdenX
+set: %s ist keine boolsche OptionX
+set: %s Option: %sX
+set: %s Option: %s: Wert UeberlaufX
+set: %s Option: %s ist eine ungueltige ZahlX
+set: %s ist keine boolsche OptionX
+Anzeige hat zu wenig Spalten, weniger als %dX
+Anzeige hat zu viele Spalten, mehr als %dX
+Anzeige hat zu wenig Zeilen, weniger als %dX
+Anzeige hat zu viele Zeilen, mehr als %dX
+Die lisp Option ist nicht implementiertX
+Messages nicht abgeschalten: %sX
+Messages nicht eingeschalten: %sX
+
+Die paragraph Option muss Gruppen zu zwei Zeichen enthaltenX
+Die section Option muss Gruppen zu zwei Zeichen enthaltenX
+
+
+
+Der Standardpuffer ist leerX
+Puffer %s ist leerX
+Dateien mit newlines im Namen sind nicht wiederherstellbarX
+Aenderungen nicht wiederherstellbar falls die Editorsitzung schief gehtX
+kopiere Datei fuer Wiederherstellung ...X
+Rettungsmechanismus funktioniert nicht: %sX
+Aenderungen nicht wiederherstellbar falls die Editorsitzung schief gehtX
+Sicherung der Datei gescheitert: %sX
+kopiere Datei fuer Wiederherstellung ...X
+Informationen ueber den Benutzer mit id %u nicht gefundenX
+Wiederherstellungsdatei kann nicht gesperrt werdenX
+Puffer der Wiederherstellungsdatei uebergelaufenX
+WiederherstellungsdateiX
+%s: Wiederherstellungsdatei hat falsches FormatX
+%s: Wiederherstellungsdatei hat falsches FormatX
+Keine von Ihnen lesbaren Dateien mit Namen %s zum WiederherstellenX
+Es gibt aeltere Versionen dieser Datei von Ihnen zum WiederherstellenX
+Sie haben noch andere Dateien zum WiederherstellenX
+schicke keine email: %sX
+Datei leer; nichts zu suchenX
+Dateiende erreicht, ohne das Suchmuster zu findenX
+Kein altes SuchmusterX
+Suchmuster nicht gefundenX
+Dateianfang erreicht, ohne das Suchmuster zu findenX
+Suche umgeschlagenX
+suche ...X
+Keine nichtdruckbaren Zeichen gefundenX
+Unbekannter KommandonameX
+
+%s: Kommando im ex Modus nicht verfuegbarX
+Count darf nicht Null seinX
+%s: falsche ZeilenangabeX
+Interner Syntaxtabellenfehler (%s: %s)X
+Benutzung: %sX
+%s: temporaerer Puffer nicht freigegebenX
+Flagoffset vor Zeile 1X
+Flagoffset hinter dem DateiendeX
+@ mit Bereich lief als Datei/Anzeige geaendert wurdeX
+globales oder v-Kommando lief als Datei/Anzeige geaendert wurdeX
+Ex Kommando misslungen: restliche Kommandoeingabe ignoriertX
+Ex Kommando misslungen: umdefinierte Tasten ungueltigX
+Die zweite Adresse ist kleiner als die ersteX
+Kein Markenname angegebenX
+\\ ohne folgenden / oder ?X
+Referenz auf eine negative ZeilennummerX
+Das Kommando %s ist unbekanntX
+Adresswert zu grossX
+Adresswert zu kleinX
+Ungueltige AdresskombinationX
+Ungueltige Adresse: nur %lu Zeilen in der DateiX
+Ungueltige Adresse: die Datei ist leerX
+Das Kommando %s erlaubt keine Adresse 0X
+Keine Abkuerzungen zum AnzeigenX
+Abkuerzungen muessen mit einem "Wort"-Zeichen endenX
+Abkuerzungen duerfen keine Tabulator- oder Leerzeichen enthaltenX
+In Abkuerzungen duerfen ausser am Ende Wort- und Nicht-Wort-Zeichen nicht gemischt werdenX
+"%s" ist keine AbkuerzungX
+Vi Kommando misslungen: umdefinierte Tasten ungueltigX
+Keine weiteren Dateien zu edierenX
+Keine vorhergehenden Dateien zu edierenX
+Keine vorhergehenden Dateien fuer rewindX
+Keine Dateiliste zum AnzeigenX
+Kein vorhergehendes Kommando um "!" zu ersetzenX
+Kein Dateiname fuer %%X
+Kein Dateiname fuer #X
+Fehler: execl: %sX
+I/O Fehler: %sX
+Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorierenX
+Kann Homedirectory nicht bestimmenX
+Neues aktuelles Directory: %sX
+Keine Puffer anzuzeigenX
+Das Kommando %s kann nicht als Teil eines global oder v Kommandos verwendet werdenX
+%s/%s: nicht gelesen: gehoehrt weder Ihnen noch rootX
+%s/%s: nicht gelesen: gehoehrt nicht IhnenX
+%s/%s: nicht gelesen: anderer Benutzer als Eigentuemer hat SchreibrechtX
+%s: nicht gelesen: gehoehrt weder Ihnen noch rootX
+%s: nicht gelesen: gehoehrt nicht IhnenX
+%s: nicht gelesen: anderer Benutzer als Eigentuemer hat SchreibrechtX
+Keine folgenden Zeilen zum VerbindenX
+Kein Eingabe-Map EintragX
+Kein Kommando-Map EintragX
+Das %s Zeichen kann nicht umgemappt werdenX
+"%s" ist momentan nicht gemapptX
+Markennamen muessen einzelne Buchstaben seinX
+%s existiert, nicht geschrieben; verwende ! zum UeberschreibenX
+Neue .exrc Datei: %s. X
+Zielzeile ist innerhalb des VerschiebebereichsX
+Das open Kommando verlangt, dass die open Option gesetzt istX
+Das open Kommando ist nocht nicht implementiertX
+Rettung dieser Datei nicht moeglichX
+Datei gerettetX
+%s wurde in zu viele Dateinamen expandiertX
+Nur regulaere Dateien und named pipes koennen gelesen werdenX
+%s: Lesesperrung war nicht moeglichX
+lese ...X
+%s: %lu Zeilen, %lu ZeichenX
+Keine Hintegrundanzeigen vorhandenX
+Das script Kommando ist nur im vi Modus verfuegbarX
+Kein Kommando auszufuehrenX
+shiftwidth Option auf 0 gesetztX
+Count ueberlaufX
+Count unterlaufX
+Regulaerer Ausdruck angegeben; r Flag bedeutungslosX
+Die #, l und p Flags koennen im vi Modus nicht mit dem c Flag kombiniert werdenX
+Keine Uebereinstimmung gefundenX
+Kein vorhergehender 'tag' angegebenX
+Weniger als %s Eintraege auf dem 'tag'-Stack; verwende :display t[ags]X
+Keine Datei namens %s auf dem 'tag'-Stack; verwende :display t[ags]X
+Druecke Enter um fortzufahren: X
+%s: 'tag' nicht gefundenX
+%s: kaputter 'tag' in %sX
+%s: die Zeilennummer des 'tag' ist hinter dem DateiendeX
+Der 'tag' Stack ist leerX
+%s: Suchmuster nicht gefundenX
+%d weitere Dateien zu edierenX
+Puffer %s ist leerX
+Bestaetige Aenderung [n]X
+Unterbrochen.X
+Kein vorhergehender Puffer zum AusfuehrenX
+Kein vorhergehender regulaerer AusdruckX
+Das Kommando %s verlangt, dass bereits eine Datei eingelesen wurdeX
+Benutzung: %sX
+Das visual Kommando verlangt dass die open Option gesetzt istX
+
+Leere DateiX
+Keine vorhergehende F, f, T oder t SucheX
+%s nicht gefundenX
+Keine vorhergehende Datei zu edierenX
+Cursor nicht auf einer ZahlX
+Sich ergebende Zahl ist zu grossX
+Sich ergebende Zahl ist zu kleinX
+Kein korrespondierendes Zeichen in dieser ZeileX
+Korrespondierendes Zeichen nicht gefundenX
+Keine Zeichen zu ersetzenX
+Keine andere Anzeige zum HinschaltenX
+Zeichen nach Suchmuster, Zeilenoffset und/oder z KommandoX
+Kein altes SuchmusterX
+Suche zur urspruenglichen Position umgeschlagenX
+Abkuerzung ueberschreitet Expansionsgrenze: Zeichen weggelassenX
+Nicht erlaubtes Zeichen; maskiere zum EingebenX
+Bereits am Anfang der EingabeX
+Keine weiteren Zeichen zu loeschenX
+Bewegung hinter das DateiendeX
+Bewegung hinter das ZeilenendeX
+Keine Cursorbewegung gemachtX
+Bereits am DateianfangX
+Bewegung vor den DateianfangX
+Bereits in der ersten SpalteX
+Puffer sollen vor dem Kommando angegeben werdenX
+Bereits am DateiendeX
+Bereits am ZeilenendeX
+%s ist kein vi KommandoX
+Benutzung: %sX
+Keine Zeichen zu loeschenX
+Das Q Kommando benoetigt das ex Terminal InterfaceX
+Kein Kommando zu wiederholenX
+Die Datei ist leerX
+%s kann nicht als Bewegungskommando verwendet werdenX
+Bereits im Kommando-ModusX
+ Cursor nicht auf einem WortX
+
+Wert der Window Option ist zu gross, max ist %uX
+AnfuegenX
+AendernX
+KommandoX
+EinfuegenX
+ErsetzenX
+Bewegung hinter das AnzeigenendeX
+Bewegung vor den AnzeigenanfangX
+Anzeige muss zum Zerlegen groesser als %d seinX
+Keine Hintergrundanzeigen vorhandenX
+Es gibt keine Hintergrundanzeige die eine Datei namens %s ediertX
+Die einzige dargestellte Anzeige kann nicht in den Hintergrund gebracht werdenX
+Die Anzeige kann nur auf %d Zeilen verkleinert werdenX
+Die Anzeige kann nicht verkleinert werdenX
+Die Anzeige kann nicht vergroessert werdenX
+
+Diese Anzeige kann nicht gestopped werdenX
+Unterbrochen: umdefinierte Tasten ungueltigX
+vi: temporaerer Puffer nicht freigegebenX
+Dieses Terminal hat keine %s TasteX
+es kann nur ein Puffer angegeben werdenX
+Zahl groesser als %luX
+UnterbrochenX
+Nicht moeglich temporaere Datei anzulegenX
+Warnung: %s ist keine regulaere DateiX
+%s ist bereits gesperrt, nur-lese EditorsitzungX
+%s: loeschenX
+%s: schliessenX
+%s: loeschenX
+%s: loeschenX
+Nur-lese Datei, nicht geschrieben; verwende ! zum UeberschreibenX
+Nur-lese Datei, nicht geschriebenX
+%s existiert, nicht geschrieben; verwende ! zum UeberschreibenX
+%s existiert, nicht geschriebenX
+Teil der Datei, nicht geschrieben; verwende ! zum UeberschreibenX
+Teil der Datei, nicht geschriebenX
+%s: Datei wurde spaeter als diese Kopie veraendert; verwende ! zum UeberschreibenX
+%s: Datei wurde spaeter als diese Kopie veraendertX
+%s: Schreibsperrung war nicht moeglichX
+schreibe ...X
+%s: WARNUNG: DATEI ABGESCHNITTENX
+Bereits am ersten 'tag' dieser GruppeX
+%s: neue Datei: %lu Zeilen, %lu ZeichenX
+%s: %lu Zeilen, %lu ZeichenX
+%s wurde in zu viele Dateinamen expandiertX
+%s: keine regulaere DateiX
+%s: gehoehrt nicht IhnenX
+%s: anderer Benutzer als Eigentuemer hat ZugriffX
+Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorierenX
+Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende :edit! zum ignorierenX
+Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorierenX
+Datei ist temporaer; beim Verlassen gehen die Aenderungen verlorenX
+Nur-lese Datei, Aenderungen werden nicht automatisch geschriebenX
+Portokollierung neu begonnenX
+bestaetigen [ynq]X
+Druecke beliebige Taste um fortzufahrenX
+Druecke beliebige Taste um fortzufahren [: um weitere Kommandos einzugeben]: X
+Druecke beliebige Taste um fortzufahren [q zum Verlassen]: X
+Diese Form von %s benoetigt das ex Terminal-InterfaceX
+Gehe in ex Eingabe-Modus.\nX
+Kommando schief gelaufen, noch keine Datei eingelesenX
+ weiter?X
+unerwartetes Zeichen - EreignisX
+unerwartetes Dateiende - EreignisX
+Keine Position zum Anspringen gefundenX
+unerwartetes Unterbrechungs - EreignisX
+unerwartetes Verlassen - EreignisX
+unerwartetes Wiederherstellungs - EreignisX
+Bereits am letzten 'tag' dieser GruppeX
+Das %s Kommando benoetigt das ex Terminal-InterfaceX
+Diese Form von %s wird nicht unterstuetzt wenn die 'secure edit' - Option gesetzt istX
+unerwartetes Zeichenketten - EreignisX
+unerwartetes timeout - EreignisX
+unerwartetes Schreibe - EreignisX
+
+Shell Expandierungen nicht unterstuetzt wenn die 'secure edit' - Option gesetzt istX
+Das %s Kommando wird nicht unterstuetzt wenn die 'secure edit' - Option gesetzt istX
+set: %s kann nicht ausgeschaltet werdenX
+Anzeige zu klein.X
+angefuegtX
+geaendertX
+geloeschtX
+verbundenX
+verschobenX
+geschobenX
+in Puffer geschriebenX
+ZeileX
+ZeilenX
+Vi wurde nicht mit dem Tcl Interpreter gelinktX
+Datei wurde seit dem letzten Schreiben veraendert.X
+Shell Expansion nicht geklapptX
+Es ist keine %s Edieroption angegebenX
+Vi wurde nicht mit einem Perl Interpreter geladenX
+Kein ex Kommando auszufuehrenX
+Druecke <CR> um ein Kommando auszufuehren, :q zum verlassenX
+Verwende "cscope help" fuer HilestellungX
+Keine cscope Verbindung aktivX
+%s: unbekannter Suchtyp: verwende einen aus %sX
+%d: keine solche cscope VerbindungX
+set: die %s Option kann nicht eingeschaltet werdenX
+set: die %s Option kann nicht auf 0 gesetzt werdenX
+%s: angefuegt: %lu Zeilen, %lu ZeichenX
+unerwartetes Groessenveraenderungs - EreignisX
+%d Dateien zu edierenX
diff --git a/contrib/nvi/catalog/german.base b/contrib/nvi/catalog/german.base
new file mode 100644
index 000000000000..135b72da7994
--- /dev/null
+++ b/contrib/nvi/catalog/german.base
@@ -0,0 +1,307 @@
+002 "Zeilenlaengen Ueberlauf"
+003 "kann Zeile %lu nicht loeschen"
+004 "kann an Zeile %lu nicht anfuegen"
+005 "kann in Zeile %lu nicht einfuegen"
+006 "kann Zeile %lu nicht speichern"
+007 "kann letzte Zeile nicht lesen"
+008 "Fehler: kann Zeile %lu nicht wiederherstellen"
+009 "Protokolldatei"
+010 "Keine Protokollierung aktiv, rueckgaengig machen nicht moeglich"
+011 "Keine Aenderungen rueckgaengig zu machen"
+012 "Keine Protokollierung aktiv, rueckgaengig machen nicht moeglich"
+013 "Keine Protokollierung aktiv, Wiederholung von Aenderungen nicht moeglich"
+014 "Keine Aenderungen zu wiederholen"
+015 "%s/%d: Protokollschreibfehler"
+016 "Vi's Standardein- und ausgabe muss ein Terminal sein"
+017 "Marke %s: nicht gesetzt"
+018 "Marke %s: die Zeile wurde geloescht"
+019 "Marke %s: Cursorposition existiert nicht mehr"
+020 "Fehler: "
+021 "neue Datei"
+022 "Name geaendert"
+023 "geaendert"
+024 "nicht geaendert"
+025 "NICHT GELOCKED"
+026 "nur zum Lesen"
+027 "Zeile %lu von %lu [%ld%%]"
+028 "leere Datei"
+029 "Zeile %lu"
+030 "Die Datei %s ist kein Meldungskatalog"
+031 "Setzen der Voreinstellung fuer %s Option nicht moeglich"
+032 "Benutzung: %s"
+033 "set: keine %s Option: 'set all' zeigt alle Optionen mit Werten an"
+034 "set: der [no]%s Option kann kein Wert zugewiesen werden"
+035 "set: %s ist keine boolsche Option"
+036 "set: %s Option: %s"
+037 "set: %s Option: %s: Wert Ueberlauf"
+038 "set: %s Option: %s ist eine ungueltige Zahl"
+039 "set: %s ist keine boolsche Option"
+040 "Anzeige hat zu wenig Spalten, weniger als %d"
+041 "Anzeige hat zu viele Spalten, mehr als %d"
+042 "Anzeige hat zu wenig Zeilen, weniger als %d"
+043 "Anzeige hat zu viele Zeilen, mehr als %d"
+044 "Die lisp Option ist nicht implementiert"
+045 "Messages nicht abgeschalten: %s"
+046 "Messages nicht eingeschalten: %s"
+048 "Die paragraph Option muss Gruppen zu zwei Zeichen enthalten"
+049 "Die section Option muss Gruppen zu zwei Zeichen enthalten"
+053 "Der Standardpuffer ist leer"
+054 "Puffer %s ist leer"
+055 "Dateien mit newlines im Namen sind nicht wiederherstellbar"
+056 "Aenderungen nicht wiederherstellbar falls die Editorsitzung schief geht"
+057 "kopiere Datei fuer Wiederherstellung ..."
+058 "Rettungsmechanismus funktioniert nicht: %s"
+059 "Aenderungen nicht wiederherstellbar falls die Editorsitzung schief geht"
+060 "Sicherung der Datei gescheitert: %s"
+061 "kopiere Datei fuer Wiederherstellung ..."
+062 "Informationen ueber den Benutzer mit id %u nicht gefunden"
+063 "Wiederherstellungsdatei kann nicht gesperrt werden"
+064 "Puffer der Wiederherstellungsdatei uebergelaufen"
+065 "Wiederherstellungsdatei"
+066 "%s: Wiederherstellungsdatei hat falsches Format"
+067 "%s: Wiederherstellungsdatei hat falsches Format"
+068 "Keine von Ihnen lesbaren Dateien mit Namen %s zum Wiederherstellen"
+069 "Es gibt aeltere Versionen dieser Datei von Ihnen zum Wiederherstellen"
+070 "Sie haben noch andere Dateien zum Wiederherstellen"
+071 "schicke keine email: %s"
+072 "Datei leer; nichts zu suchen"
+073 "Dateiende erreicht, ohne das Suchmuster zu finden"
+074 "Kein altes Suchmuster"
+075 "Suchmuster nicht gefunden"
+076 "Dateianfang erreicht, ohne das Suchmuster zu finden"
+077 "Suche umgeschlagen"
+078 "suche ..."
+079 "Keine nichtdruckbaren Zeichen gefunden"
+080 "Unbekannter Kommandoname"
+082 "%s: Kommando im ex Modus nicht verfuegbar"
+083 "Count darf nicht Null sein"
+084 "%s: falsche Zeilenangabe"
+085 "Interner Syntaxtabellenfehler (%s: %s)"
+086 "Benutzung: %s"
+087 "%s: temporaerer Puffer nicht freigegeben"
+088 "Flagoffset vor Zeile 1"
+089 "Flagoffset hinter dem Dateiende"
+090 "@ mit Bereich lief als Datei/Anzeige geaendert wurde"
+091 "globales oder v-Kommando lief als Datei/Anzeige geaendert wurde"
+092 "Ex Kommando misslungen: restliche Kommandoeingabe ignoriert"
+093 "Ex Kommando misslungen: umdefinierte Tasten ungueltig"
+094 "Die zweite Adresse ist kleiner als die erste"
+095 "Kein Markenname angegeben"
+096 "\\ ohne folgenden / oder ?"
+097 "Referenz auf eine negative Zeilennummer"
+098 "Das Kommando %s ist unbekannt"
+099 "Adresswert zu gross"
+100 "Adresswert zu klein"
+101 "Ungueltige Adresskombination"
+102 "Ungueltige Adresse: nur %lu Zeilen in der Datei"
+103 "Ungueltige Adresse: die Datei ist leer"
+104 "Das Kommando %s erlaubt keine Adresse 0"
+105 "Keine Abkuerzungen zum Anzeigen"
+106 "Abkuerzungen muessen mit einem "Wort"-Zeichen enden"
+107 "Abkuerzungen duerfen keine Tabulator- oder Leerzeichen enthalten"
+108 "In Abkuerzungen duerfen ausser am Ende Wort- und Nicht-Wort-Zeichen nicht gemischt werden"
+109 ""%s" ist keine Abkuerzung"
+110 "Vi Kommando misslungen: umdefinierte Tasten ungueltig"
+111 "Keine weiteren Dateien zu edieren"
+112 "Keine vorhergehenden Dateien zu edieren"
+113 "Keine vorhergehenden Dateien fuer rewind"
+114 "Keine Dateiliste zum Anzeigen"
+115 "Kein vorhergehendes Kommando um "!" zu ersetzen"
+116 "Kein Dateiname fuer %%"
+117 "Kein Dateiname fuer #"
+118 "Fehler: execl: %s"
+119 "I/O Fehler: %s"
+120 "Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorieren"
+121 "Kann Homedirectory nicht bestimmen"
+122 "Neues aktuelles Directory: %s"
+123 "Keine Puffer anzuzeigen"
+124 "Das Kommando %s kann nicht als Teil eines global oder v Kommandos verwendet werden"
+125 "%s/%s: nicht gelesen: gehoehrt weder Ihnen noch root"
+126 "%s/%s: nicht gelesen: gehoehrt nicht Ihnen"
+127 "%s/%s: nicht gelesen: anderer Benutzer als Eigentuemer hat Schreibrecht"
+128 "%s: nicht gelesen: gehoehrt weder Ihnen noch root"
+129 "%s: nicht gelesen: gehoehrt nicht Ihnen"
+130 "%s: nicht gelesen: anderer Benutzer als Eigentuemer hat Schreibrecht"
+131 "Keine folgenden Zeilen zum Verbinden"
+132 "Kein Eingabe-Map Eintrag"
+133 "Kein Kommando-Map Eintrag"
+134 "Das %s Zeichen kann nicht umgemappt werden"
+135 ""%s" ist momentan nicht gemappt"
+136 "Markennamen muessen einzelne Buchstaben sein"
+137 "%s existiert, nicht geschrieben; verwende ! zum Ueberschreiben"
+138 "Neue .exrc Datei: %s. "
+139 "Zielzeile ist innerhalb des Verschiebebereichs"
+140 "Das open Kommando verlangt, dass die open Option gesetzt ist"
+141 "Das open Kommando ist nocht nicht implementiert"
+142 "Rettung dieser Datei nicht moeglich"
+143 "Datei gerettet"
+144 "%s wurde in zu viele Dateinamen expandiert"
+145 "Nur regulaere Dateien und named pipes koennen gelesen werden"
+146 "%s: Lesesperrung war nicht moeglich"
+147 "lese ..."
+148 "%s: %lu Zeilen, %lu Zeichen"
+149 "Keine Hintegrundanzeigen vorhanden"
+150 "Das script Kommando ist nur im vi Modus verfuegbar"
+151 "Kein Kommando auszufuehren"
+152 "shiftwidth Option auf 0 gesetzt"
+153 "Count ueberlauf"
+154 "Count unterlauf"
+155 "Regulaerer Ausdruck angegeben; r Flag bedeutungslos"
+156 "Die #, l und p Flags koennen im vi Modus nicht mit dem c Flag kombiniert werden"
+157 "Keine Uebereinstimmung gefunden"
+158 "Kein vorhergehender 'tag' angegeben"
+159 "Weniger als %s Eintraege auf dem 'tag'-Stack; verwende :display t[ags]"
+160 "Keine Datei namens %s auf dem 'tag'-Stack; verwende :display t[ags]"
+161 "Druecke Enter um fortzufahren: "
+162 "%s: 'tag' nicht gefunden"
+163 "%s: kaputter 'tag' in %s"
+164 "%s: die Zeilennummer des 'tag' ist hinter dem Dateiende"
+165 "Der 'tag' Stack ist leer"
+166 "%s: Suchmuster nicht gefunden"
+167 "%d weitere Dateien zu edieren"
+168 "Puffer %s ist leer"
+169 "Bestaetige Aenderung [n]"
+170 "Unterbrochen."
+171 "Kein vorhergehender Puffer zum Ausfuehren"
+172 "Kein vorhergehender regulaerer Ausdruck"
+173 "Das Kommando %s verlangt, dass bereits eine Datei eingelesen wurde"
+174 "Benutzung: %s"
+175 "Das visual Kommando verlangt dass die open Option gesetzt ist"
+177 "Leere Datei"
+178 "Keine vorhergehende F, f, T oder t Suche"
+179 "%s nicht gefunden"
+180 "Keine vorhergehende Datei zu edieren"
+181 "Cursor nicht auf einer Zahl"
+182 "Sich ergebende Zahl ist zu gross"
+183 "Sich ergebende Zahl ist zu klein"
+184 "Kein korrespondierendes Zeichen in dieser Zeile"
+185 "Korrespondierendes Zeichen nicht gefunden"
+186 "Keine Zeichen zu ersetzen"
+187 "Keine andere Anzeige zum Hinschalten"
+188 "Zeichen nach Suchmuster, Zeilenoffset und/oder z Kommando"
+189 "Kein altes Suchmuster"
+190 "Suche zur urspruenglichen Position umgeschlagen"
+191 "Abkuerzung ueberschreitet Expansionsgrenze: Zeichen weggelassen"
+192 "Nicht erlaubtes Zeichen; maskiere zum Eingeben"
+193 "Bereits am Anfang der Eingabe"
+194 "Keine weiteren Zeichen zu loeschen"
+195 "Bewegung hinter das Dateiende"
+196 "Bewegung hinter das Zeilenende"
+197 "Keine Cursorbewegung gemacht"
+198 "Bereits am Dateianfang"
+199 "Bewegung vor den Dateianfang"
+200 "Bereits in der ersten Spalte"
+201 "Puffer sollen vor dem Kommando angegeben werden"
+202 "Bereits am Dateiende"
+203 "Bereits am Zeilenende"
+204 "%s ist kein vi Kommando"
+205 "Benutzung: %s"
+206 "Keine Zeichen zu loeschen"
+207 "Das Q Kommando benoetigt das ex Terminal Interface"
+208 "Kein Kommando zu wiederholen"
+209 "Die Datei ist leer"
+210 "%s kann nicht als Bewegungskommando verwendet werden"
+211 "Bereits im Kommando-Modus"
+212 " Cursor nicht auf einem Wort"
+214 "Wert der Window Option ist zu gross, max ist %u"
+215 "Anfuegen"
+216 "Aendern"
+217 "Kommando"
+218 "Einfuegen"
+219 "Ersetzen"
+220 "Bewegung hinter das Anzeigenende"
+221 "Bewegung vor den Anzeigenanfang"
+222 "Anzeige muss zum Zerlegen groesser als %d sein"
+223 "Keine Hintergrundanzeigen vorhanden"
+224 "Es gibt keine Hintergrundanzeige die eine Datei namens %s ediert"
+225 "Die einzige dargestellte Anzeige kann nicht in den Hintergrund gebracht werden"
+226 "Die Anzeige kann nur auf %d Zeilen verkleinert werden"
+227 "Die Anzeige kann nicht verkleinert werden"
+228 "Die Anzeige kann nicht vergroessert werden"
+230 "Diese Anzeige kann nicht gestopped werden"
+231 "Unterbrochen: umdefinierte Tasten ungueltig"
+232 "vi: temporaerer Puffer nicht freigegeben"
+233 "Dieses Terminal hat keine %s Taste"
+234 "es kann nur ein Puffer angegeben werden"
+235 "Zahl groesser als %lu"
+236 "Unterbrochen"
+237 "Nicht moeglich temporaere Datei anzulegen"
+238 "Warnung: %s ist keine regulaere Datei"
+239 "%s ist bereits gesperrt, nur-lese Editorsitzung"
+240 "%s: loeschen"
+241 "%s: schliessen"
+242 "%s: loeschen"
+243 "%s: loeschen"
+244 "Nur-lese Datei, nicht geschrieben; verwende ! zum Ueberschreiben"
+245 "Nur-lese Datei, nicht geschrieben"
+246 "%s existiert, nicht geschrieben; verwende ! zum Ueberschreiben"
+247 "%s existiert, nicht geschrieben"
+248 "Teil der Datei, nicht geschrieben; verwende ! zum Ueberschreiben"
+249 "Teil der Datei, nicht geschrieben"
+250 "%s: Datei wurde spaeter als diese Kopie veraendert; verwende ! zum Ueberschreiben"
+251 "%s: Datei wurde spaeter als diese Kopie veraendert"
+252 "%s: Schreibsperrung war nicht moeglich"
+253 "schreibe ..."
+254 "%s: WARNUNG: DATEI ABGESCHNITTEN"
+255 "Bereits am ersten 'tag' dieser Gruppe"
+256 "%s: neue Datei: %lu Zeilen, %lu Zeichen"
+257 "%s: %lu Zeilen, %lu Zeichen"
+258 "%s wurde in zu viele Dateinamen expandiert"
+259 "%s: keine regulaere Datei"
+260 "%s: gehoehrt nicht Ihnen"
+261 "%s: anderer Benutzer als Eigentuemer hat Zugriff"
+262 "Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorieren"
+263 "Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende :edit! zum ignorieren"
+264 "Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorieren"
+265 "Datei ist temporaer; beim Verlassen gehen die Aenderungen verloren"
+266 "Nur-lese Datei, Aenderungen werden nicht automatisch geschrieben"
+267 "Portokollierung neu begonnen"
+268 "bestaetigen [ynq]"
+269 "Druecke beliebige Taste um fortzufahren"
+270 "Druecke beliebige Taste um fortzufahren [: um weitere Kommandos einzugeben]: "
+271 "Druecke beliebige Taste um fortzufahren [q zum Verlassen]: "
+272 "Diese Form von %s benoetigt das ex Terminal-Interface"
+273 "Gehe in ex Eingabe-Modus.\n"
+274 "Kommando schief gelaufen, noch keine Datei eingelesen"
+275 " weiter?"
+276 "unerwartetes Zeichen - Ereignis"
+277 "unerwartetes Dateiende - Ereignis"
+278 "Keine Position zum Anspringen gefunden"
+279 "unerwartetes Unterbrechungs - Ereignis"
+280 "unerwartetes Verlassen - Ereignis"
+281 "unerwartetes Wiederherstellungs - Ereignis"
+282 "Bereits am letzten 'tag' dieser Gruppe"
+283 "Das %s Kommando benoetigt das ex Terminal-Interface"
+284 "Diese Form von %s wird nicht unterstuetzt wenn die 'secure edit' - Option gesetzt ist"
+285 "unerwartetes Zeichenketten - Ereignis"
+286 "unerwartetes timeout - Ereignis"
+287 "unerwartetes Schreibe - Ereignis"
+289 "Shell Expandierungen nicht unterstuetzt wenn die 'secure edit' - Option gesetzt ist"
+290 "Das %s Kommando wird nicht unterstuetzt wenn die 'secure edit' - Option gesetzt ist"
+291 "set: %s kann nicht ausgeschaltet werden"
+292 "Anzeige zu klein."
+293 "angefuegt"
+294 "geaendert"
+295 "geloescht"
+296 "verbunden"
+297 "verschoben"
+298 "geschoben"
+299 "in Puffer geschrieben"
+300 "Zeile"
+301 "Zeilen"
+302 "Vi wurde nicht mit dem Tcl Interpreter gelinkt"
+303 "Datei wurde seit dem letzten Schreiben veraendert."
+304 "Shell Expansion nicht geklappt"
+305 "Es ist keine %s Edieroption angegeben"
+306 "Vi wurde nicht mit einem Perl Interpreter geladen"
+307 "Kein ex Kommando auszufuehren"
+308 "Druecke <CR> um ein Kommando auszufuehren, :q zum verlassen"
+309 "Verwende "cscope help" fuer Hilestellung"
+310 "Keine cscope Verbindung aktiv"
+311 "%s: unbekannter Suchtyp: verwende einen aus %s"
+312 "%d: keine solche cscope Verbindung"
+313 "set: die %s Option kann nicht eingeschaltet werden"
+314 "set: die %s Option kann nicht auf 0 gesetzt werden"
+315 "%s: angefuegt: %lu Zeilen, %lu Zeichen"
+316 "unerwartetes Groessenveraenderungs - Ereignis"
+317 "%d Dateien zu edieren"
diff --git a/contrib/nvi/catalog/german.check b/contrib/nvi/catalog/german.check
new file mode 100644
index 000000000000..4fd60c14e63b
--- /dev/null
+++ b/contrib/nvi/catalog/german.check
@@ -0,0 +1,36 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+213
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s existiert, nicht geschrieben; verwende ! zum UeberschreibenX
+ 2 %s wurde in zu viele Dateinamen expandiertX
+ 2 %s: %lu Zeilen, %lu ZeichenX
+ 2 %s: Wiederherstellungsdatei hat falsches FormatX
+ 2 Aenderungen nicht wiederherstellbar falls die Editorsitzung schief gehtX
+ 2 Kein altes SuchmusterX
+ 2 Keine Protokollierung aktiv, rueckgaengig machen nicht moeglichX
+ 2 Puffer %s ist leerX
+ 2 geaendertX
+ 2 kopiere Datei fuer Wiederherstellung ...X
+ 2 set: %s ist keine boolsche OptionX
+ 3 %s: loeschenX
+ 3 Datei wurde seit dem letzten vollstaendigen Schreiben geaendert; schreibe oder verwende ! zum ignorierenX
+ 4 Benutzung: %sX
+=========================
diff --git a/contrib/nvi/catalog/german.owner b/contrib/nvi/catalog/german.owner
new file mode 100644
index 000000000000..e72e8ae268fc
--- /dev/null
+++ b/contrib/nvi/catalog/german.owner
@@ -0,0 +1 @@
+Bernhard Daeubler <daeb@physik.uni-ulm.de>
diff --git a/contrib/nvi/catalog/ru_RU.KOI8-R b/contrib/nvi/catalog/ru_RU.KOI8-R
new file mode 100644
index 000000000000..7eb35b9cf262
--- /dev/null
+++ b/contrib/nvi/catalog/ru_RU.KOI8-R
@@ -0,0 +1,267 @@
+VI_MESSAGE_CATALOG
+pEREPOLNENIE ZNA^ENIQ DLINY STROKIX
+%s/%d: NEWOZMOVNO UDALITX STROKU %uX
+%s/%d: NEWOZMOVNO DOBAWITX K STROKE %uX
+%s/%d: NEWOZMOVNO WSTAWITX W STROKU %uX
+%s/%d: NEWOZMOVNO SOHRANITX STROKU %uX
+%s/%d: NEWOZMOVNO DOSTATX POSLEDN@@ STROKUX
+
+fAJL ZAPISEJX
+zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+nET IZMENENIJ DLQ OTMENYX
+zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+zAPISI NE WELISX, NEWOZMOVNO PROSMOTRETX WPEREDX
+nET IZMENENIJ DLQ PEREDELKIX
+%s/%d: O[IBKA PRI ZAPISI PROTOKOLAX
+sTANDARTNYJ WWOD/WYWOD DLQ VI DOLVEN BYTX TERMINALX
+oTMETKA %s: NE USTANOWLENAX
+oTMETKA %s: STROKA BYLA UDALENAX
+oTMETKA %s: POZICII KURSORA BOLX[E NE SU]ESTWUETX
+o[IBKA:X
+
+
+
+
+
+
+
+
+
+fAJL %s NE QWLQETSQ KATALOGOM SOOB]ENIJX
+nEWOZMOVNO USTANOWITX OPCI@ %s PO UMOL^ANI@X
+iSPOLXZOWANIE: %sX
+oPCII %s NET: 'set all' POKAZYWAET WSE WOZMOVNYE OPCIIX
+set: [no]%s NE PRINIMAET TAKOGO ZNA^ENIQX
+set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+
+
+set: NEPRAWILXNOE ZNA^ENIE %sX
+set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+kOLI^ESTWO KOLONOK \KRANA SLI[KOM MALO, MENX[E ^EM %dX
+kOLI^ESTWO KOLONOK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %dX
+kOLI^ESTWO STROK \KRANA SLI[KOM MALO, MENX[E ^EM %dX
+kOLI^ESTWO STROK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %dX
+oPCIQ lisp OTSUTSTWUETX
+sOOB]ENIQ NE WYKL@^ENY: %sX
+sOOB]ENIQ NE WKL@^ENY: %sX
+oPCIQ modeline(s) NE MOVET BYTX PEREUSTANOWLENAX
+oPCIQ paragraph DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMIX
+oPCIQ section DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMIX
+oPCIQ shiftwidth NE MOVET BYTX USTANOWLENA NA 0X
+oPCIQ sourceany NE MOVET BYTX USTANOWLENAX
+tABULQCIQ NE MOVET BYTX USTANOWLENA NA 0X
+sTARTOWYJ BUFER PUSTX
+bUFER %s PUSTX
+fAJLY S SIMWOLAMI PEREWODA STROKI W IMENI NE MOGUT BYTX WOSSTANOWLENYX
+iZMENENIQ NE SOHRANENY PRI KRAHE SESSIIX
+
+sOHRANENIE NE UDALOSX: %sX
+iZMENENIQ NE SOHRANQ@TSQ PRI OBRYWE SESSIIX
+sOHRANENIE KOPII FAJLA NE UDALOSX: %sX
+
+iNFORMACII NA POLXZOWATELQ %u NE NAJDENOX
+nEWOZMOVNO ZA]ITITX SPASENNYJ FAJLX
+bUFER WOSSTANOWLENNOGO FAJLA PEREPOLNENX
+wOSSTANOWLENNYJ FAJLX
+%s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+%s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+fAJLOW S IMENEM %s, KOTORYE wY MOVETE ^ITATX, NE SU]ESTWUETX
+eSTX STARYE WERSII FAJLA, KOTORYE MOVNO WOSSTANOWITXX
+sU]ESTWU@T DRUGIE FAJLY, KOTORYE MOVNO WOSSTANOWITXX
+E-mail NE POSLAN: %sX
+fAJL PUST - ISKATX NE^EGOX
+dOSTIGNUT KONEC FAJLA BEZ NAHOVDENIQ OBRAZCA POISKAX
+nE ZADAN OBRAZEC POISKAX
+oBRAZEC POISKA NE NAJDENX
+dOSTUPNO NA^ALO FAJLA BEZ NAHOVDENIQ OBRAZCA POISKAX
+pOISK ZACIKLENX
+
+nEPE^ATNYH SIMWOLOW NE NAJDENOX
+nEIZWESTNAQ KOMANDAX
+
+kOMANDA NE DOSTUPNA W REVIME exX
+s^ET^IK NE MOVET BYTX NULEMX
+%s: NEPRAWILXNOE UKAZANIE STROKIX
+wNUTRENNQQ O[IBKA W SINTAKSISE (%s: %s)X
+iSPOLXZOWANIE: %sX
+%s: WREMENNYJ BUFER NE ISPOLXZOWANX
+mETKA POSTAWLENA PERED STROKOJ 1X
+mETKA POSTAWLENA POSLE KONCA FAJLAX
+
+
+kOMANDA ex NE UDALASX: PARAMETRY KOMANDY ZABYTYX
+
+wTOROJ ADRES MENX[E ^EM PERWYJX
+nE UKAZANO NAZWANIE OTMETKIX
+\\ NE ZAWER[AETSQ / ILI ?X
+sSYLKA K STROKE S NOMEROM MENX[E 0X
+kOMANDA %s NEIZWESTNAX
+pEREPOLNENIE ZNA^ENIQ ADRESAX
+nEDOBOR ZNA^ENIQ ADRESAX
+nEDOPUSTIMAQ KOMBINACIQ W ADRESEX
+nEPRAWILXNYJ ADRES: WSEGO %lu STROK W FAJLEX
+nEPRAWILXNYJ ADRES: FAJL PUSTX
+kOMMANDA %s NE MOVET ISPOLXZOWATX ADRES 0X
+aBBREWIATURY OTSUTSTWU@TX
+aBBREWIATURY DOLVNY ZAKAN^IWATXSQ SIMWOLOM "word"X
+aBBREWIATURY NE MOGUT SODERVATX SIMWOLOY TABLQCII ILI PROBELYX
+aBBREWIATURY NE MOGUT SO^ETATXSQ S SIMWOLAMI SLOW/NE-SLOW, ZA ISKL@^ENIEM KONCA STROKIX
+"%s" NE QWLQETSQ ABBREWIATUROJX
+
+fAJLOW DLQ REDAKTIROWANIQ BOLX[E NETX
+oTSUTSTWIE PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQX
+oTSUTSTWIE PREDYDU]EGO FAJLA DLQ PROSMOTRA NAZADX
+nET FAJLOWX
+oTSUTSTWIE PREDYDU]EJ KOMANDY DLQ ZAMENY "!"X
+oTSUTSTWIE ZAMENY DLQ %%X
+oTSUTSTWIE ZAMENY DLQ #X
+o[IBKA: execl: %sX
+o[IBKA WWODA/WYWODA: %sX
+fAJL IZMENEN S MOMENTA POSLEDNEJ POLNOJ ZAPISI: ISPOLXZUJTE ! DLQ OBHODAX
+nEWOZMOVNO NAJTI DOMA[NIJ KATALOGX
+nOWYJ KATALOG: %sX
+nET WYREZANYH BUFEROWX
+kOMANDA %s NE MOVET BYTX ISPOLXZOWANA WNUTRI OB]EJ KOMANDYX
+%s/%s: NE OTKRYT: NE PRINADLEVIT wAM ILI root-UX
+%s/%s: NE OTKRYT: NE PRINADLEVIT wAMX
+%s/%s: NE OTKRYT: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEMX
+%s/%s: NE S^ITAN: NE PRINADLEVIT wAM ILI root-UX
+%s/%s: NE S^ITAN: NE PRINADLEVIT wAMX
+%s/%s: NE S^ITAN: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEMX
+pOSLEDU@]IE STROKI OTSUTSTWU@TX
+oTSUTSTWIE PARAMETROW WWODAX
+oTSUTSTWIE PARAMETROW KOMANDYX
+sIMWOL %s NE MOVET BYTX PEREZAPOMNENX
+"%s" NA DANNYJ MOMENT NE OTME^ENX
+iMQ METKI DOLVNO BYTX ODNIM SIMWOLOMX
+%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+nOWYJ FAJL .exrc: %sX
+sTROKA PERENOSA NAHODITSQ WNUTRI PARAMETROW PERENOSAX
+kOMANDA open PODRAZUMEWAET USTANOWKU OPCII openX
+kOMANDA open NE REALIZOWANAX
+zA]ITA FAJLA NEWOZMOVNAX
+fAJL ZA]I]ENX
+%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+
+%s: ZA]ITA NA ^TENIE BYLA NEDOSTUPNAX
+
+%s: %lu STROK, %lu SIMWOLOWX
+nET TENEWYH OKONX
+kOMANDA script ISPOLXZUETSQ TOLXKO W REVIME viX
+nET KOMANDY DLQ ISPOLNENIQX
+oPCIQ shiftwidth USTANOWLENA NA 0X
+pEREPOLNENIE S^ET^IKAX
+cIKL WYPOLNEN NE DO KONCAX
+uKAZANO REGULQRNOE WYRAVENIE: FLAG r NE NUVENX
+fLAGI #, l I p NE MOGUT BYTX OB_EDINENY S FLAGOM c W REVIME viX
+sOWPADENIJ NETX
+mETKA OTSUTSTWUETX
+w STEKE METOK ZAPISEJ MENX[E, ^EM %s, ISPOLXZUJTE :display t[ags]X
+fAJLA S IMENEM %s W STEKE METOK NET; ISPOLXZUJTE :display t[ags]X
+
+%s: METKA NE NAJDENAX
+%s: PLOHAQ METKA W %sX
+
+sTEK METOK PUSTX
+%s: ISKOMAQ PEREMENNAQ NE NAJDENAX
+
+bUFER %s PUSTX
+
+pRERWANOX
+oTSUTSTWIE BUFERA DLQ ISPOLXZOWANIQX
+nET PREDIDU]EGO REGULQRNOGO WYRAVENIQX
+kOMANDA %s PODRAZUMEWAET NALI^IE PRO^TENNOGO FAJLAX
+iSPOLXZOWANIE: %sX
+kOMANDA visual PODRAZUMEWAET OBQZATELXNU@ USTANOWKU OPCII openX
+%s RAS[IRILSQ DO SLI[KOM BOLX[OGO KOLI^ESTWA FAJLOWX
+pUSTOJ FAJLX
+nET PREDYDU]EGO POISKA F, f, T, ILI tX
+%s NE NAJDENOX
+nET PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQX
+kURSOR STOIT NE NA CIFREX
+pOLU^ENNOE ^ISLO SLI[KOM WELIKOX
+pOLU^ENNOE ^ISLO SLI[KOM MALOX
+pODHODQ]EGO SIMWOLA NET NA \TOJ STROKEX
+pODHODQ]IJ SIMWOL NE NAJDENX
+nET SIMWOLOW DLQ UDALENIQX
+dRUGOGO \KRANA NE SU]ESTWUETX
+sIMWOLY POSLE STROKI DLQ POISKA I/ILI PEREBOR STROKIX
+pRO[LYJ OBRAZEC POISKA OTSUTSTWUETX
+pOISK ZAWER[ILSQ NA NA^ALXNOJ POZICIIX
+
+sIMWOL NEPRAWILEN; ZAKL@^EN W KAWY^KI DLQ WWODAX
+uVE NA NA^ALE WSTAWKIX
+nET SIMWOLOW DLQ UDALENIQX
+pEREDWIVENIE ZA KONEC FAJLAX
+pEREDWIVENIE ZA KONEC STROKIX
+dWIVENIE STROKI NE SDELANOX
+uVE NA NA^ALE FAJLAX
+dWIVENIE KURSORA ZA NA^ALO FAJLAX
+uVE W PERWOJ KOLONKEX
+bUFERY DOLVNY BYTX UKAZANY DO WYPOLNENIQ KOMANDYX
+uVE NA KONCE FAJLAX
+uVE NA KONSE STROKIX
+%s NE QWLQETSQ KOMANDOJ VIX
+iSPOLXZOWANIE: %sX
+nET SIMWOLOW DLQ UDALENIQX
+
+nET KOMANDY DLQ POWTORAX
+
+kOMANDA %s NE MOVET BYTX ISPOLXZOWANA KAK KOMANDA PRODWIVENIQX
+~ISLO BOLX[E ^EM %luX
+
+
+zNA^ENIE KOLI^ESTWA OKON SLI[KOM WELIKO, MAKSIMALXNOE ZNA^ENIE = %uX
+
+
+
+
+
+dWIVENIE KURSORA ZA KONEC \KRANAX
+dWIVENIE KURSORA ZA NA^ALO \KRANAX
+
+tENEWYH OKON NETX
+nE SU]ESTWUET TENEWOGO OKNA S REDAKTIROWANIEM FAJLA %sX
+wY NE MOVETE SDELATX EDINSTWENNOE OKNO TENEWYMX
+|KRAN MOVET BYTX SVATX
+|KRAN NE MOVET BYTX SVATX
+|KRAN NE MOVET BYTX RAS[IRENX
+
+
+
+
+dANNYJ TIP TERMINALA NE IMEET KLAWI[I %sX
+
+
+
+nEWOZMOVNO SOZDATX WREMENNYJ FAJLX
+wNIMANIE: %s SPECIALXNYJ FAJLX
+%s UVE ZABLOKIROWAN, DOSTUPEN TOLXKO NA ^TENIEX
+%s: UDALENX
+%s: ZAKRYTX
+%s: UDALENX
+%s: UDALENX
+fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN: iSPOLXZUJTE ! DLQ OBHODAX
+fAJL TOLXKO DLQ ^TENIQ, NE ZAPISANX
+%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+%s SU]ESTWUET, NE ZAPISANX
+iSPOLXZUJTE ! DLQ ^ASTI^NOJ ZAPISI FAJLAX
+~ASTX FAJLA, FAJL NE ZAPISANX
+%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ: ISPOLXZUJTE ! DLQ OBHODAX
+%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQX
+%s: ZA]ITA NA ZAPISX BYLA NEDOSTUPNAX
+
+%s: wnimanie: fajl use~enX
+
+%s: NOWYJ FAJL: %lu STROK, %lu SIMWOLOWX
+%s: %lu STROK, %lu SIMWOLOWX
+%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+%s SPECIALXNYJ FAJLX
+%s: NE PRINADLEVIT wAMX
+%s: DOSTUPEN NE TOLXKO wAMX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE :edit DLQ OBHODAX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+fAJL WREMENNYJ: WYHOD SOTRET L@BYE IZMENENIQX
+
+zAPISI NA^ATY ZANOWOX
diff --git a/contrib/nvi/catalog/ru_RU.KOI8-R.base b/contrib/nvi/catalog/ru_RU.KOI8-R.base
new file mode 100644
index 000000000000..59efd3f53a5f
--- /dev/null
+++ b/contrib/nvi/catalog/ru_RU.KOI8-R.base
@@ -0,0 +1,219 @@
+002 "pEREPOLNENIE ZNA^ENIQ DLINY STROKI"
+003 "%s/%d: NEWOZMOVNO UDALITX STROKU %u"
+004 "%s/%d: NEWOZMOVNO DOBAWITX K STROKE %u"
+005 "%s/%d: NEWOZMOVNO WSTAWITX W STROKU %u"
+006 "%s/%d: NEWOZMOVNO SOHRANITX STROKU %u"
+007 "%s/%d: NEWOZMOVNO DOSTATX POSLEDN@@ STROKU"
+009 "fAJL ZAPISEJ"
+010 "zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDU"
+011 "nET IZMENENIJ DLQ OTMENY"
+012 "zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDU"
+013 "zAPISI NE WELISX, NEWOZMOVNO PROSMOTRETX WPERED"
+014 "nET IZMENENIJ DLQ PEREDELKI"
+015 "%s/%d: O[IBKA PRI ZAPISI PROTOKOLA"
+016 "sTANDARTNYJ WWOD/WYWOD DLQ VI DOLVEN BYTX TERMINAL"
+017 "oTMETKA %s: NE USTANOWLENA"
+018 "oTMETKA %s: STROKA BYLA UDALENA"
+019 "oTMETKA %s: POZICII KURSORA BOLX[E NE SU]ESTWUET"
+020 "o[IBKA:"
+030 "fAJL %s NE QWLQETSQ KATALOGOM SOOB]ENIJ"
+031 "nEWOZMOVNO USTANOWITX OPCI@ %s PO UMOL^ANI@"
+032 "iSPOLXZOWANIE: %s"
+033 "oPCII %s NET: 'set all' POKAZYWAET WSE WOZMOVNYE OPCII"
+034 "set: [no]%s NE PRINIMAET TAKOGO ZNA^ENIQ"
+035 "set: %s OPCIQ NE QWLQETSQ DWOI^NOJ"
+038 "set: NEPRAWILXNOE ZNA^ENIE %s"
+039 "set: %s OPCIQ NE QWLQETSQ DWOI^NOJ"
+040 "kOLI^ESTWO KOLONOK \KRANA SLI[KOM MALO, MENX[E ^EM %d"
+041 "kOLI^ESTWO KOLONOK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %d"
+042 "kOLI^ESTWO STROK \KRANA SLI[KOM MALO, MENX[E ^EM %d"
+043 "kOLI^ESTWO STROK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %d"
+044 "oPCIQ lisp OTSUTSTWUET"
+045 "sOOB]ENIQ NE WYKL@^ENY: %s"
+046 "sOOB]ENIQ NE WKL@^ENY: %s"
+047 "oPCIQ modeline(s) NE MOVET BYTX PEREUSTANOWLENA"
+048 "oPCIQ paragraph DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMI"
+049 "oPCIQ section DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMI"
+050 "oPCIQ shiftwidth NE MOVET BYTX USTANOWLENA NA 0"
+051 "oPCIQ sourceany NE MOVET BYTX USTANOWLENA"
+052 "tABULQCIQ NE MOVET BYTX USTANOWLENA NA 0"
+053 "sTARTOWYJ BUFER PUST"
+054 "bUFER %s PUST"
+055 "fAJLY S SIMWOLAMI PEREWODA STROKI W IMENI NE MOGUT BYTX WOSSTANOWLENY"
+056 "iZMENENIQ NE SOHRANENY PRI KRAHE SESSII"
+058 "sOHRANENIE NE UDALOSX: %s"
+059 "iZMENENIQ NE SOHRANQ@TSQ PRI OBRYWE SESSII"
+060 "sOHRANENIE KOPII FAJLA NE UDALOSX: %s"
+062 "iNFORMACII NA POLXZOWATELQ %u NE NAJDENO"
+063 "nEWOZMOVNO ZA]ITITX SPASENNYJ FAJL"
+064 "bUFER WOSSTANOWLENNOGO FAJLA PEREPOLNEN"
+065 "wOSSTANOWLENNYJ FAJL"
+066 "%s: NE DO KONCA WOSSTANOWLENNYJ FAJL"
+067 "%s: NE DO KONCA WOSSTANOWLENNYJ FAJL"
+068 "fAJLOW S IMENEM %s, KOTORYE wY MOVETE ^ITATX, NE SU]ESTWUET"
+069 "eSTX STARYE WERSII FAJLA, KOTORYE MOVNO WOSSTANOWITX"
+070 "sU]ESTWU@T DRUGIE FAJLY, KOTORYE MOVNO WOSSTANOWITX"
+071 "E-mail NE POSLAN: %s"
+072 "fAJL PUST - ISKATX NE^EGO"
+073 "dOSTIGNUT KONEC FAJLA BEZ NAHOVDENIQ OBRAZCA POISKA"
+074 "nE ZADAN OBRAZEC POISKA"
+075 "oBRAZEC POISKA NE NAJDEN"
+076 "dOSTUPNO NA^ALO FAJLA BEZ NAHOVDENIQ OBRAZCA POISKA"
+077 "pOISK ZACIKLEN"
+079 "nEPE^ATNYH SIMWOLOW NE NAJDENO"
+080 "nEIZWESTNAQ KOMANDA"
+082 "kOMANDA NE DOSTUPNA W REVIME ex"
+083 "s^ET^IK NE MOVET BYTX NULEM"
+084 "%s: NEPRAWILXNOE UKAZANIE STROKI"
+085 "wNUTRENNQQ O[IBKA W SINTAKSISE (%s: %s)"
+086 "iSPOLXZOWANIE: %s"
+087 "%s: WREMENNYJ BUFER NE ISPOLXZOWAN"
+088 "mETKA POSTAWLENA PERED STROKOJ 1"
+089 "mETKA POSTAWLENA POSLE KONCA FAJLA"
+092 "kOMANDA ex NE UDALASX: PARAMETRY KOMANDY ZABYTY"
+094 "wTOROJ ADRES MENX[E ^EM PERWYJ"
+095 "nE UKAZANO NAZWANIE OTMETKI"
+096 "\\ NE ZAWER[AETSQ / ILI ?"
+097 "sSYLKA K STROKE S NOMEROM MENX[E 0"
+098 "kOMANDA %s NEIZWESTNA"
+099 "pEREPOLNENIE ZNA^ENIQ ADRESA"
+100 "nEDOBOR ZNA^ENIQ ADRESA"
+101 "nEDOPUSTIMAQ KOMBINACIQ W ADRESE"
+102 "nEPRAWILXNYJ ADRES: WSEGO %lu STROK W FAJLE"
+103 "nEPRAWILXNYJ ADRES: FAJL PUST"
+104 "kOMMANDA %s NE MOVET ISPOLXZOWATX ADRES 0"
+105 "aBBREWIATURY OTSUTSTWU@T"
+106 "aBBREWIATURY DOLVNY ZAKAN^IWATXSQ SIMWOLOM "word""
+107 "aBBREWIATURY NE MOGUT SODERVATX SIMWOLOY TABLQCII ILI PROBELY"
+108 "aBBREWIATURY NE MOGUT SO^ETATXSQ S SIMWOLAMI SLOW/NE-SLOW, ZA ISKL@^ENIEM KONCA STROKI"
+109 ""%s" NE QWLQETSQ ABBREWIATUROJ"
+111 "fAJLOW DLQ REDAKTIROWANIQ BOLX[E NET"
+112 "oTSUTSTWIE PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQ"
+113 "oTSUTSTWIE PREDYDU]EGO FAJLA DLQ PROSMOTRA NAZAD"
+114 "nET FAJLOW"
+115 "oTSUTSTWIE PREDYDU]EJ KOMANDY DLQ ZAMENY "!""
+116 "oTSUTSTWIE ZAMENY DLQ %%"
+117 "oTSUTSTWIE ZAMENY DLQ #"
+118 "o[IBKA: execl: %s"
+119 "o[IBKA WWODA/WYWODA: %s"
+120 "fAJL IZMENEN S MOMENTA POSLEDNEJ POLNOJ ZAPISI: ISPOLXZUJTE ! DLQ OBHODA"
+121 "nEWOZMOVNO NAJTI DOMA[NIJ KATALOG"
+122 "nOWYJ KATALOG: %s"
+123 "nET WYREZANYH BUFEROW"
+124 "kOMANDA %s NE MOVET BYTX ISPOLXZOWANA WNUTRI OB]EJ KOMANDY"
+125 "%s/%s: NE OTKRYT: NE PRINADLEVIT wAM ILI root-U"
+126 "%s/%s: NE OTKRYT: NE PRINADLEVIT wAM"
+127 "%s/%s: NE OTKRYT: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEM"
+128 "%s/%s: NE S^ITAN: NE PRINADLEVIT wAM ILI root-U"
+129 "%s/%s: NE S^ITAN: NE PRINADLEVIT wAM"
+130 "%s/%s: NE S^ITAN: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEM"
+131 "pOSLEDU@]IE STROKI OTSUTSTWU@T"
+132 "oTSUTSTWIE PARAMETROW WWODA"
+133 "oTSUTSTWIE PARAMETROW KOMANDY"
+134 "sIMWOL %s NE MOVET BYTX PEREZAPOMNEN"
+135 ""%s" NA DANNYJ MOMENT NE OTME^EN"
+136 "iMQ METKI DOLVNO BYTX ODNIM SIMWOLOM"
+137 "%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODA"
+138 "nOWYJ FAJL .exrc: %s"
+139 "sTROKA PERENOSA NAHODITSQ WNUTRI PARAMETROW PERENOSA"
+140 "kOMANDA open PODRAZUMEWAET USTANOWKU OPCII open"
+141 "kOMANDA open NE REALIZOWANA"
+142 "zA]ITA FAJLA NEWOZMOVNA"
+143 "fAJL ZA]I]EN"
+144 "%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOW"
+146 "%s: ZA]ITA NA ^TENIE BYLA NEDOSTUPNA"
+148 "%s: %lu STROK, %lu SIMWOLOW"
+149 "nET TENEWYH OKON"
+150 "kOMANDA script ISPOLXZUETSQ TOLXKO W REVIME vi"
+151 "nET KOMANDY DLQ ISPOLNENIQ"
+152 "oPCIQ shiftwidth USTANOWLENA NA 0"
+153 "pEREPOLNENIE S^ET^IKA"
+154 "cIKL WYPOLNEN NE DO KONCA"
+155 "uKAZANO REGULQRNOE WYRAVENIE: FLAG r NE NUVEN"
+156 "fLAGI #, l I p NE MOGUT BYTX OB_EDINENY S FLAGOM c W REVIME vi"
+157 "sOWPADENIJ NET"
+158 "mETKA OTSUTSTWUET"
+159 "w STEKE METOK ZAPISEJ MENX[E, ^EM %s, ISPOLXZUJTE :display t[ags]"
+160 "fAJLA S IMENEM %s W STEKE METOK NET; ISPOLXZUJTE :display t[ags]"
+162 "%s: METKA NE NAJDENA"
+163 "%s: PLOHAQ METKA W %s"
+165 "sTEK METOK PUST"
+166 "%s: ISKOMAQ PEREMENNAQ NE NAJDENA"
+168 "bUFER %s PUST"
+170 "pRERWANO"
+171 "oTSUTSTWIE BUFERA DLQ ISPOLXZOWANIQ"
+172 "nET PREDIDU]EGO REGULQRNOGO WYRAVENIQ"
+173 "kOMANDA %s PODRAZUMEWAET NALI^IE PRO^TENNOGO FAJLA"
+174 "iSPOLXZOWANIE: %s"
+175 "kOMANDA visual PODRAZUMEWAET OBQZATELXNU@ USTANOWKU OPCII open"
+176 "%s RAS[IRILSQ DO SLI[KOM BOLX[OGO KOLI^ESTWA FAJLOW"
+177 "pUSTOJ FAJL"
+178 "nET PREDYDU]EGO POISKA F, f, T, ILI t"
+179 "%s NE NAJDENO"
+180 "nET PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQ"
+181 "kURSOR STOIT NE NA CIFRE"
+182 "pOLU^ENNOE ^ISLO SLI[KOM WELIKO"
+183 "pOLU^ENNOE ^ISLO SLI[KOM MALO"
+184 "pODHODQ]EGO SIMWOLA NET NA \TOJ STROKE"
+185 "pODHODQ]IJ SIMWOL NE NAJDEN"
+186 "nET SIMWOLOW DLQ UDALENIQ"
+187 "dRUGOGO \KRANA NE SU]ESTWUET"
+188 "sIMWOLY POSLE STROKI DLQ POISKA I/ILI PEREBOR STROKI"
+189 "pRO[LYJ OBRAZEC POISKA OTSUTSTWUET"
+190 "pOISK ZAWER[ILSQ NA NA^ALXNOJ POZICII"
+192 "sIMWOL NEPRAWILEN; ZAKL@^EN W KAWY^KI DLQ WWODA"
+193 "uVE NA NA^ALE WSTAWKI"
+194 "nET SIMWOLOW DLQ UDALENIQ"
+195 "pEREDWIVENIE ZA KONEC FAJLA"
+196 "pEREDWIVENIE ZA KONEC STROKI"
+197 "dWIVENIE STROKI NE SDELANO"
+198 "uVE NA NA^ALE FAJLA"
+199 "dWIVENIE KURSORA ZA NA^ALO FAJLA"
+200 "uVE W PERWOJ KOLONKE"
+201 "bUFERY DOLVNY BYTX UKAZANY DO WYPOLNENIQ KOMANDY"
+202 "uVE NA KONCE FAJLA"
+203 "uVE NA KONSE STROKI"
+204 "%s NE QWLQETSQ KOMANDOJ VI"
+205 "iSPOLXZOWANIE: %s"
+206 "nET SIMWOLOW DLQ UDALENIQ"
+208 "nET KOMANDY DLQ POWTORA"
+210 "kOMANDA %s NE MOVET BYTX ISPOLXZOWANA KAK KOMANDA PRODWIVENIQ"
+211 "~ISLO BOLX[E ^EM %lu"
+214 "zNA^ENIE KOLI^ESTWA OKON SLI[KOM WELIKO, MAKSIMALXNOE ZNA^ENIE = %u"
+220 "dWIVENIE KURSORA ZA KONEC \KRANA"
+221 "dWIVENIE KURSORA ZA NA^ALO \KRANA"
+223 "tENEWYH OKON NET"
+224 "nE SU]ESTWUET TENEWOGO OKNA S REDAKTIROWANIEM FAJLA %s"
+225 "wY NE MOVETE SDELATX EDINSTWENNOE OKNO TENEWYM"
+226 "|KRAN MOVET BYTX SVAT"
+227 "|KRAN NE MOVET BYTX SVAT"
+228 "|KRAN NE MOVET BYTX RAS[IREN"
+233 "dANNYJ TIP TERMINALA NE IMEET KLAWI[I %s"
+237 "nEWOZMOVNO SOZDATX WREMENNYJ FAJL"
+238 "wNIMANIE: %s SPECIALXNYJ FAJL"
+239 "%s UVE ZABLOKIROWAN, DOSTUPEN TOLXKO NA ^TENIE"
+240 "%s: UDALEN"
+241 "%s: ZAKRYT"
+242 "%s: UDALEN"
+243 "%s: UDALEN"
+244 "fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN: iSPOLXZUJTE ! DLQ OBHODA"
+245 "fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN"
+246 "%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODA"
+247 "%s SU]ESTWUET, NE ZAPISAN"
+248 "iSPOLXZUJTE ! DLQ ^ASTI^NOJ ZAPISI FAJLA"
+249 "~ASTX FAJLA, FAJL NE ZAPISAN"
+250 "%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ: ISPOLXZUJTE ! DLQ OBHODA"
+251 "%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ"
+252 "%s: ZA]ITA NA ZAPISX BYLA NEDOSTUPNA"
+254 "%s: wnimanie: fajl use~en"
+256 "%s: NOWYJ FAJL: %lu STROK, %lu SIMWOLOW"
+257 "%s: %lu STROK, %lu SIMWOLOW"
+258 "%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOW"
+259 "%s SPECIALXNYJ FAJL"
+260 "%s: NE PRINADLEVIT wAM"
+261 "%s: DOSTUPEN NE TOLXKO wAM"
+262 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODA"
+263 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE :edit DLQ OBHODA"
+264 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODA"
+265 "fAJL WREMENNYJ: WYHOD SOTRET L@BYE IZMENENIQ"
+267 "zAPISI NA^ATY ZANOWO"
diff --git a/contrib/nvi/catalog/ru_RU.KOI8-R.check b/contrib/nvi/catalog/ru_RU.KOI8-R.check
new file mode 100644
index 000000000000..ca77b891051a
--- /dev/null
+++ b/contrib/nvi/catalog/ru_RU.KOI8-R.check
@@ -0,0 +1,169 @@
+Unused message id's (this is okay):
+001
+008
+021
+022
+023
+024
+025
+026
+027
+028
+029
+036
+037
+057
+061
+078
+081
+090
+091
+093
+110
+145
+147
+161
+164
+167
+169
+191
+207
+209
+212
+213
+215
+216
+217
+218
+219
+222
+229
+230
+231
+232
+234
+235
+236
+253
+255
+266
+=========================
+MISSING ERROR MESSAGES (Please add!):
+008
+021
+022
+023
+024
+025
+026
+027
+028
+029
+036
+037
+057
+061
+078
+090
+091
+093
+110
+145
+147
+161
+164
+167
+169
+191
+207
+209
+212
+215
+216
+217
+218
+219
+222
+230
+231
+232
+234
+235
+236
+253
+255
+266
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+=========================
+Extra error messages (just delete them):
+047
+050
+051
+052
+176
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+ 2 %s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+ 2 %s: %lu STROK, %lu SIMWOLOWX
+ 2 %s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+ 2 bUFER %s PUSTX
+ 2 fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+ 2 set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+ 2 zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+ 3 %s: UDALENX
+ 3 nET SIMWOLOW DLQ UDALENIQX
+ 4 iSPOLXZOWANIE: %sX
+=========================
diff --git a/contrib/nvi/catalog/ru_RU.KOI8-R.owner b/contrib/nvi/catalog/ru_RU.KOI8-R.owner
new file mode 100644
index 000000000000..bf4fa180298c
--- /dev/null
+++ b/contrib/nvi/catalog/ru_RU.KOI8-R.owner
@@ -0,0 +1 @@
+Dima Ruban <dima@demos.su>
diff --git a/contrib/nvi/catalog/ru_SU.KOI8-R b/contrib/nvi/catalog/ru_SU.KOI8-R
new file mode 100644
index 000000000000..7eb35b9cf262
--- /dev/null
+++ b/contrib/nvi/catalog/ru_SU.KOI8-R
@@ -0,0 +1,267 @@
+VI_MESSAGE_CATALOG
+pEREPOLNENIE ZNA^ENIQ DLINY STROKIX
+%s/%d: NEWOZMOVNO UDALITX STROKU %uX
+%s/%d: NEWOZMOVNO DOBAWITX K STROKE %uX
+%s/%d: NEWOZMOVNO WSTAWITX W STROKU %uX
+%s/%d: NEWOZMOVNO SOHRANITX STROKU %uX
+%s/%d: NEWOZMOVNO DOSTATX POSLEDN@@ STROKUX
+
+fAJL ZAPISEJX
+zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+nET IZMENENIJ DLQ OTMENYX
+zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+zAPISI NE WELISX, NEWOZMOVNO PROSMOTRETX WPEREDX
+nET IZMENENIJ DLQ PEREDELKIX
+%s/%d: O[IBKA PRI ZAPISI PROTOKOLAX
+sTANDARTNYJ WWOD/WYWOD DLQ VI DOLVEN BYTX TERMINALX
+oTMETKA %s: NE USTANOWLENAX
+oTMETKA %s: STROKA BYLA UDALENAX
+oTMETKA %s: POZICII KURSORA BOLX[E NE SU]ESTWUETX
+o[IBKA:X
+
+
+
+
+
+
+
+
+
+fAJL %s NE QWLQETSQ KATALOGOM SOOB]ENIJX
+nEWOZMOVNO USTANOWITX OPCI@ %s PO UMOL^ANI@X
+iSPOLXZOWANIE: %sX
+oPCII %s NET: 'set all' POKAZYWAET WSE WOZMOVNYE OPCIIX
+set: [no]%s NE PRINIMAET TAKOGO ZNA^ENIQX
+set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+
+
+set: NEPRAWILXNOE ZNA^ENIE %sX
+set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+kOLI^ESTWO KOLONOK \KRANA SLI[KOM MALO, MENX[E ^EM %dX
+kOLI^ESTWO KOLONOK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %dX
+kOLI^ESTWO STROK \KRANA SLI[KOM MALO, MENX[E ^EM %dX
+kOLI^ESTWO STROK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %dX
+oPCIQ lisp OTSUTSTWUETX
+sOOB]ENIQ NE WYKL@^ENY: %sX
+sOOB]ENIQ NE WKL@^ENY: %sX
+oPCIQ modeline(s) NE MOVET BYTX PEREUSTANOWLENAX
+oPCIQ paragraph DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMIX
+oPCIQ section DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMIX
+oPCIQ shiftwidth NE MOVET BYTX USTANOWLENA NA 0X
+oPCIQ sourceany NE MOVET BYTX USTANOWLENAX
+tABULQCIQ NE MOVET BYTX USTANOWLENA NA 0X
+sTARTOWYJ BUFER PUSTX
+bUFER %s PUSTX
+fAJLY S SIMWOLAMI PEREWODA STROKI W IMENI NE MOGUT BYTX WOSSTANOWLENYX
+iZMENENIQ NE SOHRANENY PRI KRAHE SESSIIX
+
+sOHRANENIE NE UDALOSX: %sX
+iZMENENIQ NE SOHRANQ@TSQ PRI OBRYWE SESSIIX
+sOHRANENIE KOPII FAJLA NE UDALOSX: %sX
+
+iNFORMACII NA POLXZOWATELQ %u NE NAJDENOX
+nEWOZMOVNO ZA]ITITX SPASENNYJ FAJLX
+bUFER WOSSTANOWLENNOGO FAJLA PEREPOLNENX
+wOSSTANOWLENNYJ FAJLX
+%s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+%s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+fAJLOW S IMENEM %s, KOTORYE wY MOVETE ^ITATX, NE SU]ESTWUETX
+eSTX STARYE WERSII FAJLA, KOTORYE MOVNO WOSSTANOWITXX
+sU]ESTWU@T DRUGIE FAJLY, KOTORYE MOVNO WOSSTANOWITXX
+E-mail NE POSLAN: %sX
+fAJL PUST - ISKATX NE^EGOX
+dOSTIGNUT KONEC FAJLA BEZ NAHOVDENIQ OBRAZCA POISKAX
+nE ZADAN OBRAZEC POISKAX
+oBRAZEC POISKA NE NAJDENX
+dOSTUPNO NA^ALO FAJLA BEZ NAHOVDENIQ OBRAZCA POISKAX
+pOISK ZACIKLENX
+
+nEPE^ATNYH SIMWOLOW NE NAJDENOX
+nEIZWESTNAQ KOMANDAX
+
+kOMANDA NE DOSTUPNA W REVIME exX
+s^ET^IK NE MOVET BYTX NULEMX
+%s: NEPRAWILXNOE UKAZANIE STROKIX
+wNUTRENNQQ O[IBKA W SINTAKSISE (%s: %s)X
+iSPOLXZOWANIE: %sX
+%s: WREMENNYJ BUFER NE ISPOLXZOWANX
+mETKA POSTAWLENA PERED STROKOJ 1X
+mETKA POSTAWLENA POSLE KONCA FAJLAX
+
+
+kOMANDA ex NE UDALASX: PARAMETRY KOMANDY ZABYTYX
+
+wTOROJ ADRES MENX[E ^EM PERWYJX
+nE UKAZANO NAZWANIE OTMETKIX
+\\ NE ZAWER[AETSQ / ILI ?X
+sSYLKA K STROKE S NOMEROM MENX[E 0X
+kOMANDA %s NEIZWESTNAX
+pEREPOLNENIE ZNA^ENIQ ADRESAX
+nEDOBOR ZNA^ENIQ ADRESAX
+nEDOPUSTIMAQ KOMBINACIQ W ADRESEX
+nEPRAWILXNYJ ADRES: WSEGO %lu STROK W FAJLEX
+nEPRAWILXNYJ ADRES: FAJL PUSTX
+kOMMANDA %s NE MOVET ISPOLXZOWATX ADRES 0X
+aBBREWIATURY OTSUTSTWU@TX
+aBBREWIATURY DOLVNY ZAKAN^IWATXSQ SIMWOLOM "word"X
+aBBREWIATURY NE MOGUT SODERVATX SIMWOLOY TABLQCII ILI PROBELYX
+aBBREWIATURY NE MOGUT SO^ETATXSQ S SIMWOLAMI SLOW/NE-SLOW, ZA ISKL@^ENIEM KONCA STROKIX
+"%s" NE QWLQETSQ ABBREWIATUROJX
+
+fAJLOW DLQ REDAKTIROWANIQ BOLX[E NETX
+oTSUTSTWIE PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQX
+oTSUTSTWIE PREDYDU]EGO FAJLA DLQ PROSMOTRA NAZADX
+nET FAJLOWX
+oTSUTSTWIE PREDYDU]EJ KOMANDY DLQ ZAMENY "!"X
+oTSUTSTWIE ZAMENY DLQ %%X
+oTSUTSTWIE ZAMENY DLQ #X
+o[IBKA: execl: %sX
+o[IBKA WWODA/WYWODA: %sX
+fAJL IZMENEN S MOMENTA POSLEDNEJ POLNOJ ZAPISI: ISPOLXZUJTE ! DLQ OBHODAX
+nEWOZMOVNO NAJTI DOMA[NIJ KATALOGX
+nOWYJ KATALOG: %sX
+nET WYREZANYH BUFEROWX
+kOMANDA %s NE MOVET BYTX ISPOLXZOWANA WNUTRI OB]EJ KOMANDYX
+%s/%s: NE OTKRYT: NE PRINADLEVIT wAM ILI root-UX
+%s/%s: NE OTKRYT: NE PRINADLEVIT wAMX
+%s/%s: NE OTKRYT: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEMX
+%s/%s: NE S^ITAN: NE PRINADLEVIT wAM ILI root-UX
+%s/%s: NE S^ITAN: NE PRINADLEVIT wAMX
+%s/%s: NE S^ITAN: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEMX
+pOSLEDU@]IE STROKI OTSUTSTWU@TX
+oTSUTSTWIE PARAMETROW WWODAX
+oTSUTSTWIE PARAMETROW KOMANDYX
+sIMWOL %s NE MOVET BYTX PEREZAPOMNENX
+"%s" NA DANNYJ MOMENT NE OTME^ENX
+iMQ METKI DOLVNO BYTX ODNIM SIMWOLOMX
+%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+nOWYJ FAJL .exrc: %sX
+sTROKA PERENOSA NAHODITSQ WNUTRI PARAMETROW PERENOSAX
+kOMANDA open PODRAZUMEWAET USTANOWKU OPCII openX
+kOMANDA open NE REALIZOWANAX
+zA]ITA FAJLA NEWOZMOVNAX
+fAJL ZA]I]ENX
+%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+
+%s: ZA]ITA NA ^TENIE BYLA NEDOSTUPNAX
+
+%s: %lu STROK, %lu SIMWOLOWX
+nET TENEWYH OKONX
+kOMANDA script ISPOLXZUETSQ TOLXKO W REVIME viX
+nET KOMANDY DLQ ISPOLNENIQX
+oPCIQ shiftwidth USTANOWLENA NA 0X
+pEREPOLNENIE S^ET^IKAX
+cIKL WYPOLNEN NE DO KONCAX
+uKAZANO REGULQRNOE WYRAVENIE: FLAG r NE NUVENX
+fLAGI #, l I p NE MOGUT BYTX OB_EDINENY S FLAGOM c W REVIME viX
+sOWPADENIJ NETX
+mETKA OTSUTSTWUETX
+w STEKE METOK ZAPISEJ MENX[E, ^EM %s, ISPOLXZUJTE :display t[ags]X
+fAJLA S IMENEM %s W STEKE METOK NET; ISPOLXZUJTE :display t[ags]X
+
+%s: METKA NE NAJDENAX
+%s: PLOHAQ METKA W %sX
+
+sTEK METOK PUSTX
+%s: ISKOMAQ PEREMENNAQ NE NAJDENAX
+
+bUFER %s PUSTX
+
+pRERWANOX
+oTSUTSTWIE BUFERA DLQ ISPOLXZOWANIQX
+nET PREDIDU]EGO REGULQRNOGO WYRAVENIQX
+kOMANDA %s PODRAZUMEWAET NALI^IE PRO^TENNOGO FAJLAX
+iSPOLXZOWANIE: %sX
+kOMANDA visual PODRAZUMEWAET OBQZATELXNU@ USTANOWKU OPCII openX
+%s RAS[IRILSQ DO SLI[KOM BOLX[OGO KOLI^ESTWA FAJLOWX
+pUSTOJ FAJLX
+nET PREDYDU]EGO POISKA F, f, T, ILI tX
+%s NE NAJDENOX
+nET PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQX
+kURSOR STOIT NE NA CIFREX
+pOLU^ENNOE ^ISLO SLI[KOM WELIKOX
+pOLU^ENNOE ^ISLO SLI[KOM MALOX
+pODHODQ]EGO SIMWOLA NET NA \TOJ STROKEX
+pODHODQ]IJ SIMWOL NE NAJDENX
+nET SIMWOLOW DLQ UDALENIQX
+dRUGOGO \KRANA NE SU]ESTWUETX
+sIMWOLY POSLE STROKI DLQ POISKA I/ILI PEREBOR STROKIX
+pRO[LYJ OBRAZEC POISKA OTSUTSTWUETX
+pOISK ZAWER[ILSQ NA NA^ALXNOJ POZICIIX
+
+sIMWOL NEPRAWILEN; ZAKL@^EN W KAWY^KI DLQ WWODAX
+uVE NA NA^ALE WSTAWKIX
+nET SIMWOLOW DLQ UDALENIQX
+pEREDWIVENIE ZA KONEC FAJLAX
+pEREDWIVENIE ZA KONEC STROKIX
+dWIVENIE STROKI NE SDELANOX
+uVE NA NA^ALE FAJLAX
+dWIVENIE KURSORA ZA NA^ALO FAJLAX
+uVE W PERWOJ KOLONKEX
+bUFERY DOLVNY BYTX UKAZANY DO WYPOLNENIQ KOMANDYX
+uVE NA KONCE FAJLAX
+uVE NA KONSE STROKIX
+%s NE QWLQETSQ KOMANDOJ VIX
+iSPOLXZOWANIE: %sX
+nET SIMWOLOW DLQ UDALENIQX
+
+nET KOMANDY DLQ POWTORAX
+
+kOMANDA %s NE MOVET BYTX ISPOLXZOWANA KAK KOMANDA PRODWIVENIQX
+~ISLO BOLX[E ^EM %luX
+
+
+zNA^ENIE KOLI^ESTWA OKON SLI[KOM WELIKO, MAKSIMALXNOE ZNA^ENIE = %uX
+
+
+
+
+
+dWIVENIE KURSORA ZA KONEC \KRANAX
+dWIVENIE KURSORA ZA NA^ALO \KRANAX
+
+tENEWYH OKON NETX
+nE SU]ESTWUET TENEWOGO OKNA S REDAKTIROWANIEM FAJLA %sX
+wY NE MOVETE SDELATX EDINSTWENNOE OKNO TENEWYMX
+|KRAN MOVET BYTX SVATX
+|KRAN NE MOVET BYTX SVATX
+|KRAN NE MOVET BYTX RAS[IRENX
+
+
+
+
+dANNYJ TIP TERMINALA NE IMEET KLAWI[I %sX
+
+
+
+nEWOZMOVNO SOZDATX WREMENNYJ FAJLX
+wNIMANIE: %s SPECIALXNYJ FAJLX
+%s UVE ZABLOKIROWAN, DOSTUPEN TOLXKO NA ^TENIEX
+%s: UDALENX
+%s: ZAKRYTX
+%s: UDALENX
+%s: UDALENX
+fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN: iSPOLXZUJTE ! DLQ OBHODAX
+fAJL TOLXKO DLQ ^TENIQ, NE ZAPISANX
+%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+%s SU]ESTWUET, NE ZAPISANX
+iSPOLXZUJTE ! DLQ ^ASTI^NOJ ZAPISI FAJLAX
+~ASTX FAJLA, FAJL NE ZAPISANX
+%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ: ISPOLXZUJTE ! DLQ OBHODAX
+%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQX
+%s: ZA]ITA NA ZAPISX BYLA NEDOSTUPNAX
+
+%s: wnimanie: fajl use~enX
+
+%s: NOWYJ FAJL: %lu STROK, %lu SIMWOLOWX
+%s: %lu STROK, %lu SIMWOLOWX
+%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+%s SPECIALXNYJ FAJLX
+%s: NE PRINADLEVIT wAMX
+%s: DOSTUPEN NE TOLXKO wAMX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE :edit DLQ OBHODAX
+fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+fAJL WREMENNYJ: WYHOD SOTRET L@BYE IZMENENIQX
+
+zAPISI NA^ATY ZANOWOX
diff --git a/contrib/nvi/catalog/ru_SU.KOI8-R.base b/contrib/nvi/catalog/ru_SU.KOI8-R.base
new file mode 100644
index 000000000000..59efd3f53a5f
--- /dev/null
+++ b/contrib/nvi/catalog/ru_SU.KOI8-R.base
@@ -0,0 +1,219 @@
+002 "pEREPOLNENIE ZNA^ENIQ DLINY STROKI"
+003 "%s/%d: NEWOZMOVNO UDALITX STROKU %u"
+004 "%s/%d: NEWOZMOVNO DOBAWITX K STROKE %u"
+005 "%s/%d: NEWOZMOVNO WSTAWITX W STROKU %u"
+006 "%s/%d: NEWOZMOVNO SOHRANITX STROKU %u"
+007 "%s/%d: NEWOZMOVNO DOSTATX POSLEDN@@ STROKU"
+009 "fAJL ZAPISEJ"
+010 "zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDU"
+011 "nET IZMENENIJ DLQ OTMENY"
+012 "zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDU"
+013 "zAPISI NE WELISX, NEWOZMOVNO PROSMOTRETX WPERED"
+014 "nET IZMENENIJ DLQ PEREDELKI"
+015 "%s/%d: O[IBKA PRI ZAPISI PROTOKOLA"
+016 "sTANDARTNYJ WWOD/WYWOD DLQ VI DOLVEN BYTX TERMINAL"
+017 "oTMETKA %s: NE USTANOWLENA"
+018 "oTMETKA %s: STROKA BYLA UDALENA"
+019 "oTMETKA %s: POZICII KURSORA BOLX[E NE SU]ESTWUET"
+020 "o[IBKA:"
+030 "fAJL %s NE QWLQETSQ KATALOGOM SOOB]ENIJ"
+031 "nEWOZMOVNO USTANOWITX OPCI@ %s PO UMOL^ANI@"
+032 "iSPOLXZOWANIE: %s"
+033 "oPCII %s NET: 'set all' POKAZYWAET WSE WOZMOVNYE OPCII"
+034 "set: [no]%s NE PRINIMAET TAKOGO ZNA^ENIQ"
+035 "set: %s OPCIQ NE QWLQETSQ DWOI^NOJ"
+038 "set: NEPRAWILXNOE ZNA^ENIE %s"
+039 "set: %s OPCIQ NE QWLQETSQ DWOI^NOJ"
+040 "kOLI^ESTWO KOLONOK \KRANA SLI[KOM MALO, MENX[E ^EM %d"
+041 "kOLI^ESTWO KOLONOK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %d"
+042 "kOLI^ESTWO STROK \KRANA SLI[KOM MALO, MENX[E ^EM %d"
+043 "kOLI^ESTWO STROK \KRANA SLI[KOM WELIKO, BOLX[E ^EM %d"
+044 "oPCIQ lisp OTSUTSTWUET"
+045 "sOOB]ENIQ NE WYKL@^ENY: %s"
+046 "sOOB]ENIQ NE WKL@^ENY: %s"
+047 "oPCIQ modeline(s) NE MOVET BYTX PEREUSTANOWLENA"
+048 "oPCIQ paragraph DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMI"
+049 "oPCIQ section DOLVNA SOSTOQTX IZ GRUPP S DWUMQ SIMWOLAMI"
+050 "oPCIQ shiftwidth NE MOVET BYTX USTANOWLENA NA 0"
+051 "oPCIQ sourceany NE MOVET BYTX USTANOWLENA"
+052 "tABULQCIQ NE MOVET BYTX USTANOWLENA NA 0"
+053 "sTARTOWYJ BUFER PUST"
+054 "bUFER %s PUST"
+055 "fAJLY S SIMWOLAMI PEREWODA STROKI W IMENI NE MOGUT BYTX WOSSTANOWLENY"
+056 "iZMENENIQ NE SOHRANENY PRI KRAHE SESSII"
+058 "sOHRANENIE NE UDALOSX: %s"
+059 "iZMENENIQ NE SOHRANQ@TSQ PRI OBRYWE SESSII"
+060 "sOHRANENIE KOPII FAJLA NE UDALOSX: %s"
+062 "iNFORMACII NA POLXZOWATELQ %u NE NAJDENO"
+063 "nEWOZMOVNO ZA]ITITX SPASENNYJ FAJL"
+064 "bUFER WOSSTANOWLENNOGO FAJLA PEREPOLNEN"
+065 "wOSSTANOWLENNYJ FAJL"
+066 "%s: NE DO KONCA WOSSTANOWLENNYJ FAJL"
+067 "%s: NE DO KONCA WOSSTANOWLENNYJ FAJL"
+068 "fAJLOW S IMENEM %s, KOTORYE wY MOVETE ^ITATX, NE SU]ESTWUET"
+069 "eSTX STARYE WERSII FAJLA, KOTORYE MOVNO WOSSTANOWITX"
+070 "sU]ESTWU@T DRUGIE FAJLY, KOTORYE MOVNO WOSSTANOWITX"
+071 "E-mail NE POSLAN: %s"
+072 "fAJL PUST - ISKATX NE^EGO"
+073 "dOSTIGNUT KONEC FAJLA BEZ NAHOVDENIQ OBRAZCA POISKA"
+074 "nE ZADAN OBRAZEC POISKA"
+075 "oBRAZEC POISKA NE NAJDEN"
+076 "dOSTUPNO NA^ALO FAJLA BEZ NAHOVDENIQ OBRAZCA POISKA"
+077 "pOISK ZACIKLEN"
+079 "nEPE^ATNYH SIMWOLOW NE NAJDENO"
+080 "nEIZWESTNAQ KOMANDA"
+082 "kOMANDA NE DOSTUPNA W REVIME ex"
+083 "s^ET^IK NE MOVET BYTX NULEM"
+084 "%s: NEPRAWILXNOE UKAZANIE STROKI"
+085 "wNUTRENNQQ O[IBKA W SINTAKSISE (%s: %s)"
+086 "iSPOLXZOWANIE: %s"
+087 "%s: WREMENNYJ BUFER NE ISPOLXZOWAN"
+088 "mETKA POSTAWLENA PERED STROKOJ 1"
+089 "mETKA POSTAWLENA POSLE KONCA FAJLA"
+092 "kOMANDA ex NE UDALASX: PARAMETRY KOMANDY ZABYTY"
+094 "wTOROJ ADRES MENX[E ^EM PERWYJ"
+095 "nE UKAZANO NAZWANIE OTMETKI"
+096 "\\ NE ZAWER[AETSQ / ILI ?"
+097 "sSYLKA K STROKE S NOMEROM MENX[E 0"
+098 "kOMANDA %s NEIZWESTNA"
+099 "pEREPOLNENIE ZNA^ENIQ ADRESA"
+100 "nEDOBOR ZNA^ENIQ ADRESA"
+101 "nEDOPUSTIMAQ KOMBINACIQ W ADRESE"
+102 "nEPRAWILXNYJ ADRES: WSEGO %lu STROK W FAJLE"
+103 "nEPRAWILXNYJ ADRES: FAJL PUST"
+104 "kOMMANDA %s NE MOVET ISPOLXZOWATX ADRES 0"
+105 "aBBREWIATURY OTSUTSTWU@T"
+106 "aBBREWIATURY DOLVNY ZAKAN^IWATXSQ SIMWOLOM "word""
+107 "aBBREWIATURY NE MOGUT SODERVATX SIMWOLOY TABLQCII ILI PROBELY"
+108 "aBBREWIATURY NE MOGUT SO^ETATXSQ S SIMWOLAMI SLOW/NE-SLOW, ZA ISKL@^ENIEM KONCA STROKI"
+109 ""%s" NE QWLQETSQ ABBREWIATUROJ"
+111 "fAJLOW DLQ REDAKTIROWANIQ BOLX[E NET"
+112 "oTSUTSTWIE PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQ"
+113 "oTSUTSTWIE PREDYDU]EGO FAJLA DLQ PROSMOTRA NAZAD"
+114 "nET FAJLOW"
+115 "oTSUTSTWIE PREDYDU]EJ KOMANDY DLQ ZAMENY "!""
+116 "oTSUTSTWIE ZAMENY DLQ %%"
+117 "oTSUTSTWIE ZAMENY DLQ #"
+118 "o[IBKA: execl: %s"
+119 "o[IBKA WWODA/WYWODA: %s"
+120 "fAJL IZMENEN S MOMENTA POSLEDNEJ POLNOJ ZAPISI: ISPOLXZUJTE ! DLQ OBHODA"
+121 "nEWOZMOVNO NAJTI DOMA[NIJ KATALOG"
+122 "nOWYJ KATALOG: %s"
+123 "nET WYREZANYH BUFEROW"
+124 "kOMANDA %s NE MOVET BYTX ISPOLXZOWANA WNUTRI OB]EJ KOMANDY"
+125 "%s/%s: NE OTKRYT: NE PRINADLEVIT wAM ILI root-U"
+126 "%s/%s: NE OTKRYT: NE PRINADLEVIT wAM"
+127 "%s/%s: NE OTKRYT: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEM"
+128 "%s/%s: NE S^ITAN: NE PRINADLEVIT wAM ILI root-U"
+129 "%s/%s: NE S^ITAN: NE PRINADLEVIT wAM"
+130 "%s/%s: NE S^ITAN: WOZMOVNOSTX ZAPISI U POLXZOWATELQ, NE QWLQ@]EGOSQ WLADELXCEM"
+131 "pOSLEDU@]IE STROKI OTSUTSTWU@T"
+132 "oTSUTSTWIE PARAMETROW WWODA"
+133 "oTSUTSTWIE PARAMETROW KOMANDY"
+134 "sIMWOL %s NE MOVET BYTX PEREZAPOMNEN"
+135 ""%s" NA DANNYJ MOMENT NE OTME^EN"
+136 "iMQ METKI DOLVNO BYTX ODNIM SIMWOLOM"
+137 "%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODA"
+138 "nOWYJ FAJL .exrc: %s"
+139 "sTROKA PERENOSA NAHODITSQ WNUTRI PARAMETROW PERENOSA"
+140 "kOMANDA open PODRAZUMEWAET USTANOWKU OPCII open"
+141 "kOMANDA open NE REALIZOWANA"
+142 "zA]ITA FAJLA NEWOZMOVNA"
+143 "fAJL ZA]I]EN"
+144 "%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOW"
+146 "%s: ZA]ITA NA ^TENIE BYLA NEDOSTUPNA"
+148 "%s: %lu STROK, %lu SIMWOLOW"
+149 "nET TENEWYH OKON"
+150 "kOMANDA script ISPOLXZUETSQ TOLXKO W REVIME vi"
+151 "nET KOMANDY DLQ ISPOLNENIQ"
+152 "oPCIQ shiftwidth USTANOWLENA NA 0"
+153 "pEREPOLNENIE S^ET^IKA"
+154 "cIKL WYPOLNEN NE DO KONCA"
+155 "uKAZANO REGULQRNOE WYRAVENIE: FLAG r NE NUVEN"
+156 "fLAGI #, l I p NE MOGUT BYTX OB_EDINENY S FLAGOM c W REVIME vi"
+157 "sOWPADENIJ NET"
+158 "mETKA OTSUTSTWUET"
+159 "w STEKE METOK ZAPISEJ MENX[E, ^EM %s, ISPOLXZUJTE :display t[ags]"
+160 "fAJLA S IMENEM %s W STEKE METOK NET; ISPOLXZUJTE :display t[ags]"
+162 "%s: METKA NE NAJDENA"
+163 "%s: PLOHAQ METKA W %s"
+165 "sTEK METOK PUST"
+166 "%s: ISKOMAQ PEREMENNAQ NE NAJDENA"
+168 "bUFER %s PUST"
+170 "pRERWANO"
+171 "oTSUTSTWIE BUFERA DLQ ISPOLXZOWANIQ"
+172 "nET PREDIDU]EGO REGULQRNOGO WYRAVENIQ"
+173 "kOMANDA %s PODRAZUMEWAET NALI^IE PRO^TENNOGO FAJLA"
+174 "iSPOLXZOWANIE: %s"
+175 "kOMANDA visual PODRAZUMEWAET OBQZATELXNU@ USTANOWKU OPCII open"
+176 "%s RAS[IRILSQ DO SLI[KOM BOLX[OGO KOLI^ESTWA FAJLOW"
+177 "pUSTOJ FAJL"
+178 "nET PREDYDU]EGO POISKA F, f, T, ILI t"
+179 "%s NE NAJDENO"
+180 "nET PREDYDU]EGO FAJLA DLQ REDAKTIROWANIQ"
+181 "kURSOR STOIT NE NA CIFRE"
+182 "pOLU^ENNOE ^ISLO SLI[KOM WELIKO"
+183 "pOLU^ENNOE ^ISLO SLI[KOM MALO"
+184 "pODHODQ]EGO SIMWOLA NET NA \TOJ STROKE"
+185 "pODHODQ]IJ SIMWOL NE NAJDEN"
+186 "nET SIMWOLOW DLQ UDALENIQ"
+187 "dRUGOGO \KRANA NE SU]ESTWUET"
+188 "sIMWOLY POSLE STROKI DLQ POISKA I/ILI PEREBOR STROKI"
+189 "pRO[LYJ OBRAZEC POISKA OTSUTSTWUET"
+190 "pOISK ZAWER[ILSQ NA NA^ALXNOJ POZICII"
+192 "sIMWOL NEPRAWILEN; ZAKL@^EN W KAWY^KI DLQ WWODA"
+193 "uVE NA NA^ALE WSTAWKI"
+194 "nET SIMWOLOW DLQ UDALENIQ"
+195 "pEREDWIVENIE ZA KONEC FAJLA"
+196 "pEREDWIVENIE ZA KONEC STROKI"
+197 "dWIVENIE STROKI NE SDELANO"
+198 "uVE NA NA^ALE FAJLA"
+199 "dWIVENIE KURSORA ZA NA^ALO FAJLA"
+200 "uVE W PERWOJ KOLONKE"
+201 "bUFERY DOLVNY BYTX UKAZANY DO WYPOLNENIQ KOMANDY"
+202 "uVE NA KONCE FAJLA"
+203 "uVE NA KONSE STROKI"
+204 "%s NE QWLQETSQ KOMANDOJ VI"
+205 "iSPOLXZOWANIE: %s"
+206 "nET SIMWOLOW DLQ UDALENIQ"
+208 "nET KOMANDY DLQ POWTORA"
+210 "kOMANDA %s NE MOVET BYTX ISPOLXZOWANA KAK KOMANDA PRODWIVENIQ"
+211 "~ISLO BOLX[E ^EM %lu"
+214 "zNA^ENIE KOLI^ESTWA OKON SLI[KOM WELIKO, MAKSIMALXNOE ZNA^ENIE = %u"
+220 "dWIVENIE KURSORA ZA KONEC \KRANA"
+221 "dWIVENIE KURSORA ZA NA^ALO \KRANA"
+223 "tENEWYH OKON NET"
+224 "nE SU]ESTWUET TENEWOGO OKNA S REDAKTIROWANIEM FAJLA %s"
+225 "wY NE MOVETE SDELATX EDINSTWENNOE OKNO TENEWYM"
+226 "|KRAN MOVET BYTX SVAT"
+227 "|KRAN NE MOVET BYTX SVAT"
+228 "|KRAN NE MOVET BYTX RAS[IREN"
+233 "dANNYJ TIP TERMINALA NE IMEET KLAWI[I %s"
+237 "nEWOZMOVNO SOZDATX WREMENNYJ FAJL"
+238 "wNIMANIE: %s SPECIALXNYJ FAJL"
+239 "%s UVE ZABLOKIROWAN, DOSTUPEN TOLXKO NA ^TENIE"
+240 "%s: UDALEN"
+241 "%s: ZAKRYT"
+242 "%s: UDALEN"
+243 "%s: UDALEN"
+244 "fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN: iSPOLXZUJTE ! DLQ OBHODA"
+245 "fAJL TOLXKO DLQ ^TENIQ, NE ZAPISAN"
+246 "%s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODA"
+247 "%s SU]ESTWUET, NE ZAPISAN"
+248 "iSPOLXZUJTE ! DLQ ^ASTI^NOJ ZAPISI FAJLA"
+249 "~ASTX FAJLA, FAJL NE ZAPISAN"
+250 "%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ: ISPOLXZUJTE ! DLQ OBHODA"
+251 "%s: fAJL IZMENQLSQ POZDNEE, ^EM DANNAQ KOPIQ"
+252 "%s: ZA]ITA NA ZAPISX BYLA NEDOSTUPNA"
+254 "%s: wnimanie: fajl use~en"
+256 "%s: NOWYJ FAJL: %lu STROK, %lu SIMWOLOW"
+257 "%s: %lu STROK, %lu SIMWOLOW"
+258 "%s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOW"
+259 "%s SPECIALXNYJ FAJL"
+260 "%s: NE PRINADLEVIT wAM"
+261 "%s: DOSTUPEN NE TOLXKO wAM"
+262 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODA"
+263 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE :edit DLQ OBHODA"
+264 "fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODA"
+265 "fAJL WREMENNYJ: WYHOD SOTRET L@BYE IZMENENIQ"
+267 "zAPISI NA^ATY ZANOWO"
diff --git a/contrib/nvi/catalog/ru_SU.KOI8-R.check b/contrib/nvi/catalog/ru_SU.KOI8-R.check
new file mode 100644
index 000000000000..ca77b891051a
--- /dev/null
+++ b/contrib/nvi/catalog/ru_SU.KOI8-R.check
@@ -0,0 +1,169 @@
+Unused message id's (this is okay):
+001
+008
+021
+022
+023
+024
+025
+026
+027
+028
+029
+036
+037
+057
+061
+078
+081
+090
+091
+093
+110
+145
+147
+161
+164
+167
+169
+191
+207
+209
+212
+213
+215
+216
+217
+218
+219
+222
+229
+230
+231
+232
+234
+235
+236
+253
+255
+266
+=========================
+MISSING ERROR MESSAGES (Please add!):
+008
+021
+022
+023
+024
+025
+026
+027
+028
+029
+036
+037
+057
+061
+078
+090
+091
+093
+110
+145
+147
+161
+164
+167
+169
+191
+207
+209
+212
+215
+216
+217
+218
+219
+222
+230
+231
+232
+234
+235
+236
+253
+255
+266
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+=========================
+Extra error messages (just delete them):
+047
+050
+051
+052
+176
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s RAS[IRILSQ W SLI[KOM BOLX[OE KOLI^ESTWO IMEN FAJLOWX
+ 2 %s SU]ESTWUET, NE ZAPISAN; ISPOLXZUJTE ! DLQ OBHODAX
+ 2 %s: %lu STROK, %lu SIMWOLOWX
+ 2 %s: NE DO KONCA WOSSTANOWLENNYJ FAJLX
+ 2 bUFER %s PUSTX
+ 2 fAJL IZMENEN SO WREMENI POSLEDNEJ ZAPISI: SOHRANITE ILI ISPOLXZUJTE ! DLQ OBHODAX
+ 2 set: %s OPCIQ NE QWLQETSQ DWOI^NOJX
+ 2 zAPISI NE WELISX, NEWOZMOVNO OTMENITX POSLEDN@@ KOMANDUX
+ 3 %s: UDALENX
+ 3 nET SIMWOLOW DLQ UDALENIQX
+ 4 iSPOLXZOWANIE: %sX
+=========================
diff --git a/contrib/nvi/catalog/ru_SU.KOI8-R.owner b/contrib/nvi/catalog/ru_SU.KOI8-R.owner
new file mode 100644
index 000000000000..bf4fa180298c
--- /dev/null
+++ b/contrib/nvi/catalog/ru_SU.KOI8-R.owner
@@ -0,0 +1 @@
+Dima Ruban <dima@demos.su>
diff --git a/contrib/nvi/catalog/spanish b/contrib/nvi/catalog/spanish
new file mode 100644
index 000000000000..fb67ee9dc422
--- /dev/null
+++ b/contrib/nvi/catalog/spanish
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+Desbordamiento de longitud de l¡neaX
+no se puede borrar la l¡nea %luX
+no se puede a¤adir la l¡nea %luX
+no se puede insertar en la l¡nea %luX
+no se puede guardar la l¡nea %luX
+no se puede obtener la £ltima l¡neaX
+Error: no se puede recuperar la l¡nea %luX
+Archivo de logX
+No se realiza log, no se puede deshacerX
+No hay cambios para deshacerX
+No se realiza log, no se puede deshacerX
+No se realiza log, no se puede remontar hacia adelanteX
+No hay cambios para rehacerX
+%s/%d: error de logX
+La entrada y salida est ndar de Vi debe ser una terminalX
+Marcar %s: no determinadoX
+Marcar %s: se borr¢ la l¡neaX
+Marcar %s: la posici¢n del cursor ya no existeX
+Error: X
+nuevo archivoX
+nombre cambiadoX
+modificadoX
+no modificadoX
+DESTRABADOX
+lectura solamenteX
+l¡nea %lu de %lu [%ld%%]X
+archivo vac¡oX
+l¡nea %luX
+El archivo %s no es un cat logo de mensajesX
+No se puede determinar la opci¢n por omisi¢n %sX
+Uso: %sX
+determinar: no hay opci¢n %s: 'determinar todo' establece todos los valores de opci¢nX
+determinar: [no] hay opci¢n %s no tiene valorX
+determinar: opci¢n %s no es booleanoX
+determinar: opci¢n %s: %sX
+determinar: opci¢n %s: %s: desbordamiento de valoresX
+determinar: opci¢n %s: %s es un n£mero ilegalX
+determinar: opci¢n %s no es booleanoX
+Las columnas en pantalla son demasiado peque¤as, menos de %dX
+Las columnas en pantalla son demasiado grandes, m s de %dX
+Las l¡neas en pantalla son demasiado peque¤as, menos de %dX
+Las l¡neas en pantalla son demasiado grandes, m s de %dX
+La opci¢n lisp no est  implementadaX
+mensajes no desconectados: %sX
+mensajes no conectados: %sX
+
+La opci¢n de p rrafo debe estar en dos grupos de caracteresX
+La opci¢n de secci¢n debe estar en dos grupos de caracteresX
+
+
+
+El buffer por omisi¢n est  vac¡oX
+El buffer %s est  vac¡oX
+Los archivos con nuevas l¡neas en el nombre son irrecuperablesX
+Las modificaciones no pueden recuperarse si la sesi¢n fallaX
+Copiando archivo para recuperaci¢n...X
+Preservaci¢n fracasada: %sX
+Las modificaciones no pueden recuperarse si la sesi¢n fallaX
+Archivo de respaldo fracasado: %sX
+Copiando archivo para recuperaci¢n...X
+Informaci¢n sobre identificaci¢n del usuario %u no encontradaX
+No se puede trabar archivo de recuperaci¢nX
+Desbordamiento de buffer de archivo de recuperaci¢nX
+Archivo de recuperaci¢nX
+%s: archivo de recuperaci¢n defectuosoX
+%s: archivo de recuperaci¢n defectuosoX
+No hay archivos denominados %s, que usted pueda leer, para recuperarX
+Existen versiones m s antiguas de este archivo que usted puede recuperarX
+Existen otros archivos que usted puede recuperarX
+no env¡a email: %sX
+Archivo vac¡o; no hay nada para buscarX
+Se alcanz¢ el final del archivo sin encontrar el patr¢nX
+No hay patr¢n anterior de b£squedaX
+No se encontr¢ el patr¢nX
+ Se alcanz¢ el principio del archivo sin encontrar el patr¢nX
+B£squeda reiniciadaX
+Buscando...X
+No se encontr¢ ning£n car cter no imprimibleX
+Nombre de comando desconocidoX
+
+%s: comando no disponible en modalidad exX
+La cuenta no puede ser ceroX
+%s: mala especificaci¢n de l¡neaX
+Error interno de tabla de sintaxis (%s: %s)X
+Uso: %sX
+%s: buffer temporario no liberadoX
+Desplazamiento de marcador a antes de la l¡nea 1X
+Desplazamiento de marcador m s all  del final del archivoX
+@ con rango que corre cuando se cambia el archivo/la pantallaX
+Comando global/v que corre cuando se cambia el archivo/la pantallaX
+Comando Ex fracasado: comandos pendientes descartadosX
+Comando Ex fracasado: teclas mapeadas descartadasX
+La segunda direcci¢n es m s peque¤a que la primeraX
+No se suministra nombre de marcaX
+\\ no es seguido por / o ?X
+Referencia a un n£mero de l¡nea menor que 0X
+El comando %s es desconocidoX
+Desbordamiento de valor de direcci¢nX
+Subdesbordamiento de valor de direcci¢nX
+Combinaci¢n de direcci¢n ilegalX
+Direcci¢n ilegal: s¢lo %lu l¡neas en el archivoX
+Direcci¢n ilegal: el archivo est  vac¡oX
+El comando %s no permite una direcci¢n de 0X
+No hay abreviaturas para visualizarX
+Las abreviaturas deben terminar con un car cter de \"palabra\" X
+Las abreviaturas no pueden contener tabs o espaciosX
+Las abreviaturas no pueden mezclar caracteres palabra/no-palabra, salvo al finalX
+\"%s\" no es una abreviaturaX
+Comando Vi fracasado: teclas mapeadas descartadasX
+No hay m s archivos para editarX
+No hay archivos anteriores para editarX
+No hay archivos anteriores para rebobinarX
+No hay lista de archivos para visualizarX
+No hay un comando anterior para reemplazar a \"!\"X
+No hay nombre de archivo para sustituir por %%X
+No hay nombre de archivo para sustituir por #X
+Error: execl: %sX
+Error de E/S: %sX
+Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterarX
+No se puede encontrar la ubicaci¢n del directorio inicialX
+Nuevo directorio actual: %sX
+No hay buffers sueltos para visualizarX
+El comando %s no puede usarse como parte de un comando global o vX
+%s/%s: sin fuente: no le pertenece a usted o a ra¡zX
+%s/%s: sin fuente: no le pertenece a ustedX
+%s/%s: sin fuente: puede ser escrito por un usuario que no sea el propietarioX
+%s: sin fuente: no le pertenece a usted o a ra¡zX
+%s: sin fuente: no le pertenece a ustedX
+%s: sin fuente: puede ser escrito por un usuario que no sea el propietarioX
+No hay l¡neas siguientes para unirX
+No hay anotaciones de mapa de entradaX
+No hay anotaciones de mapa de comandoX
+El car cter %s no puede remapearseX
+\"%s\" no est  mapeado actualmenteX
+Marca de nombres debe ser un s¢lo car cterX
+%s existe, no est  escrito; usar ! para alterarX
+Nuevo archivo exrc: %sX
+La l¡nea de destino se encuentra dentro del rango de movimientoX
+El comando abierto requiere que se determine la opci¢n abiertaX
+El comando abierto no se ha implementado a£nX
+No es posible preservar este archivoX
+Archivo preservadoX
+%s: expandido a demasiados nombres de archivoX
+S¢lo pueden leerse los archivos regulares y los conductos nombradosX
+%s: traba de lectura no disponibleX
+Leyendo...X
+%s: %lu l¡neas, %lu caracteresX
+No hay pantallas de fondo para mostrarX
+El comando de script s¢lo est  disponible en modalidad viX
+No hay comando para ejecutarX
+opci¢n de ancho de desplazamiento en 0X
+Desbordamiento de cuentaX
+Subdesbordamiento de cuentaX
+Expresi¢n regular especificada; marcador r no tiene significadoX
+Los marcadores #, l y p no pueden combinarse con el marcador c en la modalidad viX
+No se encontr¢ coincidenciaX
+No se ingres¢ un identificador anteriorX
+Se encontraron menos de %s anotaciones en la pila de identificadores; usar :visualizar i[dentificadores]X
+No hay archivo %s en la pila de identificadores al que se pueda volver; usar :visualizar i[dentificadores]"
+Presionar Intro para continuar: X
+%s: no se encontr¢ el identificadorX
+%s: identificador corrompido en %sX
+%s: el n£mero de l¡nea del identificador es posterior al final del archivoX
+La pila de identificadores est  vac¡aX
+%s: patr¢n de b£squeda no encontradoX
+%d archivos m s para editarX
+El buffer %s est  vac¡oX
+¨Confirmar cambio? [n]X
+InterrumpidoX
+No hay buffer anterior para ejecutarX
+No hay expresi¢n regular anteriorX
+El comando %s requiere que se haya le¡do un archivoX
+Uso: %sX
+El comando visual requiere que se determine la opci¢n abiertaX
+
+Archivo vac¡oX
+No hay b£squeda F, f, T o t anteriorX
+%s no se encontr¢X
+No hay archivo anterior para editarX
+El cursor no est  en un n£meroX
+El n£mero resultante es demasiado grandeX
+ El n£mero resultante es demasiado peque¤oX
+No hay car cter coincidente en esta l¡neaX
+No se encontr¢ un car cter coincidenteX
+No hay caracteres para reemplazarX
+No hay otra pantalla a la que se pueda pasarX
+Caracteres despu‚s de cadena de b£squeda, desplazamiento de l¡nea y/o comando zX
+No hay patr¢n anterior de b£squedaX
+B£squeda vuelve a la posici¢n inicialX
+Se super¢ el l¡mite de expansi¢n de abreviatura: se descartaron caracteresX
+Car cter ilegal; mencionar para entrarX
+Ya se encuentra al principio de la inserci¢nX
+No hay m s caracteres para borrarX
+Movimiento m s all  del final del archivoX
+Movimiento m s all  del final de la l¡neaX
+No hay movimiento del cursorX
+Ya se encuentra al principio del archivoX
+Movimiento m s all  del principio del archivoX
+Ya se encuentra en la primera columnaX
+Los buffers deben especificarse antes del comandoX
+Ya se encuentra al final del archivoX
+Ya se encuentra al final de la l¡neaX
+%s no es un comando viX
+Uso: %sX
+No hay caracteres para borrarX
+El comando Q requiere la interfase de terminal exX
+No hay comando para repetirX
+El archivo est  vac¡oX
+%s no puede usarse como comando de movimientoX
+Ya se encuentra en modalidad de comandoX
+El cursor no se encuentra en una palabraX
+
+El valor de opci¢n de Windows es demasiado grande, el m x. es %uX
+A¤adirX
+CambiarX
+ComandoX
+InsertarX
+ReemplazarX
+El movimiento va m s all  del final de la pantallaX
+El movimiento va m s all  del principio de la pantallaX
+La pantalla debe tener m s de %d l¡neas para dividirseX
+No hay pantallas de fondoX
+No hay pantalla de fondo editando un archivo denominado %sX
+No se puede poner fondo a la £nica pantalla que se visualizaX
+La pantalla s¢lo puede reducirse a %d hilerasX
+La pantalla no puede reducirseX
+La pantalla no puede aumentarseX
+
+Esta pantalla no puede suspenderseX
+Interrumpido: teclas mapeadas descartadasX
+vi: buffer temporario no liberadoX
+Esta terminal no tiene tecla %sX
+S¢lo un buffer puede especificarseX
+N£mero mayor que %luX
+InterrumpidoX
+No se puede crear archivo temporarioX
+Advertencia: %s no es un archivo regularX
+%s ya se encuentra trabado, la sesi¢n es de lectura solamenteX
+%s: eliminarX
+%s: cerrarX
+%s: eliminarX
+%s: eliminarX
+Archivo de lectura solamente, no escrito; usar ! para alterarX
+ Archivo de lectura solamente, no escritoX
+%s existe, no escrito; usar ! para alterarX
+%s existe, no escritoX
+Archivo parcial, no escrito; usar ! para alterarX
+Archivo parcial, no escritoX
+%s: archivo modificado m s recientemente que esta copia; usar ! para alterarX
+%s: archivo modificado m s recientemente que esta copiaX
+%s: la traba de escritura no estaba disponibleX
+Escribiendo...X
+%s: ADVERTENCIA: ARCHIVO TRUNCADOX
+Ya se encuentra en el primer identificador de este grupoX
+%s: nuevo archivo: %lu l¡neas, %lu caracteresX
+%s: %lu l¡neas, %lu caracteresX
+%s expandido a demasiados nombres de archivosX
+%s: no es un archivo regularX
+%s: no le perteneceX
+%s: accesible por un usuario que no sea el propietarioX
+Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterarX
+Archivo modificado desde la £ltima escritura completa; escribir o usar :editar! para alterarX
+Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterarX
+El archivo es temporario; al salir se descartar n las modificacionesX
+Archivo de lectura solamente, las modificaciones no se autoescribenX
+Se reinici¢ el logX
+confirmar? [snq]X
+Presionar cualquier tecla para continuar: X
+Presionar cualquier tecla para continuar [: para ingresar m s comandos ex]: X
+Presionar cualquier tecla para continuar [q para salir]: X
+Esta forma de %s requiere la interfase terminal exX
+Ingresando en la modalidad de entrada ex.X
+Comando fracasado, no hay archivo le¡do aun.X
+ cont?X
+Evento inesperado de car cterX
+Evento inesperado de final de archivoX
+No hay coincidencias para consultaX
+Evento inesperado de interrupci¢nX
+Evento inesperado de salidaX
+Evento inesperado de repinturaX
+Ya se encuentra en el £ltimo identificador de este grupoX
+El comando %s requiere la interfase terminal exX
+Esta forma de %s no se encuentra soportada cuando se determina la opci¢n de edici¢n seguraX
+Evento inesperado de cadenaX
+Evento inesperado de tiempo excedidoX
+Evento inesperado de escrituraX
+
+Las expansiones de shell no se encuentran soportadas cuando se determina la opci¢n de edici¢n seguraX
+El comando %s no se encuentra soportado cuando se determina la opci¢n de edici¢n seguraX
+determinar: la opci¢n %s puede no estar desconectadaX
+El monitor es demasiado peque¤o.X
+agregadoX
+cambiadoX
+borradoX
+unidoX
+movidoX
+desplazadoX
+arrancadoX
+l¡neaX
+l¡neasX
+Vi no se carg¢ con un int‚rprete TclX
+Archivo modificado desde la £ltima escritura.X
+Expansi¢n de shell fracasadaX
+No hay opci¢n de edici¢n %s especificadaX
+Vi no se carg¢ con un int‚rprete PerlX
+No hay comando ex para ejecutarX
+Ingresar <CR> para ejecutar un comando, :q para salirX
+Usar \"cscope ayuda\" para obtener ayudaX
+No hay conexiones cscope corriendoX
+%s: tipo de b£squeda desconocido: usar uno de %sX
+%d: no existe esta sesi¢n cscopeX
+determinar: la opci¢n %s no puede conectarse nuncaX
+determinar: la opci¢n %s no puede determinarse nunca en 0X
+%s: a¤adido: %lu l¡neas, %lu caracteresX
+Evento inesperado de modificaci¢n de tama¤oX
+%d archivos para editarX
diff --git a/contrib/nvi/catalog/spanish.base b/contrib/nvi/catalog/spanish.base
new file mode 100644
index 000000000000..270199eee5c8
--- /dev/null
+++ b/contrib/nvi/catalog/spanish.base
@@ -0,0 +1,309 @@
+002 "Desbordamiento de longitud de l¡nea"
+003 "no se puede borrar la l¡nea %lu"
+004 "no se puede a¤adir la l¡nea %lu"
+005 "no se puede insertar en la l¡nea %lu"
+006 "no se puede guardar la l¡nea %lu"
+007 "no se puede obtener la £ltima l¡nea"
+008 "Error: no se puede recuperar la l¡nea %lu"
+009 "Archivo de log"
+010 "No se realiza log, no se puede deshacer"
+011 "No hay cambios para deshacer"
+012 "No se realiza log, no se puede deshacer"
+013 "No se realiza log, no se puede remontar hacia adelante"
+014 "No hay cambios para rehacer"
+015 "%s/%d: error de log"
+016 "La entrada y salida est ndar de Vi debe ser una terminal"
+017 "Marcar %s: no determinado"
+018 "Marcar %s: se borr¢ la l¡nea"
+019 "Marcar %s: la posici¢n del cursor ya no existe"
+020 "Error: "
+021 "nuevo archivo"
+022 "nombre cambiado"
+023 "modificado"
+024 "no modificado"
+025 "DESTRABADO"
+026 "lectura solamente"
+027 "l¡nea %lu de %lu [%ld%%]"
+028 "archivo vac¡o"
+029 "l¡nea %lu"
+030 "El archivo %s no es un cat logo de mensajes"
+031 "No se puede determinar la opci¢n por omisi¢n %s"
+032 "Uso: %s"
+033 "determinar: no hay opci¢n %s: 'determinar todo' establece todos los valores de opci¢n"
+034 "determinar: [no] hay opci¢n %s no tiene valor"
+035 "determinar: opci¢n %s no es booleano"
+036 "determinar: opci¢n %s: %s"
+037 "determinar: opci¢n %s: %s: desbordamiento de valores"
+038 "determinar: opci¢n %s: %s es un n£mero ilegal"
+039 "determinar: opci¢n %s no es booleano"
+040 "Las columnas en pantalla son demasiado peque¤as, menos de %d"
+041 "Las columnas en pantalla son demasiado grandes, m s de %d"
+042 "Las l¡neas en pantalla son demasiado peque¤as, menos de %d"
+043 "Las l¡neas en pantalla son demasiado grandes, m s de %d"
+044 "La opci¢n lisp no est  implementada"
+045 "mensajes no desconectados: %s"
+046 "mensajes no conectados: %s"
+048 "La opci¢n de p rrafo debe estar en dos grupos de caracteres"
+049 "La opci¢n de secci¢n debe estar en dos grupos de caracteres"
+053 "El buffer por omisi¢n est  vac¡o"
+054 "El buffer %s est  vac¡o"
+055 "Los archivos con nuevas l¡neas en el nombre son irrecuperables"
+056 "Las modificaciones no pueden recuperarse si la sesi¢n falla"
+057 "Copiando archivo para recuperaci¢n..."
+058 "Preservaci¢n fracasada: %s"
+059 "Las modificaciones no pueden recuperarse si la sesi¢n falla"
+060 "Archivo de respaldo fracasado: %s"
+061 "Copiando archivo para recuperaci¢n..."
+062 "Informaci¢n sobre identificaci¢n del usuario %u no encontrada"
+063 "No se puede trabar archivo de recuperaci¢n"
+064 "Desbordamiento de buffer de archivo de recuperaci¢n"
+065 "Archivo de recuperaci¢n"
+066 "%s: archivo de recuperaci¢n defectuoso"
+067 "%s: archivo de recuperaci¢n defectuoso"
+068 "No hay archivos denominados %s, que usted pueda leer, para recuperar"
+069 "Existen versiones m s antiguas de este archivo que usted puede recuperar"
+070 "Existen otros archivos que usted puede recuperar"
+071 "no env¡a email: %s"
+072 "Archivo vac¡o; no hay nada para buscar"
+073 "Se alcanz¢ el final del archivo sin encontrar el patr¢n"
+074 "No hay patr¢n anterior de b£squeda"
+075 "No se encontr¢ el patr¢n"
+076 " Se alcanz¢ el principio del archivo sin encontrar el patr¢n"
+077 "B£squeda reiniciada"
+078 "Buscando..."
+079 "No se encontr¢ ning£n car cter no imprimible"
+080 "Nombre de comando desconocido"
+082 "%s: comando no disponible en modalidad ex"
+083 "La cuenta no puede ser cero"
+084 "%s: mala especificaci¢n de l¡nea"
+085 "Error interno de tabla de sintaxis (%s: %s)"
+086 "Uso: %s"
+087 "%s: buffer temporario no liberado"
+088 "Desplazamiento de marcador a antes de la l¡nea 1"
+089 "Desplazamiento de marcador m s all  del final del archivo"
+090 "@ con rango que corre cuando se cambia el archivo/la pantalla"
+091 "Comando global/v que corre cuando se cambia el archivo/la pantalla"
+092 "Comando Ex fracasado: comandos pendientes descartados"
+093 "Comando Ex fracasado: teclas mapeadas descartadas"
+094 "La segunda direcci¢n es m s peque¤a que la primera"
+095 "No se suministra nombre de marca"
+096 "\\ no es seguido por / o ?"
+097 "Referencia a un n£mero de l¡nea menor que 0"
+098 "El comando %s es desconocido"
+099 "Desbordamiento de valor de direcci¢n"
+100 "Subdesbordamiento de valor de direcci¢n"
+101 "Combinaci¢n de direcci¢n ilegal"
+102 "Direcci¢n ilegal: s¢lo %lu l¡neas en el archivo"
+103 "Direcci¢n ilegal: el archivo est  vac¡o"
+104 "El comando %s no permite una direcci¢n de 0"
+105 "No hay abreviaturas para visualizar"
+106 "Las abreviaturas deben terminar con un car cter de \"palabra\" "
+107 "Las abreviaturas no pueden contener tabs o espacios"
+108 "Las abreviaturas no pueden mezclar caracteres palabra/no-palabra, salvo al final"
+109 "\"%s\" no es una abreviatura"
+110 "Comando Vi fracasado: teclas mapeadas descartadas"
+111 "No hay m s archivos para editar"
+112 "No hay archivos anteriores para editar"
+113 "No hay archivos anteriores para rebobinar"
+114 "No hay lista de archivos para visualizar"
+115 "No hay un comando anterior para reemplazar a \"!\""
+116 "No hay nombre de archivo para sustituir por %%"
+117 "No hay nombre de archivo para sustituir por #"
+118 "Error: execl: %s"
+119 "Error de E/S: %s"
+120 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
+121 "No se puede encontrar la ubicaci¢n del directorio inicial"
+122 "Nuevo directorio actual: %s"
+123 "No hay buffers sueltos para visualizar"
+124 "El comando %s no puede usarse como parte de un comando global o v"
+125 "%s/%s: sin fuente: no le pertenece a usted o a ra¡z"
+126 "%s/%s: sin fuente: no le pertenece a usted"
+127 "%s/%s: sin fuente: puede ser escrito por un usuario que no sea el propietario"
+128 "%s: sin fuente: no le pertenece a usted o a ra¡z"
+129 "%s: sin fuente: no le pertenece a usted"
+130 "%s: sin fuente: puede ser escrito por un usuario que no sea el propietario"
+131 "No hay l¡neas siguientes para unir"
+132 "No hay anotaciones de mapa de entrada"
+133 "No hay anotaciones de mapa de comando"
+134 "El car cter %s no puede remapearse"
+135 "\"%s\" no est  mapeado actualmente"
+136 "Marca de nombres debe ser un s¢lo car cter"
+137 "%s existe, no est  escrito; usar ! para alterar"
+138 "Nuevo archivo exrc: %s"
+139 "La l¡nea de destino se encuentra dentro del rango de movimiento"
+140 "El comando abierto requiere que se determine la opci¢n abierta"
+141 "El comando abierto no se ha implementado a£n"
+142 "No es posible preservar este archivo"
+143 "Archivo preservado"
+144 "%s: expandido a demasiados nombres de archivo"
+145 "S¢lo pueden leerse los archivos regulares y los conductos nombrados"
+146 "%s: traba de lectura no disponible"
+147 "Leyendo..."
+148 "%s: %lu l¡neas, %lu caracteres"
+149 "No hay pantallas de fondo para mostrar"
+150 "El comando de script s¢lo est  disponible en modalidad vi"
+151 "No hay comando para ejecutar"
+152 "opci¢n de ancho de desplazamiento en 0"
+153 "Desbordamiento de cuenta"
+154 "Subdesbordamiento de cuenta"
+155 "Expresi¢n regular especificada; marcador r no tiene significado"
+156 "Los marcadores #, l y p no pueden combinarse con el marcador c en la modalidad vi"
+157 "No se encontr¢ coincidencia"
+158 "No se ingres¢ un identificador anterior"
+159 "Se encontraron menos de %s anotaciones en la pila de identificadores; usar :visualizar i[dentificadores]"
+160 "No hay archivo %s en la pila de identificadores al que se pueda volver; usar :visualizar i[dentificadores]"
+161 "Presionar Intro para continuar: "
+162 "%s: no se encontr¢ el identificador"
+163 "%s: identificador corrompido en %s"
+164 "%s: el n£mero de l¡nea del identificador es posterior al final del archivo"
+165 "La pila de identificadores est  vac¡a"
+166 "%s: patr¢n de b£squeda no encontrado"
+167 "%d archivos m s para editar"
+168 "El buffer %s est  vac¡o"
+169 "¨Confirmar cambio? [n]"
+170 "Interrumpido"
+171 "No hay buffer anterior para ejecutar"
+172 "No hay expresi¢n regular anterior"
+173 "El comando %s requiere que se haya le¡do un archivo"
+174 "Uso: %s"
+175 "El comando visual requiere que se determine la opci¢n abierta"
+177 "Archivo vac¡o"
+178 "No hay b£squeda F, f, T o t anterior"
+179 "%s no se encontr¢"
+180 "No hay archivo anterior para editar"
+181 "El cursor no est  en un n£mero"
+182 "El n£mero resultante es demasiado grande"
+183 " El n£mero resultante es demasiado peque¤o"
+184 "No hay car cter coincidente en esta l¡nea"
+185 "No se encontr¢ un car cter coincidente"
+186 "No hay caracteres para reemplazar"
+187 "No hay otra pantalla a la que se pueda pasar"
+188 "Caracteres despu‚s de cadena de b£squeda, desplazamiento de l¡nea y/o comando z"
+189 "No hay patr¢n anterior de b£squeda"
+190 "B£squeda vuelve a la posici¢n inicial"
+191 "Se super¢ el l¡mite de expansi¢n de abreviatura: se descartaron caracteres"
+192 "Car cter ilegal; mencionar para entrar"
+193 "Ya se encuentra al principio de la inserci¢n"
+194 "No hay m s caracteres para borrar"
+195 "Movimiento m s all  del final del archivo"
+196 "Movimiento m s all  del final de la l¡nea"
+197 "No hay movimiento del cursor"
+198 "Ya se encuentra al principio del archivo"
+199 "Movimiento m s all  del principio del archivo"
+200 "Ya se encuentra en la primera columna"
+201 "Los buffers deben especificarse antes del comando"
+202 "Ya se encuentra al final del archivo"
+203 "Ya se encuentra al final de la l¡nea"
+204 "%s no es un comando vi"
+205 "Uso: %s"
+206 "No hay caracteres para borrar"
+207 "El comando Q requiere la interfase de terminal ex"
+208 "No hay comando para repetir"
+209 "El archivo est  vac¡o"
+209 "El archivo est  vac¡o"
+210 "%s no puede usarse como comando de movimiento"
+211 "Ya se encuentra en modalidad de comando"
+212 "El cursor no se encuentra en una palabra"
+214 "El valor de opci¢n de Windows es demasiado grande, el m x. es %u"
+215 "A¤adir"
+216 "Cambiar"
+217 "Comando"
+218 "Insertar"
+219 "Reemplazar"
+220 "El movimiento va m s all  del final de la pantalla"
+221 "El movimiento va m s all  del principio de la pantalla"
+222 "La pantalla debe tener m s de %d l¡neas para dividirse"
+223 "No hay pantallas de fondo"
+224 "No hay pantalla de fondo editando un archivo denominado %s"
+225 "No se puede poner fondo a la £nica pantalla que se visualiza"
+226 "La pantalla s¢lo puede reducirse a %d hileras"
+227 "La pantalla no puede reducirse"
+228 "La pantalla no puede aumentarse"
+230 "Esta pantalla no puede suspenderse"
+231 "Interrumpido: teclas mapeadas descartadas"
+232 "vi: buffer temporario no liberado"
+233 "Esta terminal no tiene tecla %s"
+234 "S¢lo un buffer puede especificarse"
+235 "N£mero mayor que %lu"
+236 "Interrumpido"
+237 "No se puede crear archivo temporario"
+238 "Advertencia: %s no es un archivo regular"
+239 "%s ya se encuentra trabado, la sesi¢n es de lectura solamente"
+240 "%s: eliminar"
+241 "%s: cerrar"
+242 "%s: eliminar"
+243 "%s: eliminar"
+244 "Archivo de lectura solamente, no escrito; usar ! para alterar"
+245 " Archivo de lectura solamente, no escrito"
+246 "%s existe, no escrito; usar ! para alterar"
+247 "%s existe, no escrito"
+248 "Archivo parcial, no escrito; usar ! para alterar"
+249 "Archivo parcial, no escrito"
+250 "%s: archivo modificado m s recientemente que esta copia; usar ! para alterar"
+251 "%s: archivo modificado m s recientemente que esta copia"
+252 "%s: la traba de escritura no estaba disponible"
+253 "Escribiendo..."
+254 "%s: ADVERTENCIA: ARCHIVO TRUNCADO"
+255 "Ya se encuentra en el primer identificador de este grupo"
+256 "%s: nuevo archivo: %lu l¡neas, %lu caracteres"
+257 "%s: %lu l¡neas, %lu caracteres"
+258 "%s expandido a demasiados nombres de archivos"
+259 "%s: no es un archivo regular"
+260 "%s: no le pertenece"
+261 "%s: accesible por un usuario que no sea el propietario"
+262 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
+263 "Archivo modificado desde la £ltima escritura completa; escribir o usar :editar! para alterar"
+264 "Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterar"
+265 "El archivo es temporario; al salir se descartar n las modificaciones"
+266 "Archivo de lectura solamente, las modificaciones no se autoescriben"
+267 "Se reinici¢ el log"
+268 "confirmar? [snq]"
+269 "Presionar cualquier tecla para continuar: "
+270 "Presionar cualquier tecla para continuar [: para ingresar m s comandos ex]: "
+271 "Presionar cualquier tecla para continuar [q para salir]: "
+272 "Esta forma de %s requiere la interfase terminal ex"
+273 "Ingresando en la modalidad de entrada ex."
+274 "Comando fracasado, no hay archivo le¡do aun."
+275 " cont?"
+276 "Evento inesperado de car cter"
+277 "Evento inesperado de final de archivo"
+278 "No hay coincidencias para consulta"
+279 "Evento inesperado de interrupci¢n"
+280 "Evento inesperado de salida"
+281 "Evento inesperado de repintura"
+282 "Ya se encuentra en el £ltimo identificador de este grupo"
+283 "El comando %s requiere la interfase terminal ex"
+284 "Esta forma de %s no se encuentra soportada cuando se determina la opci¢n de edici¢n segura"
+285 "Evento inesperado de cadena"
+286 "Evento inesperado de tiempo excedido"
+287 "Evento inesperado de escritura"
+289 "Las expansiones de shell no se encuentran soportadas cuando se determina la opci¢n de edici¢n segura"
+290 "El comando %s no se encuentra soportado cuando se determina la opci¢n de edici¢n segura"
+291 "determinar: la opci¢n %s puede no estar desconectada"
+292 "El monitor es demasiado peque¤o."
+293 "agregado"
+294 "cambiado"
+295 "borrado"
+296 "unido"
+297 "movido"
+298 "desplazado"
+299 "arrancado"
+300 "l¡nea"
+301 "l¡neas"
+302 "Vi no se carg¢ con un int‚rprete Tcl"
+303 "Archivo modificado desde la £ltima escritura."
+304 "Expansi¢n de shell fracasada"
+304 "Expansi¢n de shell fracasada"
+305 "No hay opci¢n de edici¢n %s especificada"
+306 "Vi no se carg¢ con un int‚rprete Perl"
+307 "No hay comando ex para ejecutar"
+308 "Ingresar <CR> para ejecutar un comando, :q para salir"
+309 "Usar \"cscope ayuda\" para obtener ayuda"
+310 "No hay conexiones cscope corriendo"
+311 "%s: tipo de b£squeda desconocido: usar uno de %s"
+312 "%d: no existe esta sesi¢n cscope"
+313 "determinar: la opci¢n %s no puede conectarse nunca"
+314 "determinar: la opci¢n %s no puede determinarse nunca en 0"
+315 "%s: a¤adido: %lu l¡neas, %lu caracteres"
+316 "Evento inesperado de modificaci¢n de tama¤o"
+317 "%d archivos para editar"
diff --git a/contrib/nvi/catalog/spanish.check b/contrib/nvi/catalog/spanish.check
new file mode 100644
index 000000000000..c04785e3a2ef
--- /dev/null
+++ b/contrib/nvi/catalog/spanish.check
@@ -0,0 +1,35 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+ 2 209 "El archivo est  vac¡o"
+ 2 304 "Expansi¢n de shell fracasada"
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s: %lu l¡neas, %lu caracteresX
+ 2 %s: archivo de recuperaci¢n defectuosoX
+ 2 Copiando archivo para recuperaci¢n...X
+ 2 El buffer %s est  vac¡oX
+ 2 InterrumpidoX
+ 2 Las modificaciones no pueden recuperarse si la sesi¢n fallaX
+ 2 No hay patr¢n anterior de b£squedaX
+ 2 No se realiza log, no se puede deshacerX
+ 2 determinar: opci¢n %s no es booleanoX
+ 3 %s: eliminarX
+ 3 Archivo modificado desde la £ltima escritura completa; escribir o usar ! para alterarX
+ 4 Uso: %sX
+=========================
diff --git a/contrib/nvi/catalog/spell.ok b/contrib/nvi/catalog/spell.ok
new file mode 100644
index 000000000000..00be47141879
--- /dev/null
+++ b/contrib/nvi/catalog/spell.ok
@@ -0,0 +1,19 @@
+ARGMAX
+LC
+NL
+XXXX
+arg1
+arg2
+chys
+english
+english.base
+german.base
+langauge
+msg
+msg.c
+msgcat
+msgq
+nvi
+nvi's
+pathname
+sp
diff --git a/contrib/nvi/catalog/swedish b/contrib/nvi/catalog/swedish
new file mode 100644
index 000000000000..044b354fa070
--- /dev/null
+++ b/contrib/nvi/catalog/swedish
@@ -0,0 +1,317 @@
+VI_MESSAGE_CATALOG
+För långa raderX
+kan inte ta bort rad %luX
+kan inte lägga till på rad %luX
+kan inte sätta in på rad %luX
+kan inte lagra rad %luX
+kan inte hämta sista radenX
+Fel: kan inte hämta rad %luX
+LoggningsfilX
+Loggning utförs inte, ångra är inte möjligtX
+Inga ändringar att ångraX
+Loggning utförs inte, ångra är inte möjligtX
+Loggning utförs inte, ångra ångra är inte möjligtX
+Inga ändringar att återgöraX
+%s/%d: fel vid loggningX
+Vi:s standard in och ut måste gå till en terminalX
+Markering %s: inte sattX
+Markering %s: raden har tagits bortX
+Markering %s: markörpositionen finns inte längreX
+Fel: X
+ny filX
+namnet ändradesX
+ändradX
+oförändradX
+OLÅSTX
+inte skrivbarX
+rad %lu av %lu [%ld%%]X
+tom filX
+rad %luX
+Filen %s är ingen meddelandekatalogX
+Kan inte sätta standardvärde för %s flagganX
+Användning: %sX
+set: %s är en okänd flagga: "set all" visar alla flaggorX
+set: [no]%s flaggan kan inte ges ett värdeX
+set: %s flaggan är inte boleanskX
+set: %s flaggan: %sX
+set: %s flaggan: %s: för stort värdeX
+set: %s flaggan: %s är ett otillåtet talX
+set: %s flaggan är inte boleanskX
+Fönsterkolumnerna är för få, mindre än %dX
+Fönsterkolumnerna är för många, fler än %dX
+Fönsterraderna är för få, mindre än %dX
+Fönsterraderna är för många, fler än %dX
+Lisp flaggan är inte implementeradX
+meddelanden är inte avslagna: %sX
+meddelanden är inte påslagna: %sX
+
+Paragraph flaggan måste ges i teckengrupper om tvåX
+Section flaggan måste ges i teckengrupper om tvåX
+
+
+
+Standardbufferten är tomX
+Buffer %s är tomX
+Filer med radmatning i namnet kan inte återskapasX
+Ändringar kan inte återskapas om programmet krascharX
+Kopierar filen för återskapning...X
+Säkerhetskopiering misslyckades: %sX
+Ändringar kan inte återskapas om programmet krascharX
+Misslyckades att säkerhetskopiera filen: %sX
+Kopierar filen för återskapning...X
+Kan inte hitta information om användaridentitet %uX
+Kan inte låsa återskapningsfilenX
+Återskapningsfilens buffer överskrivenX
+ÅterskapningsfilX
+%s: Återskapningsfilen är korruptX
+%s: Återskapningsfilen är korruptX
+Det finns ingen fil %s, läsbar av dig, att återskapaX
+Det finns äldre versioner av denna fil som du kan återskapaX
+Det finns andra filer du kan återskapaX
+skickar inte email: %sX
+Filen är tom; inget att söka iX
+Kom till slutet på filen utan att hitta söksträngenX
+Ingen tidigare söksträngX
+Hittar inte söksträngenX
+Kom till början av filen utan att hitta söksträngenX
+Sökningen slog runtX
+Söker...X
+Inga icke skrivbara tecken funnaX
+Okänt kommandonamnX
+
+%s: kommandot är inte tillgängligt i "ex" lägeX
+Talet får inte vara nollX
+%s: Ogiltig radspecifikationX
+Fel i intern syntaxtabell (%s: %s)X
+Användning: %sX
+%s: temporärbuffert inte frisläpptX
+Offset är före rad 1X
+Offset är efter slutet på filenX
+@ med intervall exekverades när filen/fönstret ändradesX
+Global/v kommando exekverades när filen/fönstret ändradesX
+Ex kommando misslyckades: efterföljande kommandon ignoreradeX
+Ex kommando misslyckades: omdefinierade tangenter ignoreradeX
+Den andra adressen är mindre än den förstaX
+Inget namn på markering givetX
+\\ följs inte av / eller ?X
+Referens till ett radnummer mindre än 0X
+%s kommandot är inte käntX
+Värdet på adressen är för stortX
+Värdet på adressen är för litetX
+Otillåten adresskombinationX
+Otillåten adress: bara %lu rader finns i filenX
+Otillåten adress: filen är tomX
+%s kommandot tillåter inte en adress som är 0X
+Inga förkortningar att visaX
+Förkortningar måste sluta med ett "ord" teckenX
+Förkortningar kan inte innehålla mellanslag eller tabX
+Förkortningar kan inte blanda "ord"/"icke ord" tecken, utom i slutetX
+"%s" är ingen förkortningX
+Vi kommando misslyckades: omdefinierade tangenter ignoreradeX
+Inga fler filer att editeraX
+Inga tidigare filer att editeraX
+Inga tidigare filer att spela tillbakaX
+Ingen fillista att visaX
+Inget tidigare kommando att ersätta "!" medX
+Inget filnamn att ersätta %% medX
+Inget filnamn att ersätta # medX
+Fel: execl: %sX
+I/O fel: %sX
+Filen ändrad efter sista skrivning; spara eller använd !X
+Kan inte hitta hemkatalogX
+Ny nuvarande katalog: %sX
+Inga "cut buffers" att visaX
+%s kommandot kan inte används som del i ett "global" eller v kommandoX
+%s/%s: inte läst: varken du eller root är ägareX
+%s/%s: inte läst: du är inte ägareX
+%s/%s: inte läst: skrivbar av annan än ägarenX
+%s: inte läst: varken du eller root är ägareX
+%s: inte läst: du är inte ägareX
+%s: inte läst: skrivbar av annan än ägarenX
+Ingen nästa rad att sätta ihop medX
+Det finns inget i inmatningsmappningenX
+Det finns inget i kommandomappningenX
+%s tecknet kan inte mappas omX
+"%s" är inte ommappat just nuX
+Namn på markeringar måste vara ett tecken långaX
+%s finns, inget sparat; använd ! för att sparaX
+Ny exrc fil: %sX
+Målraden ligger inne i området som ska flyttasX
+Open kommandot kräver att open flaggan är sattX
+Open kommandot är inte implementerat ännuX
+Säkerhetskopiering av filen är inte möjligtX
+Filen säkerhetskopieradX
+%s expanderade till för många filnamnX
+Endast vanliga filer och namngivna rör kan läsasX
+%s: läslåset är otillgängligtX
+Läser...X
+%s: %lu rader, %lu teckenX
+Inga bakgrundsfönster att visaX
+Script kommandot finns bara i "vi" lägeX
+Inget kommando att exekveraX
+shiftwidth flaggan satt till 0X
+Talet har för stort värdeX
+Talet har för litet värdeX
+Reguljärt uttryck är givet; r flaggan är meningslösX
+#, l och p flaggorna kan inte kombineras med c flaggan i "vi" lägeX
+Ingen matchande text funnenX
+Inget tidigare märke har givitsX
+Det är färre än %s märken i stacken; använd :display t[ags]X
+Det finns ingen fil %s i märkesstacken; använd :display t[ags]X
+Tryck Enter för att fortsätta: X
+%s: märke inte funnetX
+%s: korrupt märke i %sX
+%s: märkets radnummer är bortom filslutetX
+Märkesstacken är tomX
+%s: söksträngen inte funnenX
+%d filer till att editeraX
+Buffert %s är tomX
+Bekräfta ändring? [n]X
+AvbrutenX
+Ingen tidigare buffert att exekveraX
+Inget tidigare reguljärt uttryckX
+%s kommandot kräver att en fil redan lästs inX
+Användning: %sX
+Visual kommandot kräver att open flaggan är sattX
+
+Tom filX
+Ingen tidigare F, f, T eller t sökningX
+%s inte funnenX
+Ingen tidigare fil att editeraX
+Markören är inte i ett talX
+Det resulterande talet är för stortX
+Det resulterande talet är för litetX
+Inget matchande tecken på denna radX
+Matchande tecken inte funnetX
+Det finns inga tecken att ersättaX
+Det finns inget fönster att byta tillX
+Tecken efter söksträng, radoffset och/eller z kommandotX
+Ingen tidigare söksträngX
+Sökningen slog runt till ursprungliga positionenX
+Förkortning överskred expanderingsgränsen: tecken har tagits bortX
+Ogiltigt tecken; använd "quote" för att sätta inX
+Redan i början på insättningenX
+Inga fler tecken att ta bortX
+Försök att gå bortom slutet på filenX
+Försök att gå bortom slutet på radenX
+Ingen förflyttning gjordX
+Redan i början på filenX
+Försök att gå före början på filenX
+Redan i första kolumnenX
+Buffertar måste anges före kommandotX
+Redan i slutet av filenX
+Redan på slutet av radenX
+%s är inte ett "vi" kommandoX
+Användning: %sX
+Inga tecken att ta bortX
+Q kommandot kräver "ex" i terminallägeX
+Inget kommando att repeteraX
+Filen är tomX
+%s kan inte användas som ett förflyttningskommandoX
+Redan i kommando lägeX
+Markören är inte i ett ordX
+
+Windows flaggans värde är för stor, största värde är %uX
+Lägg tillX
+ÄndraX
+KommandoX
+Sätt inX
+ErsättX
+Förflyttning bortom fönsterslutX
+Förflyttning till före fönstrets börjanX
+Fönstret måste vara större än %d rader för delningX
+Det finns inga fönster i bakgrundenX
+Det finns inget fönster i bakgrunden som editerar filen %sX
+Du får inte sätta ditt enda synliga fönster i bakgrundenX
+Fönstret kan bara krympa till %d raderX
+Fönstret kan inte krympaX
+Fönstret kan inte växaX
+
+Detta fönster kan inte pausasX
+Avbrutet: omdefinierade tangenter ignoreradeX
+vi: temporärbuffertar inte frisläpptaX
+Denna terminal har ingen %s tangentX
+Endast en buffert kan angesX
+Talet är större än %luX
+AvbrutetX
+Kan inte skapa temporär filX
+Warning: %s är inte en normal filX
+%s är redan låst, detta blir en icke skrivbar sessionX
+%s: ta bortX
+%s: stängX
+%s: ta bortX
+%s: ta bortX
+Ej skrivbar fil, filen inte sparad; använd ! för att skriva överX
+Ej skrivbar fil, filen inte sparadX
+%s finns, ej sparad; använd ! för att utföra operationenX
+%s finns, filen inte sparadX
+Ofullständig fil, filen inte sparad, använd ! för att skriva överX
+Ofullständig fil, filen inte sparadX
+%s: filen ändrad efter denna kopia togs; använd ! för att utföra operationenX
+%s: filen ändrad efter denna kopia togsX
+%s: skrivlåset är otillgängligtX
+Skriver...X
+%s: VARNING: FILEN TRUNKERADX
+Redan vid första märket i denna gruppX
+%s: ny fil: %lu rader, %lu teckenX
+%s: %lu rader, %lu teckenX
+%s expanderade till för många filnamnX
+%s är inte en normal filX
+%s ägs inte av digX
+%s är åtkomstbar av andra än ägarenX
+Filen har ändrats efter den sparats; spara eller använd !X
+Filen har ändrats efter den sparats; spara eller använd :edit!X
+Filen har ändrats efter den sparats; spara eller använd !X
+Filen är temporär; exit kastar bort ändringarnaX
+Ej skrivbar fil, ändringar har inte automatsparatsX
+Loggningen startar omX
+bekräfta? [ynq]X
+Tryck på en tangent för att fortsätta: X
+Tryck på en tangent för att fortsätta [: för att ge fler kommandon]: X
+Tryck på en tangent för att fortsätta [q för att avsluta]: X
+Den formen av %s kräver "ex" i terminallägeX
+Går till "ex" inmatningsläge.X
+Kommandot misslyckades, ingen fil inläst ännu.X
+ forts?X
+Oväntad teckenhändelseX
+Oväntad filslutshändelseX
+Sökningen hittade ingentingX
+Oväntad avbrottshändelseX
+Oväntad avslutningshändelseX
+Oväntad omritningshändelseX
+Redan vid sista märket i denna gruppX
+%s kommandot kräver "ex" i terminallägeX
+Den formen av %s är inte tillgänglig när secure edit flaggan är sattX
+Oväntad stränghändelseX
+Oväntad tidshändelseX
+Oväntad skrivhändelseX
+
+Skalexpansion är inte tillgänglig när secure edit flaggan är sattX
+%s kommandot är inte tillgänglig när secure edit flaggan är sattX
+set: %s kan inte slås avX
+Fönstret för litet.X
+tillagdaX
+ändradeX
+borttagnaX
+ihopsattaX
+flyttadeX
+flyttadeX
+inklistradeX
+radX
+raderX
+Vi har inte länkats med en Tcl tolkX
+Filen har ändrats efter den sparats.X
+Skalexpansion misslyckadesX
+Ingen %s edit flagga givenX
+Vi har inte länkats med en Perl tolkX
+Inga "ex" kommandon att exekveraX
+Tryck <CR> för att exekvera kommando, :q för att avslutaX
+Gör "cscope help" för hjälpX
+Inga cscope kopplingar körsX
+%s: okänd söktyp: använd en av %sX
+%d: ingen sådan cscope sessionX
+set: %s flaggan får aldrig slås påX
+set: %s flaggan får aldrig sättas till 0X
+%s: tillagt: %lu rader, %lu teckenX
+Oväntad storleksändringX
+%d filer att editeraX
diff --git a/contrib/nvi/catalog/swedish.base b/contrib/nvi/catalog/swedish.base
new file mode 100644
index 000000000000..43bf7765a25e
--- /dev/null
+++ b/contrib/nvi/catalog/swedish.base
@@ -0,0 +1,307 @@
+002 "För långa rader"
+003 "kan inte ta bort rad %lu"
+004 "kan inte lägga till på rad %lu"
+005 "kan inte sätta in på rad %lu"
+006 "kan inte lagra rad %lu"
+007 "kan inte hämta sista raden"
+008 "Fel: kan inte hämta rad %lu"
+009 "Loggningsfil"
+010 "Loggning utförs inte, ångra är inte möjligt"
+011 "Inga ändringar att ångra"
+012 "Loggning utförs inte, ångra är inte möjligt"
+013 "Loggning utförs inte, ångra ångra är inte möjligt"
+014 "Inga ändringar att återgöra"
+015 "%s/%d: fel vid loggning"
+016 "Vi:s standard in och ut måste gå till en terminal"
+017 "Markering %s: inte satt"
+018 "Markering %s: raden har tagits bort"
+019 "Markering %s: markörpositionen finns inte längre"
+020 "Fel: "
+021 "ny fil"
+022 "namnet ändrades"
+023 "ändrad"
+024 "oförändrad"
+025 "OLÅST"
+026 "inte skrivbar"
+027 "rad %lu av %lu [%ld%%]"
+028 "tom fil"
+029 "rad %lu"
+030 "Filen %s är ingen meddelandekatalog"
+031 "Kan inte sätta standardvärde för %s flaggan"
+032 "Användning: %s"
+033 "set: %s är en okänd flagga: "set all" visar alla flaggor"
+034 "set: [no]%s flaggan kan inte ges ett värde"
+035 "set: %s flaggan är inte boleansk"
+036 "set: %s flaggan: %s"
+037 "set: %s flaggan: %s: för stort värde"
+038 "set: %s flaggan: %s är ett otillåtet tal"
+039 "set: %s flaggan är inte boleansk"
+040 "Fönsterkolumnerna är för få, mindre än %d"
+041 "Fönsterkolumnerna är för många, fler än %d"
+042 "Fönsterraderna är för få, mindre än %d"
+043 "Fönsterraderna är för många, fler än %d"
+044 "Lisp flaggan är inte implementerad"
+045 "meddelanden är inte avslagna: %s"
+046 "meddelanden är inte påslagna: %s"
+048 "Paragraph flaggan måste ges i teckengrupper om två"
+049 "Section flaggan måste ges i teckengrupper om två"
+053 "Standardbufferten är tom"
+054 "Buffer %s är tom"
+055 "Filer med radmatning i namnet kan inte återskapas"
+056 "Ändringar kan inte återskapas om programmet kraschar"
+057 "Kopierar filen för återskapning..."
+058 "Säkerhetskopiering misslyckades: %s"
+059 "Ändringar kan inte återskapas om programmet kraschar"
+060 "Misslyckades att säkerhetskopiera filen: %s"
+061 "Kopierar filen för återskapning..."
+062 "Kan inte hitta information om användaridentitet %u"
+063 "Kan inte låsa återskapningsfilen"
+064 "Återskapningsfilens buffer överskriven"
+065 "Återskapningsfil"
+066 "%s: Återskapningsfilen är korrupt"
+067 "%s: Återskapningsfilen är korrupt"
+068 "Det finns ingen fil %s, läsbar av dig, att återskapa"
+069 "Det finns äldre versioner av denna fil som du kan återskapa"
+070 "Det finns andra filer du kan återskapa"
+071 "skickar inte email: %s"
+072 "Filen är tom; inget att söka i"
+073 "Kom till slutet på filen utan att hitta söksträngen"
+074 "Ingen tidigare söksträng"
+075 "Hittar inte söksträngen"
+076 "Kom till början av filen utan att hitta söksträngen"
+077 "Sökningen slog runt"
+078 "Söker..."
+079 "Inga icke skrivbara tecken funna"
+080 "Okänt kommandonamn"
+082 "%s: kommandot är inte tillgängligt i "ex" läge"
+083 "Talet får inte vara noll"
+084 "%s: Ogiltig radspecifikation"
+085 "Fel i intern syntaxtabell (%s: %s)"
+086 "Användning: %s"
+087 "%s: temporärbuffert inte frisläppt"
+088 "Offset är före rad 1"
+089 "Offset är efter slutet på filen"
+090 "@ med intervall exekverades när filen/fönstret ändrades"
+091 "Global/v kommando exekverades när filen/fönstret ändrades"
+092 "Ex kommando misslyckades: efterföljande kommandon ignorerade"
+093 "Ex kommando misslyckades: omdefinierade tangenter ignorerade"
+094 "Den andra adressen är mindre än den första"
+095 "Inget namn på markering givet"
+096 "\\ följs inte av / eller ?"
+097 "Referens till ett radnummer mindre än 0"
+098 "%s kommandot är inte känt"
+099 "Värdet på adressen är för stort"
+100 "Värdet på adressen är för litet"
+101 "Otillåten adresskombination"
+102 "Otillåten adress: bara %lu rader finns i filen"
+103 "Otillåten adress: filen är tom"
+104 "%s kommandot tillåter inte en adress som är 0"
+105 "Inga förkortningar att visa"
+106 "Förkortningar måste sluta med ett "ord" tecken"
+107 "Förkortningar kan inte innehålla mellanslag eller tab"
+108 "Förkortningar kan inte blanda "ord"/"icke ord" tecken, utom i slutet"
+109 ""%s" är ingen förkortning"
+110 "Vi kommando misslyckades: omdefinierade tangenter ignorerade"
+111 "Inga fler filer att editera"
+112 "Inga tidigare filer att editera"
+113 "Inga tidigare filer att spela tillbaka"
+114 "Ingen fillista att visa"
+115 "Inget tidigare kommando att ersätta "!" med"
+116 "Inget filnamn att ersätta %% med"
+117 "Inget filnamn att ersätta # med"
+118 "Fel: execl: %s"
+119 "I/O fel: %s"
+120 "Filen ändrad efter sista skrivning; spara eller använd !"
+121 "Kan inte hitta hemkatalog"
+122 "Ny nuvarande katalog: %s"
+123 "Inga "cut buffers" att visa"
+124 "%s kommandot kan inte används som del i ett "global" eller v kommando"
+125 "%s/%s: inte läst: varken du eller root är ägare"
+126 "%s/%s: inte läst: du är inte ägare"
+127 "%s/%s: inte läst: skrivbar av annan än ägaren"
+128 "%s: inte läst: varken du eller root är ägare"
+129 "%s: inte läst: du är inte ägare"
+130 "%s: inte läst: skrivbar av annan än ägaren"
+131 "Ingen nästa rad att sätta ihop med"
+132 "Det finns inget i inmatningsmappningen"
+133 "Det finns inget i kommandomappningen"
+134 "%s tecknet kan inte mappas om"
+135 ""%s" är inte ommappat just nu"
+136 "Namn på markeringar måste vara ett tecken långa"
+137 "%s finns, inget sparat; använd ! för att spara"
+138 "Ny exrc fil: %s"
+139 "Målraden ligger inne i området som ska flyttas"
+140 "Open kommandot kräver att open flaggan är satt"
+141 "Open kommandot är inte implementerat ännu"
+142 "Säkerhetskopiering av filen är inte möjligt"
+143 "Filen säkerhetskopierad"
+144 "%s expanderade till för många filnamn"
+145 "Endast vanliga filer och namngivna rör kan läsas"
+146 "%s: läslåset är otillgängligt"
+147 "Läser..."
+148 "%s: %lu rader, %lu tecken"
+149 "Inga bakgrundsfönster att visa"
+150 "Script kommandot finns bara i "vi" läge"
+151 "Inget kommando att exekvera"
+152 "shiftwidth flaggan satt till 0"
+153 "Talet har för stort värde"
+154 "Talet har för litet värde"
+155 "Reguljärt uttryck är givet; r flaggan är meningslös"
+156 "#, l och p flaggorna kan inte kombineras med c flaggan i "vi" läge"
+157 "Ingen matchande text funnen"
+158 "Inget tidigare märke har givits"
+159 "Det är färre än %s märken i stacken; använd :display t[ags]"
+160 "Det finns ingen fil %s i märkesstacken; använd :display t[ags]"
+161 "Tryck Enter för att fortsätta: "
+162 "%s: märke inte funnet"
+163 "%s: korrupt märke i %s"
+164 "%s: märkets radnummer är bortom filslutet"
+165 "Märkesstacken är tom"
+166 "%s: söksträngen inte funnen"
+167 "%d filer till att editera"
+168 "Buffert %s är tom"
+169 "Bekräfta ändring? [n]"
+170 "Avbruten"
+171 "Ingen tidigare buffert att exekvera"
+172 "Inget tidigare reguljärt uttryck"
+173 "%s kommandot kräver att en fil redan lästs in"
+174 "Användning: %s"
+175 "Visual kommandot kräver att open flaggan är satt"
+177 "Tom fil"
+178 "Ingen tidigare F, f, T eller t sökning"
+179 "%s inte funnen"
+180 "Ingen tidigare fil att editera"
+181 "Markören är inte i ett tal"
+182 "Det resulterande talet är för stort"
+183 "Det resulterande talet är för litet"
+184 "Inget matchande tecken på denna rad"
+185 "Matchande tecken inte funnet"
+186 "Det finns inga tecken att ersätta"
+187 "Det finns inget fönster att byta till"
+188 "Tecken efter söksträng, radoffset och/eller z kommandot"
+189 "Ingen tidigare söksträng"
+190 "Sökningen slog runt till ursprungliga positionen"
+191 "Förkortning överskred expanderingsgränsen: tecken har tagits bort"
+192 "Ogiltigt tecken; använd "quote" för att sätta in"
+193 "Redan i början på insättningen"
+194 "Inga fler tecken att ta bort"
+195 "Försök att gå bortom slutet på filen"
+196 "Försök att gå bortom slutet på raden"
+197 "Ingen förflyttning gjord"
+198 "Redan i början på filen"
+199 "Försök att gå före början på filen"
+200 "Redan i första kolumnen"
+201 "Buffertar måste anges före kommandot"
+202 "Redan i slutet av filen"
+203 "Redan på slutet av raden"
+204 "%s är inte ett "vi" kommando"
+205 "Användning: %s"
+206 "Inga tecken att ta bort"
+207 "Q kommandot kräver "ex" i terminalläge"
+208 "Inget kommando att repetera"
+209 "Filen är tom"
+210 "%s kan inte användas som ett förflyttningskommando"
+211 "Redan i kommando läge"
+212 "Markören är inte i ett ord"
+214 "Windows flaggans värde är för stor, största värde är %u"
+215 "Lägg till"
+216 "Ändra"
+217 "Kommando"
+218 "Sätt in"
+219 "Ersätt"
+220 "Förflyttning bortom fönsterslut"
+221 "Förflyttning till före fönstrets början"
+222 "Fönstret måste vara större än %d rader för delning"
+223 "Det finns inga fönster i bakgrunden"
+224 "Det finns inget fönster i bakgrunden som editerar filen %s"
+225 "Du får inte sätta ditt enda synliga fönster i bakgrunden"
+226 "Fönstret kan bara krympa till %d rader"
+227 "Fönstret kan inte krympa"
+228 "Fönstret kan inte växa"
+230 "Detta fönster kan inte pausas"
+231 "Avbrutet: omdefinierade tangenter ignorerade"
+232 "vi: temporärbuffertar inte frisläppta"
+233 "Denna terminal har ingen %s tangent"
+234 "Endast en buffert kan anges"
+235 "Talet är större än %lu"
+236 "Avbrutet"
+237 "Kan inte skapa temporär fil"
+238 "Warning: %s är inte en normal fil"
+239 "%s är redan låst, detta blir en icke skrivbar session"
+240 "%s: ta bort"
+241 "%s: stäng"
+242 "%s: ta bort"
+243 "%s: ta bort"
+244 "Ej skrivbar fil, filen inte sparad; använd ! för att skriva över"
+245 "Ej skrivbar fil, filen inte sparad"
+246 "%s finns, ej sparad; använd ! för att utföra operationen"
+247 "%s finns, filen inte sparad"
+248 "Ofullständig fil, filen inte sparad, använd ! för att skriva över"
+249 "Ofullständig fil, filen inte sparad"
+250 "%s: filen ändrad efter denna kopia togs; använd ! för att utföra operationen"
+251 "%s: filen ändrad efter denna kopia togs"
+252 "%s: skrivlåset är otillgängligt"
+253 "Skriver..."
+254 "%s: VARNING: FILEN TRUNKERAD"
+255 "Redan vid första märket i denna grupp"
+256 "%s: ny fil: %lu rader, %lu tecken"
+257 "%s: %lu rader, %lu tecken"
+258 "%s expanderade till för många filnamn"
+259 "%s är inte en normal fil"
+260 "%s ägs inte av dig"
+261 "%s är åtkomstbar av andra än ägaren"
+262 "Filen har ändrats efter den sparats; spara eller använd !"
+263 "Filen har ändrats efter den sparats; spara eller använd :edit!"
+264 "Filen har ändrats efter den sparats; spara eller använd !"
+265 "Filen är temporär; exit kastar bort ändringarna"
+266 "Ej skrivbar fil, ändringar har inte automatsparats"
+267 "Loggningen startar om"
+268 "bekräfta? [ynq]"
+269 "Tryck på en tangent för att fortsätta: "
+270 "Tryck på en tangent för att fortsätta [: för att ge fler kommandon]: "
+271 "Tryck på en tangent för att fortsätta [q för att avsluta]: "
+272 "Den formen av %s kräver "ex" i terminalläge"
+273 "Går till "ex" inmatningsläge."
+274 "Kommandot misslyckades, ingen fil inläst ännu."
+275 " forts?"
+276 "Oväntad teckenhändelse"
+277 "Oväntad filslutshändelse"
+278 "Sökningen hittade ingenting"
+279 "Oväntad avbrottshändelse"
+280 "Oväntad avslutningshändelse"
+281 "Oväntad omritningshändelse"
+282 "Redan vid sista märket i denna grupp"
+283 "%s kommandot kräver "ex" i terminalläge"
+284 "Den formen av %s är inte tillgänglig när secure edit flaggan är satt"
+285 "Oväntad stränghändelse"
+286 "Oväntad tidshändelse"
+287 "Oväntad skrivhändelse"
+289 "Skalexpansion är inte tillgänglig när secure edit flaggan är satt"
+290 "%s kommandot är inte tillgänglig när secure edit flaggan är satt"
+291 "set: %s kan inte slås av"
+292 "Fönstret för litet."
+293 "tillagda"
+294 "ändrade"
+295 "borttagna"
+296 "ihopsatta"
+297 "flyttade"
+298 "flyttade"
+299 "inklistrade"
+300 "rad"
+301 "rader"
+302 "Vi har inte länkats med en Tcl tolk"
+303 "Filen har ändrats efter den sparats."
+304 "Skalexpansion misslyckades"
+305 "Ingen %s edit flagga given"
+306 "Vi har inte länkats med en Perl tolk"
+307 "Inga "ex" kommandon att exekvera"
+308 "Tryck <CR> för att exekvera kommando, :q för att avsluta"
+309 "Gör "cscope help" för hjälp"
+310 "Inga cscope kopplingar körs"
+311 "%s: okänd söktyp: använd en av %s"
+312 "%d: ingen sådan cscope session"
+313 "set: %s flaggan får aldrig slås på"
+314 "set: %s flaggan får aldrig sättas till 0"
+315 "%s: tillagt: %lu rader, %lu tecken"
+316 "Oväntad storleksändring"
+317 "%d filer att editera"
diff --git a/contrib/nvi/catalog/swedish.check b/contrib/nvi/catalog/swedish.check
new file mode 100644
index 000000000000..c70b9bbd4095
--- /dev/null
+++ b/contrib/nvi/catalog/swedish.check
@@ -0,0 +1,34 @@
+Unused message id's (this is okay):
+001
+047
+050
+051
+052
+081
+176
+213
+229
+288
+=========================
+MISSING ERROR MESSAGES (Please add!):
+=========================
+Extra error messages (just delete them):
+=========================
+MESSAGES WITH THE SAME MESSAGE ID's (FIX!):
+=========================
+Duplicate messages, both id and message (this is okay):
+=========================
+Duplicate messages, just message (this is okay):
+ 2 %s expanderade till för många filnamnX
+ 2 %s: %lu rader, %lu teckenX
+ 2 %s: Återskapningsfilen är korruptX
+ 2 Filen har ändrats efter den sparats; spara eller använd !X
+ 2 Ingen tidigare söksträngX
+ 2 Kopierar filen för återskapning...X
+ 2 Loggning utförs inte, ångra är inte möjligtX
+ 2 flyttadeX
+ 2 set: %s flaggan är inte boleanskX
+ 2 Ändringar kan inte återskapas om programmet krascharX
+ 3 %s: ta bortX
+ 4 Användning: %sX
+=========================
diff --git a/contrib/nvi/catalog/swedish.owner b/contrib/nvi/catalog/swedish.owner
new file mode 100644
index 000000000000..2b36f2d324b8
--- /dev/null
+++ b/contrib/nvi/catalog/swedish.owner
@@ -0,0 +1 @@
+Jan Djarv <jan.djarv@mbox200.swipnet.se>
diff --git a/contrib/nvi/cl/README.signal b/contrib/nvi/cl/README.signal
new file mode 100644
index 000000000000..7faa45673b75
--- /dev/null
+++ b/contrib/nvi/cl/README.signal
@@ -0,0 +1,174 @@
+# @(#)README.signal 10.1 (Berkeley) 6/23/95
+
+There are six (normally) asynchronous actions about which vi cares:
+SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
+
+The assumptions:
+ 1: The DB routines are not reentrant.
+ 2: The curses routines may not be reentrant.
+ 3: Neither DB nor curses will restart system calls.
+
+XXX
+Note, most C library functions don't restart system calls. So, we should
+*probably* start blocking around any imported function that we don't know
+doesn't make a system call. This is going to be a genuine annoyance...
+
+SIGHUP, SIGTERM
+ Used for file recovery. The DB routines can't be reentered, nor
+ can they handle interrupted system calls, so the vi routines that
+ call DB block signals. This means that DB routines could be
+ called at interrupt time, if necessary.
+
+SIGQUIT
+ Disabled by the signal initialization routines. Historically, ^\
+ switched vi into ex mode, and we continue that practice.
+
+SIGWINCH:
+ The interrupt routine sets a global bit which is checked by the
+ key-read routine, so there are no reentrancy issues. This means
+ that the screen will not resize until vi runs out of keys, but
+ that doesn't seem like a problem.
+
+SIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has
+to permit the user to interrupt long-running operations. Generally, a
+search, substitution or read/write is done on a large file, or, the user
+creates a key mapping with an infinite loop. This problem will become
+worse as more complex semantics are added to vi, especially things like
+making it a pure text widget. There are four major solutions on the table,
+each of which have minor permutations.
+
+1: Run in raw mode.
+
+ The up side is that there's no asynchronous behavior to worry about,
+ and obviously no reentrancy problems. The down side is that it's easy
+ to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
+ like an interrupt) and it's easy to get into places where we won't see
+ interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
+ historic implementations of vi). Periodically reading the terminal
+ input buffer might solve the latter problem, but it's not going to be
+ pretty.
+
+ Also, we're going to be checking for ^C's and ^Z's both, all over
+ the place -- I hate to litter the source code with that. For example,
+ the historic version of vi didn't permit you to suspend the screen if
+ you were on the colon command line. This isn't right. ^Z isn't a vi
+ command, it's a terminal event. (Dammit.)
+
+2: Run in cbreak mode. There are two problems in this area. First, the
+ current curses implementations (both System V and Berkeley) don't give
+ you clean cbreak modes. For example, the IEXTEN bit is left on, turning
+ on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
+ the exception that flow control and signals are turned on, and curses
+ cbreak mode doesn't give you this.
+
+ We can either set raw mode and twiddle the tty, or cbreak mode and
+ twiddle the tty. I chose to use raw mode, on the grounds that raw
+ mode is better defined and I'm less likely to be surprised by a curses
+ implementation down the road. The twiddling consists of setting ISIG,
+ IXON/IXOFF, and disabling some of the interrupt characters (see the
+ comments in cl_init.c). This is all found in historic System V (SVID
+ 3) and POSIX 1003.1-1992, so it should be fairly portable.
+
+ The second problem is that vi permits you to enter literal signal
+ characters, e.g. ^V^C. There are two possible solutions. First, you
+ can turn off signals when you get a ^V, but that means that a network
+ packet containing ^V and ^C will lose, since the ^C may take effect
+ before vi reads the ^V. (This is particularly problematic if you're
+ talking over a protocol that recognizes signals locally and sends OOB
+ packets when it sees them.) Second, you can turn the ^C into a literal
+ character in vi, but that means that there's a race between entering
+ ^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
+ Also, the second solution doesn't work for flow control characters, as
+ they aren't delivered to the program as signals.
+
+ Generally, this is what historic vi did. (It didn't have the curses
+ problems because it didn't use curses.) It entered signals following
+ ^V characters into the input stream, (which is why there's no way to
+ enter a literal flow control character).
+
+3: Run in mostly raw mode; turn signals on when doing an operation the
+ user might want to interrupt, but leave them off most of the time.
+
+ This works well for things like file reads and writes. This doesn't
+ work well for trying to detect infinite maps. The problem is that
+ you can write the code so that you don't have to turn on interrupts
+ per keystroke, but the code isn't pretty and it's hard to make sure
+ that an optimization doesn't cover up an infinite loop. This also
+ requires interaction or state between the vi parser and the key
+ reading routines, as an infinite loop may still be returning keys
+ to the parser.
+
+ Also, if the user inserts an interrupt into the tty queue while the
+ interrupts are turned off, the key won't be treated as an interrupt,
+ and requiring the user to pound the keyboard to catch an interrupt
+ window is nasty.
+
+4: Run in mostly raw mode, leaving signals on all of the time. Done
+ by setting raw mode, and twiddling the tty's termios ISIG bit.
+
+ This works well for the interrupt cases, because the code only has
+ to check to see if the interrupt flag has been set, and can otherwise
+ ignore signals. It's also less likely that we'll miss a case, and we
+ don't have to worry about synchronizing between the vi parser and the
+ key read routines.
+
+ The down side is that we have to turn signals off if the user wants
+ to enter a literal character (e.g. ^V^C). If the user enters the
+ combination fast enough, or as part of a single network packet,
+ the text input routines will treat it as a signal instead of as a
+ literal character. To some extent, we have this problem already,
+ since we turn off flow control so that the user can enter literal
+ XON/XOFF characters.
+
+ This is probably the easiest to code, and provides the smoothest
+ programming interface.
+
+There are a couple of other problems to consider.
+
+First, System V's curses doesn't handle SIGTSTP correctly. If you use the
+newterm() interface, the TSTP signal will leave you in raw mode, and the
+final endwin() will leave you in the correct shell mode. If you use the
+initscr() interface, the TSTP signal will return you to the correct shell
+mode, but the final endwin() will leave you in raw mode. There you have
+it: proof that drug testing is not making any significant headway in the
+computer industry. The 4BSD curses is deficient in that it does not have
+an interface to the terminal keypad. So, regardless, we have to do our
+own SIGTSTP handling.
+
+The problem with this is that if we do our own SIGTSTP handling, in either
+models #3 or #4, we're going to have to call curses routines at interrupt
+time, which means that we might be reentering curses, which is something we
+don't want to do.
+
+Second, SIGTSTP has its own little problems. It's broadcast to the entire
+process group, not sent to a single process. The scenario goes something
+like this: the shell execs the mail program, which execs vi. The user hits
+^Z, and all three programs get the signal, in some random order. The mail
+program goes to sleep immediately (since it probably didn't have a SIGTSTP
+handler in place). The shell gets a SIGCHLD, does a wait, and finds out
+that the only child in its foreground process group (of which it's aware)
+is asleep. It then optionally resets the terminal (because the modes aren't
+how it left them), and starts prompting the user for input. The problem is
+that somewhere in the middle of all of this, vi is resetting the terminal,
+and getting ready to send a SIGTSTP to the process group in order to put
+itself to sleep. There's a solution to all of this: when vi starts, it puts
+itself into its own process group, and then only it (and possible child
+processes) receive the SIGTSTP. This permits it to clean up the terminal
+and switch back to the original process group, where it sends that process
+group a SIGTSTP, putting everyone to sleep and waking the shell.
+
+Third, handing SIGTSTP asynchronously is further complicated by the child
+processes vi may fork off. If vi calls ex, ex resets the terminal and
+starts running some filter, and SIGTSTP stops them both, vi has to know
+when it restarts that it can't repaint the screen until ex's child has
+finished running. This is solveable, but it's annoying.
+
+Well, somebody had to make a decision, and this is the way it's going to be
+(unless I get talked out of it). SIGINT is handled asynchronously, so
+that we can pretty much guarantee that the user can interrupt any operation
+at any time. SIGTSTP is handled synchronously, so that we don't have to
+reenter curses and so that we don't have to play the process group games.
+^Z is recognized in the standard text input and command modes. (^Z should
+also be recognized during operations that may potentially take a long time.
+The simplest solution is probably to twiddle the tty, install a handler for
+SIGTSTP, and then restore normal tty modes when the operation is complete.)
diff --git a/contrib/nvi/cl/cl.h b/contrib/nvi/cl/cl.h
new file mode 100644
index 000000000000..2ef2b8d1f81e
--- /dev/null
+++ b/contrib/nvi/cl/cl.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)cl.h 10.19 (Berkeley) 9/24/96
+ */
+
+typedef struct _cl_private {
+ CHAR_T ibuf[256]; /* Input keys. */
+
+ int eof_count; /* EOF count. */
+
+ struct termios orig; /* Original terminal values. */
+ struct termios ex_enter;/* Terminal values to enter ex. */
+ struct termios vi_enter;/* Terminal values to enter vi. */
+
+ char *el; /* Clear to EOL terminal string. */
+ char *cup; /* Cursor movement terminal string. */
+ char *cuu1; /* Cursor up terminal string. */
+ char *rmso, *smso; /* Inverse video terminal strings. */
+ char *smcup, *rmcup; /* Terminal start/stop strings. */
+
+ int killersig; /* Killer signal. */
+#define INDX_HUP 0
+#define INDX_INT 1
+#define INDX_TERM 2
+#define INDX_WINCH 3
+#define INDX_MAX 4 /* Original signal information. */
+ struct sigaction oact[INDX_MAX];
+
+ enum { /* Tty group write mode. */
+ TGW_UNKNOWN=0, TGW_SET, TGW_UNSET } tgw;
+
+ enum { /* Terminal initialization strings. */
+ TE_SENT=0, TI_SENT } ti_te;
+
+#define CL_IN_EX 0x0001 /* Currently running ex. */
+#define CL_RENAME 0x0002 /* X11 xterm icon/window renamed. */
+#define CL_RENAME_OK 0x0004 /* User wants the windows renamed. */
+#define CL_SCR_EX_INIT 0x0008 /* Ex screen initialized. */
+#define CL_SCR_VI_INIT 0x0010 /* Vi screen initialized. */
+#define CL_SIGHUP 0x0020 /* SIGHUP arrived. */
+#define CL_SIGINT 0x0040 /* SIGINT arrived. */
+#define CL_SIGTERM 0x0080 /* SIGTERM arrived. */
+#define CL_SIGWINCH 0x0100 /* SIGWINCH arrived. */
+#define CL_STDIN_TTY 0x0200 /* Talking to a terminal. */
+ u_int32_t flags;
+} CL_PRIVATE;
+
+#define CLP(sp) ((CL_PRIVATE *)((sp)->gp->cl_private))
+#define GCLP(gp) ((CL_PRIVATE *)gp->cl_private)
+
+/* Return possibilities from the keyboard read routine. */
+typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_INTR, INP_TIMEOUT } input_t;
+
+/* The screen line relative to a specific window. */
+#define RLNO(sp, lno) (sp)->woff + (lno)
+
+/* X11 xterm escape sequence to rename the icon/window. */
+#define XTERM_RENAME "\033]0;%s\007"
+
+/*
+ * XXX
+ * Some implementations of curses.h don't define these for us. Used for
+ * compatibility only.
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#include "cl_extern.h"
diff --git a/contrib/nvi/cl/cl_bsd.c b/contrib/nvi/cl/cl_bsd.c
new file mode 100644
index 000000000000..4a06a54af2f1
--- /dev/null
+++ b/contrib/nvi/cl/cl_bsd.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_bsd.c 8.29 (Berkeley) 7/1/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <curses.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "cl.h"
+
+static char *ke; /* Keypad on. */
+static char *ks; /* Keypad off. */
+static char *vb; /* Visible bell string. */
+
+/*
+ * HP's support the entire System V curses package except for the tigetstr
+ * and tigetnum functions. Ultrix supports the BSD curses package except
+ * for the idlok function. Cthulu only knows why. Break things up into a
+ * minimal set of functions.
+ */
+
+#ifndef HAVE_CURSES_ADDNSTR
+/*
+ * addnstr --
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_ADDNSTR
+ * PUBLIC: int addnstr __P((char *, int));
+ * PUBLIC: #endif
+ */
+int
+addnstr(s, n)
+ char *s;
+ int n;
+{
+ int ch;
+
+ while (n-- && (ch = *s++))
+ addch(ch);
+ return (OK);
+}
+#endif
+
+#ifndef HAVE_CURSES_BEEP
+/*
+ * beep --
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_BEEP
+ * PUBLIC: void beep __P((void));
+ * PUBLIC: #endif
+ */
+void
+beep()
+{
+ (void)write(1, "\007", 1); /* '\a' */
+}
+#endif /* !HAVE_CURSES_BEEP */
+
+#ifndef HAVE_CURSES_FLASH
+/*
+ * flash --
+ * Flash the screen.
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_FLASH
+ * PUBLIC: void flash __P((void));
+ * PUBLIC: #endif
+ */
+void
+flash()
+{
+ if (vb != NULL) {
+ (void)tputs(vb, 1, cl_putchar);
+ (void)fflush(stdout);
+ } else
+ beep();
+}
+#endif /* !HAVE_CURSES_FLASH */
+
+#ifndef HAVE_CURSES_IDLOK
+/*
+ * idlok --
+ * Turn on/off hardware line insert/delete.
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_IDLOK
+ * PUBLIC: void idlok __P((WINDOW *, int));
+ * PUBLIC: #endif
+ */
+void
+idlok(win, bf)
+ WINDOW *win;
+ int bf;
+{
+ return;
+}
+#endif /* !HAVE_CURSES_IDLOK */
+
+#ifndef HAVE_CURSES_KEYPAD
+/*
+ * keypad --
+ * Put the keypad/cursor arrows into or out of application mode.
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_KEYPAD
+ * PUBLIC: int keypad __P((void *, int));
+ * PUBLIC: #endif
+ */
+int
+keypad(a, on)
+ void *a;
+ int on;
+{
+ char *p;
+
+ if ((p = tigetstr(on ? "smkx" : "rmkx")) != (char *)-1) {
+ (void)tputs(p, 0, cl_putchar);
+ (void)fflush(stdout);
+ }
+ return (0);
+}
+#endif /* !HAVE_CURSES_KEYPAD */
+
+#ifndef HAVE_CURSES_NEWTERM
+/*
+ * newterm --
+ * Create a new curses screen.
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_NEWTERM
+ * PUBLIC: void *newterm __P((const char *, FILE *, FILE *));
+ * PUBLIC: #endif
+ */
+void *
+newterm(a, b, c)
+ const char *a;
+ FILE *b, *c;
+{
+ return (initscr());
+}
+#endif /* !HAVE_CURSES_NEWTERM */
+
+#ifndef HAVE_CURSES_SETUPTERM
+/*
+ * setupterm --
+ * Set up terminal.
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_SETUPTERM
+ * PUBLIC: void setupterm __P((char *, int, int *));
+ * PUBLIC: #endif
+ */
+void
+setupterm(ttype, fno, errp)
+ char *ttype;
+ int fno, *errp;
+{
+ static char buf[2048];
+ char *p;
+
+ if ((*errp = tgetent(buf, ttype)) > 0) {
+ if (ke != NULL)
+ free(ke);
+ ke = ((p = tigetstr("rmkx")) == (char *)-1) ?
+ NULL : strdup(p);
+ if (ks != NULL)
+ free(ks);
+ ks = ((p = tigetstr("smkx")) == (char *)-1) ?
+ NULL : strdup(p);
+ if (vb != NULL)
+ free(vb);
+ vb = ((p = tigetstr("flash")) == (char *)-1) ?
+ NULL : strdup(p);
+ }
+}
+#endif /* !HAVE_CURSES_SETUPTERM */
+
+#ifndef HAVE_CURSES_TIGETSTR
+/* Terminfo-to-termcap translation table. */
+typedef struct _tl {
+ char *terminfo; /* Terminfo name. */
+ char *termcap; /* Termcap name. */
+} TL;
+static const TL list[] = {
+ "cols", "co", /* Terminal columns. */
+ "cup", "cm", /* Cursor up. */
+ "cuu1", "up", /* Cursor up. */
+ "el", "ce", /* Clear to end-of-line. */
+ "flash", "vb", /* Visible bell. */
+ "kcub1", "kl", /* Cursor left. */
+ "kcud1", "kd", /* Cursor down. */
+ "kcuf1", "kr", /* Cursor right. */
+ "kcuu1", "ku", /* Cursor up. */
+ "kdch1", "kD", /* Delete character. */
+ "kdl1", "kL", /* Delete line. */
+ "ked", "kS", /* Delete to end of screen. */
+ "kel", "kE", /* Delete to eol. */
+ "khome", "kh", /* Go to sol. */
+ "kich1", "kI", /* Insert at cursor. */
+ "kil1", "kA", /* Insert line. */
+ "kind", "kF", /* Scroll down. */
+ "kll", "kH", /* Go to eol. */
+ "knp", "kN", /* Page down. */
+ "kpp", "kP", /* Page up. */
+ "kri", "kR", /* Scroll up. */
+ "lines", "li", /* Terminal lines. */
+ "rmcup", "te", /* Terminal end string. */
+ "rmkx", "ke", /* Exit "keypad-transmit" mode. */
+ "rmso", "se", /* Standout end. */
+ "smcup", "ti", /* Terminal initialization string. */
+ "smkx", "ks", /* Enter "keypad-transmit" mode. */
+ "smso", "so", /* Standout begin. */
+};
+
+#ifdef _AIX
+/*
+ * AIX's implementation for function keys greater than 10 is different and
+ * only goes as far as 36.
+ */
+static const char codes[] = {
+/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
+/* 11-20 */ '<', '>', '!', '@', '#', '$', '%', '^', '&', '*',
+/* 21-30 */ '(', ')', '-', '_', '+', ',', ':', '?', '[', ']',
+/* 31-36 */ '{', '}', '|', '~', '/', '='
+};
+
+#else
+
+/*
+ * !!!
+ * Historically, the 4BSD termcap code didn't support functions keys greater
+ * than 9. This was silently enforced -- asking for key k12 would return the
+ * value for k1. We try and get around this by using the tables specified in
+ * the terminfo(TI_ENV) man page from the 3rd Edition SVID. This assumes the
+ * implementors of any System V compatibility code or an extended termcap used
+ * those codes.
+ */
+static const char codes[] = {
+/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
+/* 11-19 */ '1', '2', '3', '4', '5', '6', '7', '8', '9',
+/* 20-63 */ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+};
+#endif /* _AIX */
+
+/*
+ * lcmp --
+ * list comparison routine for bsearch.
+ */
+static int
+lcmp(a, b)
+ const void *a, *b;
+{
+ return (strcmp(a, ((TL *)b)->terminfo));
+}
+
+/*
+ * tigetstr --
+ *
+ * Vendors put the prototype for tigetstr into random include files, including
+ * <term.h>, which we can't include because it makes other systems unhappy.
+ * Try and work around the problem, since we only care about the return value.
+ *
+ * PUBLIC: #ifdef HAVE_CURSES_TIGETSTR
+ * PUBLIC: char *tigetstr();
+ * PUBLIC: #else
+ * PUBLIC: char *tigetstr __P((char *));
+ * PUBLIC: #endif
+ */
+char *
+tigetstr(name)
+ char *name;
+{
+ static char sbuf[256];
+ TL *tlp;
+ int n;
+ char *p, keyname[3];
+
+ if ((tlp = bsearch(name,
+ list, sizeof(list) / sizeof(TL), sizeof(TL), lcmp)) == NULL) {
+#ifdef _AIX
+ if (name[0] == 'k' &&
+ name[1] == 'f' && (n = atoi(name + 2)) <= 36) {
+ keyname[0] = 'k';
+ keyname[1] = codes[n];
+ keyname[2] = '\0';
+#else
+ if (name[0] == 'k' &&
+ name[1] == 'f' && (n = atoi(name + 2)) <= 63) {
+ keyname[0] = n <= 10 ? 'k' : 'F';
+ keyname[1] = codes[n];
+ keyname[2] = '\0';
+#endif
+ name = keyname;
+ }
+ } else
+ name = tlp->termcap;
+
+ p = sbuf;
+#ifdef _AIX
+ return ((p = tgetstr(name, &p)) == NULL ? (char *)-1 : strcpy(sbuf, p));
+#else
+ return (tgetstr(name, &p) == NULL ? (char *)-1 : sbuf);
+#endif
+}
+
+/*
+ * tigetnum --
+ *
+ * PUBLIC: #ifndef HAVE_CURSES_TIGETSTR
+ * PUBLIC: int tigetnum __P((char *));
+ * PUBLIC: #endif
+ */
+int
+tigetnum(name)
+ char *name;
+{
+ TL *tlp;
+ int val;
+
+ if ((tlp = bsearch(name,
+ list, sizeof(list) / sizeof(TL), sizeof(TL), lcmp)) != NULL) {
+ name = tlp->termcap;
+ }
+
+ return ((val = tgetnum(name)) == -1 ? -2 : val);
+}
+#endif /* !HAVE_CURSES_TIGETSTR */
diff --git a/contrib/nvi/cl/cl_funcs.c b/contrib/nvi/cl/cl_funcs.c
new file mode 100644
index 000000000000..40315ee85e89
--- /dev/null
+++ b/contrib/nvi/cl/cl_funcs.c
@@ -0,0 +1,704 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_funcs.c 10.50 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <curses.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "cl.h"
+
+/*
+ * cl_addstr --
+ * Add len bytes from the string at the cursor, advancing the cursor.
+ *
+ * PUBLIC: int cl_addstr __P((SCR *, const char *, size_t));
+ */
+int
+cl_addstr(sp, str, len)
+ SCR *sp;
+ const char *str;
+ size_t len;
+{
+ CL_PRIVATE *clp;
+ size_t oldy, oldx;
+ int iv;
+
+ clp = CLP(sp);
+
+ /*
+ * If ex isn't in control, it's the last line of the screen and
+ * it's a split screen, use inverse video.
+ */
+ iv = 0;
+ getyx(stdscr, oldy, oldx);
+ if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
+ oldy == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) {
+ iv = 1;
+ (void)standout();
+ }
+
+ if (addnstr(str, len) == ERR)
+ return (1);
+
+ if (iv)
+ (void)standend();
+ return (0);
+}
+
+/*
+ * cl_attr --
+ * Toggle a screen attribute on/off.
+ *
+ * PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int));
+ */
+int
+cl_attr(sp, attribute, on)
+ SCR *sp;
+ scr_attr_t attribute;
+ int on;
+{
+ CL_PRIVATE *clp;
+
+ clp = CLP(sp);
+
+ switch (attribute) {
+ case SA_ALTERNATE:
+ /*
+ * !!!
+ * There's a major layering violation here. The problem is that the
+ * X11 xterm screen has what's known as an "alternate" screen. Some
+ * xterm termcap/terminfo entries include sequences to switch to/from
+ * that alternate screen as part of the ti/te (smcup/rmcup) strings.
+ * Vi runs in the alternate screen, so that you are returned to the
+ * same screen contents on exit from vi that you had when you entered
+ * vi. Further, when you run :shell, or :!date or similar ex commands,
+ * you also see the original screen contents. This wasn't deliberate
+ * on vi's part, it's just that it historically sent terminal init/end
+ * sequences at those times, and the addition of the alternate screen
+ * sequences to the strings changed the behavior of vi. The problem
+ * caused by this is that we don't want to switch back to the alternate
+ * screen while getting a new command from the user, when the user is
+ * continuing to enter ex commands, e.g.:
+ *
+ * :!date <<< switch to original screen
+ * [Hit return to continue] <<< prompt user to continue
+ * :command <<< get command from user
+ *
+ * Note that the :command input is a true vi input mode, e.g., input
+ * maps and abbreviations are being done. So, we need to be able to
+ * switch back into the vi screen mode, without flashing the screen.
+ *
+ * To make matters worse, the curses initscr() and endwin() calls will
+ * do this automatically -- so, this attribute isn't as controlled by
+ * the higher level screen as closely as one might like.
+ */
+ if (on) {
+ if (clp->ti_te != TI_SENT) {
+ clp->ti_te = TI_SENT;
+ if (clp->smcup == NULL)
+ (void)cl_getcap(sp, "smcup", &clp->smcup);
+ if (clp->smcup != NULL)
+ (void)tputs(clp->smcup, 1, cl_putchar);
+ }
+ } else
+ if (clp->ti_te != TE_SENT) {
+ clp->ti_te = TE_SENT;
+ if (clp->rmcup == NULL)
+ (void)cl_getcap(sp, "rmcup", &clp->rmcup);
+ if (clp->rmcup != NULL)
+ (void)tputs(clp->rmcup, 1, cl_putchar);
+ (void)fflush(stdout);
+ }
+ (void)fflush(stdout);
+ break;
+ case SA_INVERSE:
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
+ if (clp->smso == NULL)
+ return (1);
+ if (on)
+ (void)tputs(clp->smso, 1, cl_putchar);
+ else
+ (void)tputs(clp->rmso, 1, cl_putchar);
+ (void)fflush(stdout);
+ } else {
+ if (on)
+ (void)standout();
+ else
+ (void)standend();
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * cl_baud --
+ * Return the baud rate.
+ *
+ * PUBLIC: int cl_baud __P((SCR *, u_long *));
+ */
+int
+cl_baud(sp, ratep)
+ SCR *sp;
+ u_long *ratep;
+{
+ CL_PRIVATE *clp;
+
+ /*
+ * XXX
+ * There's no portable way to get a "baud rate" -- cfgetospeed(3)
+ * returns the value associated with some #define, which we may
+ * never have heard of, or which may be a purely local speed. Vi
+ * only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
+ * Try and detect the slow ones, and default to fast.
+ */
+ clp = CLP(sp);
+ switch (cfgetospeed(&clp->orig)) {
+ case B50:
+ case B75:
+ case B110:
+ case B134:
+ case B150:
+ case B200:
+ case B300:
+ case B600:
+ *ratep = 600;
+ break;
+ case B1200:
+ *ratep = 1200;
+ break;
+ default:
+ *ratep = 9600;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cl_bell --
+ * Ring the bell/flash the screen.
+ *
+ * PUBLIC: int cl_bell __P((SCR *));
+ */
+int
+cl_bell(sp)
+ SCR *sp;
+{
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
+ (void)write(STDOUT_FILENO, "\07", 1); /* \a */
+ else {
+ /*
+ * Vi has an edit option which determines if the terminal
+ * should be beeped or the screen flashed.
+ */
+ if (O_ISSET(sp, O_FLASH))
+ (void)flash();
+ else
+ (void)beep();
+ }
+ return (0);
+}
+
+/*
+ * cl_clrtoeol --
+ * Clear from the current cursor to the end of the line.
+ *
+ * PUBLIC: int cl_clrtoeol __P((SCR *));
+ */
+int
+cl_clrtoeol(sp)
+ SCR *sp;
+{
+ return (clrtoeol() == ERR);
+}
+
+/*
+ * cl_cursor --
+ * Return the current cursor position.
+ *
+ * PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *));
+ */
+int
+cl_cursor(sp, yp, xp)
+ SCR *sp;
+ size_t *yp, *xp;
+{
+ /*
+ * The curses screen support splits a single underlying curses screen
+ * into multiple screens to support split screen semantics. For this
+ * reason the returned value must be adjusted to be relative to the
+ * current screen, and not absolute. Screens that implement the split
+ * using physically distinct screens won't need this hack.
+ */
+ getyx(stdscr, *yp, *xp);
+ *yp -= sp->woff;
+ return (0);
+}
+
+/*
+ * cl_deleteln --
+ * Delete the current line, scrolling all lines below it.
+ *
+ * PUBLIC: int cl_deleteln __P((SCR *));
+ */
+int
+cl_deleteln(sp)
+ SCR *sp;
+{
+ CHAR_T ch;
+ CL_PRIVATE *clp;
+ size_t col, lno, spcnt, oldy, oldx;
+
+ clp = CLP(sp);
+
+ /*
+ * This clause is required because the curses screen uses reverse
+ * video to delimit split screens. If the screen does not do this,
+ * this code won't be necessary.
+ *
+ * If the bottom line was in reverse video, rewrite it in normal
+ * video before it's scrolled.
+ *
+ * Check for the existence of a chgat function; XSI requires it, but
+ * historic implementations of System V curses don't. If it's not
+ * a #define, we'll fall back to doing it by hand, which is slow but
+ * acceptable.
+ *
+ * By hand means walking through the line, retrieving and rewriting
+ * each character. Curses has no EOL marker, so track strings of
+ * spaces, and copy the trailing spaces only if there's a non-space
+ * character following.
+ */
+ if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
+ getyx(stdscr, oldy, oldx);
+#ifdef mvchgat
+ mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL);
+#else
+ for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) {
+ (void)move(lno, col);
+ ch = winch(stdscr);
+ if (isblank(ch))
+ ++spcnt;
+ else {
+ (void)move(lno, col - spcnt);
+ for (; spcnt > 0; --spcnt)
+ (void)addch(' ');
+ (void)addch(ch);
+ }
+ if (++col >= sp->cols)
+ break;
+ }
+#endif
+ (void)move(oldy, oldx);
+ }
+
+ /*
+ * The bottom line is expected to be blank after this operation,
+ * and other screens must support that semantic.
+ */
+ return (deleteln() == ERR);
+}
+
+/*
+ * cl_ex_adjust --
+ * Adjust the screen for ex. This routine is purely for standalone
+ * ex programs. All special purpose, all special case.
+ *
+ * PUBLIC: int cl_ex_adjust __P((SCR *, exadj_t));
+ */
+int
+cl_ex_adjust(sp, action)
+ SCR *sp;
+ exadj_t action;
+{
+ CL_PRIVATE *clp;
+ int cnt;
+
+ clp = CLP(sp);
+ switch (action) {
+ case EX_TERM_SCROLL:
+ /* Move the cursor up one line if that's possible. */
+ if (clp->cuu1 != NULL)
+ (void)tputs(clp->cuu1, 1, cl_putchar);
+ else if (clp->cup != NULL)
+ (void)tputs(tgoto(clp->cup,
+ 0, LINES - 2), 1, cl_putchar);
+ else
+ return (0);
+ /* FALLTHROUGH */
+ case EX_TERM_CE:
+ /* Clear the line. */
+ if (clp->el != NULL) {
+ (void)putchar('\r');
+ (void)tputs(clp->el, 1, cl_putchar);
+ } else {
+ /*
+ * Historically, ex didn't erase the line, so, if the
+ * displayed line was only a single glyph, and <eof>
+ * was more than one glyph, the output would not fully
+ * overwrite the user's input. To fix this, output
+ * the maxiumum character number of spaces. Note,
+ * this won't help if the user entered extra prompt
+ * or <blank> characters before the command character.
+ * We'd have to do a lot of work to make that work, and
+ * it's almost certainly not worth the effort.
+ */
+ for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
+ (void)putchar('\b');
+ for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
+ (void)putchar(' ');
+ (void)putchar('\r');
+ (void)fflush(stdout);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * cl_insertln --
+ * Push down the current line, discarding the bottom line.
+ *
+ * PUBLIC: int cl_insertln __P((SCR *));
+ */
+int
+cl_insertln(sp)
+ SCR *sp;
+{
+ /*
+ * The current line is expected to be blank after this operation,
+ * and the screen must support that semantic.
+ */
+ return (insertln() == ERR);
+}
+
+/*
+ * cl_keyval --
+ * Return the value for a special key.
+ *
+ * PUBLIC: int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+ */
+int
+cl_keyval(sp, val, chp, dnep)
+ SCR *sp;
+ scr_keyval_t val;
+ CHAR_T *chp;
+ int *dnep;
+{
+ CL_PRIVATE *clp;
+
+ /*
+ * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
+ * VWERASE is a 4BSD extension.
+ */
+ clp = CLP(sp);
+ switch (val) {
+ case KEY_VEOF:
+ *dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
+ break;
+ case KEY_VERASE:
+ *dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
+ break;
+ case KEY_VKILL:
+ *dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
+ break;
+#ifdef VWERASE
+ case KEY_VWERASE:
+ *dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
+ break;
+#endif
+ default:
+ *dnep = 1;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cl_move --
+ * Move the cursor.
+ *
+ * PUBLIC: int cl_move __P((SCR *, size_t, size_t));
+ */
+int
+cl_move(sp, lno, cno)
+ SCR *sp;
+ size_t lno, cno;
+{
+ /* See the comment in cl_cursor. */
+ if (move(RLNO(sp, lno), cno) == ERR) {
+ msgq(sp, M_ERR,
+ "Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * cl_refresh --
+ * Refresh the screen.
+ *
+ * PUBLIC: int cl_refresh __P((SCR *, int));
+ */
+int
+cl_refresh(sp, repaint)
+ SCR *sp;
+ int repaint;
+{
+ CL_PRIVATE *clp;
+
+ clp = CLP(sp);
+
+ /*
+ * If we received a killer signal, we're done, there's no point
+ * in refreshing the screen.
+ */
+ if (clp->killersig)
+ return (0);
+
+ /*
+ * If repaint is set, the editor is telling us that we don't know
+ * what's on the screen, so we have to repaint from scratch.
+ *
+ * In the curses library, doing wrefresh(curscr) is okay, but the
+ * screen flashes when we then apply the refresh() to bring it up
+ * to date. So, use clearok().
+ */
+ if (repaint)
+ clearok(curscr, 1);
+ return (refresh() == ERR);
+}
+
+/*
+ * cl_rename --
+ * Rename the file.
+ *
+ * PUBLIC: int cl_rename __P((SCR *, char *, int));
+ */
+int
+cl_rename(sp, name, on)
+ SCR *sp;
+ char *name;
+ int on;
+{
+ GS *gp;
+ CL_PRIVATE *clp;
+ char *ttype;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+
+ ttype = OG_STR(gp, GO_TERM);
+
+ /*
+ * XXX
+ * We can only rename windows for xterm.
+ */
+ if (on) {
+ if (F_ISSET(clp, CL_RENAME_OK) &&
+ !strncmp(ttype, "xterm", sizeof("xterm") - 1)) {
+ F_SET(clp, CL_RENAME);
+ (void)printf(XTERM_RENAME, name);
+ (void)fflush(stdout);
+ }
+ } else
+ if (F_ISSET(clp, CL_RENAME)) {
+ F_CLR(clp, CL_RENAME);
+ (void)printf(XTERM_RENAME, ttype);
+ (void)fflush(stdout);
+ }
+ return (0);
+}
+
+/*
+ * cl_suspend --
+ * Suspend a screen.
+ *
+ * PUBLIC: int cl_suspend __P((SCR *, int *));
+ */
+int
+cl_suspend(sp, allowedp)
+ SCR *sp;
+ int *allowedp;
+{
+ struct termios t;
+ CL_PRIVATE *clp;
+ GS *gp;
+ size_t oldy, oldx;
+ int changed;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+ *allowedp = 1;
+
+ /*
+ * The ex implementation of this function isn't needed by screens not
+ * supporting ex commands that require full terminal canonical mode
+ * (e.g. :suspend).
+ *
+ * The vi implementation of this function isn't needed by screens not
+ * supporting vi process suspension, i.e. any screen that isn't backed
+ * by a UNIX shell.
+ *
+ * Setting allowedp to 0 will cause the editor to reject the command.
+ */
+ if (F_ISSET(sp, SC_EX)) {
+ /* Save the terminal settings, and restore the original ones. */
+ if (F_ISSET(clp, CL_STDIN_TTY)) {
+ (void)tcgetattr(STDIN_FILENO, &t);
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &clp->orig);
+ }
+
+ /* Stop the process group. */
+ (void)kill(0, SIGTSTP);
+
+ /* Time passes ... */
+
+ /* Restore terminal settings. */
+ if (F_ISSET(clp, CL_STDIN_TTY))
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+ return (0);
+ }
+
+ /*
+ * Move to the lower left-hand corner of the screen.
+ *
+ * XXX
+ * Not sure this is necessary in System V implementations, but it
+ * shouldn't hurt.
+ */
+ getyx(stdscr, oldy, oldx);
+ (void)move(LINES - 1, 0);
+ (void)refresh();
+
+ /*
+ * Temporarily end the screen. System V introduced a semantic where
+ * endwin() could be restarted. We use it because restarting curses
+ * from scratch often fails in System V. 4BSD curses didn't support
+ * restarting after endwin(), so we have to do what clean up we can
+ * without calling it.
+ */
+#ifdef HAVE_BSD_CURSES
+ /* Save the terminal settings. */
+ (void)tcgetattr(STDIN_FILENO, &t);
+#endif
+
+ /* Restore the cursor keys to normal mode. */
+ (void)keypad(stdscr, FALSE);
+
+ /* Restore the window name. */
+ (void)cl_rename(sp, NULL, 0);
+
+#ifdef HAVE_BSD_CURSES
+ (void)cl_attr(sp, SA_ALTERNATE, 0);
+#else
+ (void)endwin();
+#endif
+ /*
+ * XXX
+ * Restore the original terminal settings. This is bad -- the
+ * reset can cause character loss from the tty queue. However,
+ * we can't call endwin() in BSD curses implementations, and too
+ * many System V curses implementations don't get it right.
+ */
+ (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
+
+ /* Stop the process group. */
+ (void)kill(0, SIGTSTP);
+
+ /* Time passes ... */
+
+ /*
+ * If we received a killer signal, we're done. Leave everything
+ * unchanged. In addition, the terminal has already been reset
+ * correctly, so leave it alone.
+ */
+ if (clp->killersig) {
+ F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
+ return (0);
+ }
+
+#ifdef HAVE_BSD_CURSES
+ /* Restore terminal settings. */
+ if (F_ISSET(clp, CL_STDIN_TTY))
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+
+ (void)cl_attr(sp, SA_ALTERNATE, 1);
+#endif
+
+ /* Set the window name. */
+ (void)cl_rename(sp, sp->frp->name, 1);
+
+ /* Put the cursor keys into application mode. */
+ (void)keypad(stdscr, TRUE);
+
+ /* Refresh and repaint the screen. */
+ (void)move(oldy, oldx);
+ (void)cl_refresh(sp, 1);
+
+ /* If the screen changed size, set the SIGWINCH bit. */
+ if (cl_ssize(sp, 1, NULL, NULL, &changed))
+ return (1);
+ if (changed)
+ F_SET(CLP(sp), CL_SIGWINCH);
+
+ return (0);
+}
+
+/*
+ * cl_usage --
+ * Print out the curses usage messages.
+ *
+ * PUBLIC: void cl_usage __P((void));
+ */
+void
+cl_usage()
+{
+#define USAGE "\
+usage: ex [-eFRrSsv] [-c command] [-t tag] [-w size] [file ...]\n\
+usage: vi [-eFlRrSv] [-c command] [-t tag] [-w size] [file ...]\n"
+ (void)fprintf(stderr, "%s", USAGE);
+#undef USAGE
+}
+
+#ifdef DEBUG
+/*
+ * gdbrefresh --
+ * Stub routine so can flush out curses screen changes using gdb.
+ */
+int
+gdbrefresh()
+{
+ refresh();
+ return (0); /* XXX Convince gdb to run it. */
+}
+#endif
diff --git a/contrib/nvi/cl/cl_main.c b/contrib/nvi/cl/cl_main.c
new file mode 100644
index 000000000000..2889f709cbb2
--- /dev/null
+++ b/contrib/nvi/cl/cl_main.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_main.c 10.36 (Berkeley) 10/14/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#ifdef RUNNING_IP
+#include "../ip/ip.h"
+#endif
+#include "cl.h"
+#include "pathnames.h"
+
+GS *__global_list; /* GLOBAL: List of screens. */
+sigset_t __sigblockset; /* GLOBAL: Blocked signals. */
+
+static void cl_func_std __P((GS *));
+static CL_PRIVATE *cl_init __P((GS *));
+static GS *gs_init __P((char *));
+static void perr __P((char *, char *));
+static int setsig __P((int, struct sigaction *, void (*)(int)));
+static void sig_end __P((GS *));
+static void term_init __P((char *, char *));
+
+/*
+ * main --
+ * This is the main loop for the standalone curses editor.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static int reenter;
+ CL_PRIVATE *clp;
+ GS *gp;
+ size_t rows, cols;
+ int rval;
+ char *ip_arg, **p_av, **t_av, *ttype;
+
+ /* If loaded at 0 and jumping through a NULL pointer, stop. */
+ if (reenter++)
+ abort();
+
+ /* Create and initialize the global structure. */
+ __global_list = gp = gs_init(argv[0]);
+
+ /*
+ * Strip out any arguments that vi isn't going to understand. There's
+ * no way to portably call getopt twice, so arguments parsed here must
+ * be removed from the argument list.
+ */
+#ifdef RUNNING_IP
+ ip_arg = NULL;
+ for (p_av = t_av = argv;;) {
+ if (*t_av == NULL) {
+ *p_av = NULL;
+ break;
+ }
+ if (!strcmp(*t_av, "--")) {
+ while ((*p_av++ = *t_av++) != NULL);
+ break;
+ }
+ if (!memcmp(*t_av, "-I", sizeof("-I") - 1)) {
+ if (t_av[0][2] != '\0') {
+ ip_arg = t_av[0] + 2;
+ ++t_av;
+ --argc;
+ continue;
+ }
+ if (t_av[1] != NULL) {
+ ip_arg = t_av[1];
+ t_av += 2;
+ argc -= 2;
+ continue;
+ }
+ }
+ *p_av++ = *t_av++;
+ }
+
+ /*
+ * If we're being called as an editor library, we're done here, we
+ * get loaded with the curses screen, we don't share much code.
+ */
+ if (ip_arg != NULL)
+ exit (ip_main(argc, argv, gp, ip_arg));
+#else
+ ip_arg = argv[0];
+#endif
+
+ /* Create and initialize the CL_PRIVATE structure. */
+ clp = cl_init(gp);
+
+ /*
+ * Initialize the terminal information.
+ *
+ * We have to know what terminal it is from the start, since we may
+ * have to use termcap/terminfo to find out how big the screen is.
+ */
+ if ((ttype = getenv("TERM")) == NULL)
+ ttype = "unknown";
+ term_init(gp->progname, ttype);
+
+ /* Add the terminal type to the global structure. */
+ if ((OG_D_STR(gp, GO_TERM) =
+ OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
+ perr(gp->progname, NULL);
+
+ /* Figure out how big the screen is. */
+ if (cl_ssize(NULL, 0, &rows, &cols, NULL))
+ exit (1);
+
+ /* Add the rows and columns to the global structure. */
+ OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
+ OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
+
+ /* Ex wants stdout to be buffered. */
+ (void)setvbuf(stdout, NULL, _IOFBF, 0);
+
+ /* Start catching signals. */
+ if (sig_init(gp, NULL))
+ exit (1);
+
+ /* Run ex/vi. */
+ rval = editor(gp, argc, argv);
+
+ /* Clean up signals. */
+ sig_end(gp);
+
+ /* Clean up the terminal. */
+ (void)cl_quit(gp);
+
+ /*
+ * XXX
+ * Reset the O_MESG option.
+ */
+ if (clp->tgw != TGW_UNKNOWN)
+ (void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
+
+ /*
+ * XXX
+ * Reset the X11 xterm icon/window name.
+ */
+ if (F_ISSET(clp, CL_RENAME)) {
+ (void)printf(XTERM_RENAME, ttype);
+ (void)fflush(stdout);
+ }
+
+ /* If a killer signal arrived, pretend we just got it. */
+ if (clp->killersig) {
+ (void)signal(clp->killersig, SIG_DFL);
+ (void)kill(getpid(), clp->killersig);
+ /* NOTREACHED */
+ }
+
+ /* Free the global and CL private areas. */
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ free(clp);
+ free(gp);
+#endif
+
+ exit (rval);
+}
+
+/*
+ * gs_init --
+ * Create and partially initialize the GS structure.
+ */
+static GS *
+gs_init(name)
+ char *name;
+{
+ CL_PRIVATE *clp;
+ GS *gp;
+ char *p;
+
+ /* Figure out what our name is. */
+ if ((p = strrchr(name, '/')) != NULL)
+ name = p + 1;
+
+ /* Allocate the global structure. */
+ CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
+ if (gp == NULL)
+ perr(name, NULL);
+
+
+ gp->progname = name;
+ return (gp);
+}
+
+/*
+ * cl_init --
+ * Create and partially initialize the CL structure.
+ */
+static CL_PRIVATE *
+cl_init(gp)
+ GS *gp;
+{
+ CL_PRIVATE *clp;
+ int fd;
+
+ /* Allocate the CL private structure. */
+ CALLOC_NOMSG(NULL, clp, CL_PRIVATE *, 1, sizeof(CL_PRIVATE));
+ if (clp == NULL)
+ perr(gp->progname, NULL);
+ gp->cl_private = clp;
+
+ /*
+ * Set the CL_STDIN_TTY flag. It's purpose is to avoid setting
+ * and resetting the tty if the input isn't from there. We also
+ * use the same test to determine if we're running a script or
+ * not.
+ */
+ if (isatty(STDIN_FILENO))
+ F_SET(clp, CL_STDIN_TTY);
+ else
+ F_SET(gp, G_SCRIPTED);
+
+ /*
+ * We expect that if we've lost our controlling terminal that the
+ * open() (but not the tcgetattr()) will fail.
+ */
+ if (F_ISSET(clp, CL_STDIN_TTY)) {
+ if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
+ goto tcfail;
+ } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
+ if (tcgetattr(fd, &clp->orig) == -1) {
+tcfail: perr(gp->progname, "tcgetattr");
+ exit (1);
+ }
+ (void)close(fd);
+ }
+
+ /* Initialize the list of curses functions. */
+ cl_func_std(gp);
+
+ return (clp);
+}
+
+/*
+ * term_init --
+ * Initialize terminal information.
+ */
+static void
+term_init(name, ttype)
+ char *name, *ttype;
+{
+ int err;
+
+ /* Set up the terminal database information. */
+ setupterm(ttype, STDOUT_FILENO, &err);
+ switch (err) {
+ case -1:
+ (void)fprintf(stderr,
+ "%s: No terminal database found\n", name);
+ exit (1);
+ case 0:
+ (void)fprintf(stderr,
+ "%s: %s: unknown terminal type\n", name, ttype);
+ exit (1);
+ }
+}
+
+#define GLOBAL_CLP \
+ CL_PRIVATE *clp = GCLP(__global_list);
+static void
+h_hup(signo)
+ int signo;
+{
+ GLOBAL_CLP;
+
+ F_SET(clp, CL_SIGHUP);
+ clp->killersig = SIGHUP;
+}
+
+static void
+h_int(signo)
+ int signo;
+{
+ GLOBAL_CLP;
+
+ F_SET(clp, CL_SIGINT);
+}
+
+static void
+h_term(signo)
+ int signo;
+{
+ GLOBAL_CLP;
+
+ F_SET(clp, CL_SIGTERM);
+ clp->killersig = SIGTERM;
+}
+
+static void
+h_winch(signo)
+ int signo;
+{
+ GLOBAL_CLP;
+
+ F_SET(clp, CL_SIGWINCH);
+}
+#undef GLOBAL_CLP
+
+/*
+ * sig_init --
+ * Initialize signals.
+ *
+ * PUBLIC: int sig_init __P((GS *, SCR *));
+ */
+int
+sig_init(gp, sp)
+ GS *gp;
+ SCR *sp;
+{
+ CL_PRIVATE *clp;
+
+ clp = GCLP(gp);
+
+ if (sp == NULL) {
+ (void)sigemptyset(&__sigblockset);
+ if (sigaddset(&__sigblockset, SIGHUP) ||
+ setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
+ sigaddset(&__sigblockset, SIGINT) ||
+ setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
+ sigaddset(&__sigblockset, SIGTERM) ||
+ setsig(SIGTERM, &clp->oact[INDX_TERM], h_term)
+#ifdef SIGWINCH
+ ||
+ sigaddset(&__sigblockset, SIGWINCH) ||
+ setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
+#endif
+ ) {
+ perr(gp->progname, NULL);
+ return (1);
+ }
+ } else
+ if (setsig(SIGHUP, NULL, h_hup) ||
+ setsig(SIGINT, NULL, h_int) ||
+ setsig(SIGTERM, NULL, h_term)
+#ifdef SIGWINCH
+ ||
+ setsig(SIGWINCH, NULL, h_winch)
+#endif
+ ) {
+ msgq(sp, M_SYSERR, "signal-reset");
+ }
+ return (0);
+}
+
+/*
+ * setsig --
+ * Set a signal handler.
+ */
+static int
+setsig(signo, oactp, handler)
+ int signo;
+ struct sigaction *oactp;
+ void (*handler) __P((int));
+{
+ struct sigaction act;
+
+ /*
+ * Use sigaction(2), not signal(3), since we don't always want to
+ * restart system calls. The example is when waiting for a command
+ * mode keystroke and SIGWINCH arrives. Besides, you can't portably
+ * restart system calls (thanks, POSIX!). On the other hand, you
+ * can't portably NOT restart system calls (thanks, Sun!). SunOS
+ * used SA_INTERRUPT as their extension to NOT restart read calls.
+ * We sure hope nobody else used it for anything else. Mom told me
+ * there'd be days like this. She just never told me that there'd
+ * be so many.
+ */
+ act.sa_handler = handler;
+ sigemptyset(&act.sa_mask);
+
+#ifdef SA_INTERRUPT
+ act.sa_flags = SA_INTERRUPT;
+#else
+ act.sa_flags = 0;
+#endif
+ return (sigaction(signo, &act, oactp));
+}
+
+/*
+ * sig_end --
+ * End signal setup.
+ */
+static void
+sig_end(gp)
+ GS *gp;
+{
+ CL_PRIVATE *clp;
+
+ clp = GCLP(gp);
+ (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
+ (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
+ (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
+#ifdef SIGWINCH
+ (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
+#endif
+}
+
+/*
+ * cl_func_std --
+ * Initialize the standard curses functions.
+ */
+static void
+cl_func_std(gp)
+ GS *gp;
+{
+ gp->scr_addstr = cl_addstr;
+ gp->scr_attr = cl_attr;
+ gp->scr_baud = cl_baud;
+ gp->scr_bell = cl_bell;
+ gp->scr_busy = NULL;
+ gp->scr_clrtoeol = cl_clrtoeol;
+ gp->scr_cursor = cl_cursor;
+ gp->scr_deleteln = cl_deleteln;
+ gp->scr_event = cl_event;
+ gp->scr_ex_adjust = cl_ex_adjust;
+ gp->scr_fmap = cl_fmap;
+ gp->scr_insertln = cl_insertln;
+ gp->scr_keyval = cl_keyval;
+ gp->scr_move = cl_move;
+ gp->scr_msg = NULL;
+ gp->scr_optchange = cl_optchange;
+ gp->scr_refresh = cl_refresh;
+ gp->scr_rename = cl_rename;
+ gp->scr_screen = cl_screen;
+ gp->scr_suspend = cl_suspend;
+ gp->scr_usage = cl_usage;
+}
+
+/*
+ * perr --
+ * Print system error.
+ */
+static void
+perr(name, msg)
+ char *name, *msg;
+{
+ (void)fprintf(stderr, "%s:", name);
+ if (msg != NULL)
+ (void)fprintf(stderr, "%s:", msg);
+ (void)fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+}
diff --git a/contrib/nvi/cl/cl_read.c b/contrib/nvi/cl/cl_read.c
new file mode 100644
index 000000000000..8a95a77b2438
--- /dev/null
+++ b/contrib/nvi/cl/cl_read.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_read.c 10.15 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../ex/script.h"
+#include "cl.h"
+
+static input_t cl_read __P((SCR *,
+ u_int32_t, CHAR_T *, size_t, int *, struct timeval *));
+static int cl_resize __P((SCR *, size_t, size_t));
+
+/*
+ * cl_event --
+ * Return a single event.
+ *
+ * PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int));
+ */
+int
+cl_event(sp, evp, flags, ms)
+ SCR *sp;
+ EVENT *evp;
+ u_int32_t flags;
+ int ms;
+{
+ struct timeval t, *tp;
+ CL_PRIVATE *clp;
+ size_t lines, columns;
+ int changed, nr;
+
+ /*
+ * Queue signal based events. We never clear SIGHUP or SIGTERM events,
+ * so that we just keep returning them until the editor dies.
+ */
+ clp = CLP(sp);
+retest: if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
+ if (F_ISSET(clp, CL_SIGINT)) {
+ F_CLR(clp, CL_SIGINT);
+ evp->e_event = E_INTERRUPT;
+ } else
+ evp->e_event = E_TIMEOUT;
+ return (0);
+ }
+ if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
+ if (F_ISSET(clp, CL_SIGHUP)) {
+ evp->e_event = E_SIGHUP;
+ return (0);
+ }
+ if (F_ISSET(clp, CL_SIGTERM)) {
+ evp->e_event = E_SIGTERM;
+ return (0);
+ }
+ if (F_ISSET(clp, CL_SIGWINCH)) {
+ F_CLR(clp, CL_SIGWINCH);
+ if (cl_ssize(sp, 1, &lines, &columns, &changed))
+ return (1);
+ if (changed) {
+ (void)cl_resize(sp, lines, columns);
+ evp->e_event = E_WRESIZE;
+ return (0);
+ }
+ /* No real change, ignore the signal. */
+ }
+ }
+
+ /* Set timer. */
+ if (ms == 0)
+ tp = NULL;
+ else {
+ t.tv_sec = ms / 1000;
+ t.tv_usec = (ms % 1000) * 1000;
+ tp = &t;
+ }
+
+ /* Read input characters. */
+ switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
+ clp->ibuf, sizeof(clp->ibuf), &nr, tp)) {
+ case INP_OK:
+ evp->e_csp = clp->ibuf;
+ evp->e_len = nr;
+ evp->e_event = E_STRING;
+ break;
+ case INP_EOF:
+ evp->e_event = E_EOF;
+ break;
+ case INP_ERR:
+ evp->e_event = E_ERR;
+ break;
+ case INP_INTR:
+ goto retest;
+ case INP_TIMEOUT:
+ evp->e_event = E_TIMEOUT;
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * cl_read --
+ * Read characters from the input.
+ */
+static input_t
+cl_read(sp, flags, bp, blen, nrp, tp)
+ SCR *sp;
+ u_int32_t flags;
+ CHAR_T *bp;
+ size_t blen;
+ int *nrp;
+ struct timeval *tp;
+{
+ struct termios term1, term2;
+ struct timeval poll;
+ CL_PRIVATE *clp;
+ GS *gp;
+ SCR *tsp;
+ fd_set rdfd;
+ input_t rval;
+ int maxfd, nr, term_reset;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+ term_reset = 0;
+
+ /*
+ * 1: A read from a file or a pipe. In this case, the reads
+ * never timeout regardless. This means that we can hang
+ * when trying to complete a map, but we're going to hang
+ * on the next read anyway.
+ */
+ if (!F_ISSET(clp, CL_STDIN_TTY)) {
+ switch (nr = read(STDIN_FILENO, bp, blen)) {
+ case 0:
+ return (INP_EOF);
+ case -1:
+ goto err;
+ default:
+ *nrp = nr;
+ return (INP_OK);
+ }
+ /* NOTREACHED */
+ }
+
+ /*
+ * 2: A read with an associated timeout, e.g., trying to complete
+ * a map sequence. If input exists, we fall into #3.
+ */
+ FD_ZERO(&rdfd);
+ poll.tv_sec = 0;
+ poll.tv_usec = 0;
+ if (tp != NULL) {
+ FD_SET(STDIN_FILENO, &rdfd);
+ switch (select(STDIN_FILENO + 1,
+ &rdfd, NULL, NULL, tp == NULL ? &poll : tp)) {
+ case 0:
+ return (INP_TIMEOUT);
+ case -1:
+ goto err;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * The user can enter a key in the editor to quote a character. If we
+ * get here and the next key is supposed to be quoted, do what we can.
+ * Reset the tty so that the user can enter a ^C, ^Q, ^S. There's an
+ * obvious race here, when the key has already been entered, but there's
+ * nothing that we can do to fix that problem.
+ *
+ * The editor can ask for the next literal character even thought it's
+ * generally running in line-at-a-time mode. Do what we can.
+ */
+ if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
+ term_reset = 1;
+ if (LF_ISSET(EC_QUOTED)) {
+ term2 = term1;
+ term2.c_lflag &= ~ISIG;
+ term2.c_iflag &= ~(IXON | IXOFF);
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &term2);
+ } else
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &clp->vi_enter);
+ }
+
+ /*
+ * 3: Wait for input.
+ *
+ * Select on the command input and scripting window file descriptors.
+ * It's ugly that we wait on scripting file descriptors here, but it's
+ * the only way to keep from locking out scripting windows.
+ */
+ if (F_ISSET(gp, G_SCRWIN)) {
+loop: FD_ZERO(&rdfd);
+ FD_SET(STDIN_FILENO, &rdfd);
+ maxfd = STDIN_FILENO;
+ for (tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (F_ISSET(sp, SC_SCRIPT)) {
+ FD_SET(sp->script->sh_master, &rdfd);
+ if (sp->script->sh_master > maxfd)
+ maxfd = sp->script->sh_master;
+ }
+ switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
+ case 0:
+ abort();
+ case -1:
+ goto err;
+ default:
+ break;
+ }
+ if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
+ if (sscr_input(sp))
+ return (INP_ERR);
+ goto loop;
+ }
+ }
+
+ /*
+ * 4: Read the input.
+ *
+ * !!!
+ * What's going on here is some scary stuff. Ex runs the terminal in
+ * canonical mode. So, the <newline> character terminating a line of
+ * input is returned in the buffer, but a trailing <EOF> character is
+ * not similarly included. As ex uses 0<EOF> and ^<EOF> as autoindent
+ * commands, it has to see the trailing <EOF> characters to determine
+ * the difference between the user entering "0ab" and "0<EOF>ab". We
+ * leave an extra slot in the buffer, so that we can add a trailing
+ * <EOF> character if the buffer isn't terminated by a <newline>. We
+ * lose if the buffer is too small for the line and exactly N characters
+ * are entered followed by an <EOF> character.
+ */
+#define ONE_FOR_EOF 1
+ switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
+ case 0: /* EOF. */
+ /*
+ * ^D in canonical mode returns a read of 0, i.e. EOF. EOF is
+ * a valid command, but we don't want to loop forever because
+ * the terminal driver is returning EOF because the user has
+ * disconnected. The editor will almost certainly try to write
+ * something before this fires, which should kill us, but You
+ * Never Know.
+ */
+ if (++clp->eof_count < 50) {
+ bp[0] = clp->orig.c_cc[VEOF];
+ *nrp = 1;
+ rval = INP_OK;
+
+ } else
+ rval = INP_EOF;
+ break;
+ case -1: /* Error or interrupt. */
+err: if (errno == EINTR)
+ rval = INP_INTR;
+ else {
+ rval = INP_ERR;
+ msgq(sp, M_SYSERR, "input");
+ }
+ break;
+ default: /* Input characters. */
+ if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
+ bp[nr++] = clp->orig.c_cc[VEOF];
+ *nrp = nr;
+ clp->eof_count = 0;
+ rval = INP_OK;
+ break;
+ }
+
+ /* Restore the terminal state if it was modified. */
+ if (term_reset)
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
+ return (rval);
+}
+
+/*
+ * cl_resize --
+ * Reset the options for a resize event.
+ */
+static int
+cl_resize(sp, lines, columns)
+ SCR *sp;
+ size_t lines, columns;
+{
+ ARGS *argv[2], a, b;
+ char b1[1024];
+
+ a.bp = b1;
+ b.bp = NULL;
+ a.len = b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+ (void)snprintf(b1, sizeof(b1), "lines=%lu", (u_long)lines);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ (void)snprintf(b1, sizeof(b1), "columns=%lu", (u_long)columns);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ return (0);
+}
diff --git a/contrib/nvi/cl/cl_screen.c b/contrib/nvi/cl/cl_screen.c
new file mode 100644
index 000000000000..2ce58e80cc78
--- /dev/null
+++ b/contrib/nvi/cl/cl_screen.c
@@ -0,0 +1,581 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_screen.c 10.49 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <curses.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "cl.h"
+
+static int cl_ex_end __P((GS *));
+static int cl_ex_init __P((SCR *));
+static void cl_freecap __P((CL_PRIVATE *));
+static int cl_vi_end __P((GS *));
+static int cl_vi_init __P((SCR *));
+static int cl_putenv __P((char *, char *, u_long));
+
+/*
+ * cl_screen --
+ * Switch screen types.
+ *
+ * PUBLIC: int cl_screen __P((SCR *, u_int32_t));
+ */
+int
+cl_screen(sp, flags)
+ SCR *sp;
+ u_int32_t flags;
+{
+ CL_PRIVATE *clp;
+ GS *gp;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+
+ /* See if the current information is incorrect. */
+ if (F_ISSET(gp, G_SRESTART)) {
+ if (cl_quit(gp))
+ return (1);
+ F_CLR(gp, G_SRESTART);
+ }
+
+ /* See if we're already in the right mode. */
+ if (LF_ISSET(SC_EX) && F_ISSET(sp, SC_SCR_EX) ||
+ LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI))
+ return (0);
+
+ /*
+ * Fake leaving ex mode.
+ *
+ * We don't actually exit ex or vi mode unless forced (e.g. by a window
+ * size change). This is because many curses implementations can't be
+ * called twice in a single program. Plus, it's faster. If the editor
+ * "leaves" vi to enter ex, when it exits ex we'll just fall back into
+ * vi.
+ */
+ if (F_ISSET(sp, SC_SCR_EX))
+ F_CLR(sp, SC_SCR_EX);
+
+ /*
+ * Fake leaving vi mode.
+ *
+ * Clear out the rest of the screen if we're in the middle of a split
+ * screen. Move to the last line in the current screen -- this makes
+ * terminal scrolling happen naturally. Note: *don't* move past the
+ * end of the screen, as there are ex commands (e.g., :read ! cat file)
+ * that don't want to. Don't clear the info line, its contents may be
+ * valid, e.g. :file|append.
+ */
+ if (F_ISSET(sp, SC_SCR_VI)) {
+ F_CLR(sp, SC_SCR_VI);
+
+ if (sp->q.cqe_next != (void *)&gp->dq) {
+ (void)move(RLNO(sp, sp->rows), 0);
+ clrtobot();
+ }
+ (void)move(RLNO(sp, sp->rows) - 1, 0);
+ refresh();
+ }
+
+ /* Enter the requested mode. */
+ if (LF_ISSET(SC_EX)) {
+ if (cl_ex_init(sp))
+ return (1);
+ F_SET(clp, CL_IN_EX | CL_SCR_EX_INIT);
+
+ /*
+ * If doing an ex screen for ex mode, move to the last line
+ * on the screen.
+ */
+ if (F_ISSET(sp, SC_EX) && clp->cup != NULL)
+ tputs(tgoto(clp->cup,
+ 0, O_VAL(sp, O_LINES) - 1), 1, cl_putchar);
+ } else {
+ if (cl_vi_init(sp))
+ return (1);
+ F_CLR(clp, CL_IN_EX);
+ F_SET(clp, CL_SCR_VI_INIT);
+ }
+ return (0);
+}
+
+/*
+ * cl_quit --
+ * Shutdown the screens.
+ *
+ * PUBLIC: int cl_quit __P((GS *));
+ */
+int
+cl_quit(gp)
+ GS *gp;
+{
+ CL_PRIVATE *clp;
+ int rval;
+
+ rval = 0;
+ clp = GCLP(gp);
+
+ /*
+ * If we weren't really running, ignore it. This happens if the
+ * screen changes size before we've called curses.
+ */
+ if (!F_ISSET(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT))
+ return (0);
+
+ /* Clean up the terminal mappings. */
+ if (cl_term_end(gp))
+ rval = 1;
+
+ /* Really leave vi mode. */
+ if (F_ISSET(clp, CL_STDIN_TTY) &&
+ F_ISSET(clp, CL_SCR_VI_INIT) && cl_vi_end(gp))
+ rval = 1;
+
+ /* Really leave ex mode. */
+ if (F_ISSET(clp, CL_STDIN_TTY) &&
+ F_ISSET(clp, CL_SCR_EX_INIT) && cl_ex_end(gp))
+ rval = 1;
+
+ /*
+ * If we were running ex when we quit, or we're using an implementation
+ * of curses where endwin() doesn't get this right, restore the original
+ * terminal modes.
+ *
+ * XXX
+ * We always do this because it's too hard to figure out what curses
+ * implementations get it wrong. It may discard type-ahead characters
+ * from the tty queue.
+ */
+ (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
+
+ F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
+ return (rval);
+}
+
+/*
+ * cl_vi_init --
+ * Initialize the curses vi screen.
+ */
+static int
+cl_vi_init(sp)
+ SCR *sp;
+{
+ CL_PRIVATE *clp;
+ GS *gp;
+ char *o_cols, *o_lines, *o_term, *ttype;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+
+ /* If already initialized, just set the terminal modes. */
+ if (F_ISSET(clp, CL_SCR_VI_INIT))
+ goto fast;
+
+ /* Curses vi always reads from (and writes to) a terminal. */
+ if (!F_ISSET(clp, CL_STDIN_TTY) || !isatty(STDOUT_FILENO)) {
+ msgq(sp, M_ERR,
+ "016|Vi's standard input and output must be a terminal");
+ return (1);
+ }
+
+ /* We'll need a terminal type. */
+ if (opts_empty(sp, O_TERM, 0))
+ return (1);
+ ttype = O_STR(sp, O_TERM);
+
+ /*
+ * XXX
+ * Changing the row/column and terminal values is done by putting them
+ * into the environment, which is then read by curses. What this loses
+ * in ugliness, it makes up for in stupidity. We can't simply put the
+ * values into the environment ourselves, because in the presence of a
+ * kernel mechanism for returning the window size, entering values into
+ * the environment will screw up future screen resizing events, e.g. if
+ * the user enters a :shell command and then resizes their window. So,
+ * if they weren't already in the environment, we make sure to delete
+ * them immediately after setting them.
+ *
+ * XXX
+ * Putting the TERM variable into the environment is necessary, even
+ * though we're using newterm() here. We may be using initscr() as
+ * the underlying function.
+ */
+ o_term = getenv("TERM");
+ cl_putenv("TERM", ttype, 0);
+ o_lines = getenv("LINES");
+ cl_putenv("LINES", NULL, (u_long)O_VAL(sp, O_LINES));
+ o_cols = getenv("COLUMNS");
+ cl_putenv("COLUMNS", NULL, (u_long)O_VAL(sp, O_COLUMNS));
+
+ /*
+ * We don't care about the SCREEN reference returned by newterm, we
+ * never have more than one SCREEN at a time.
+ *
+ * XXX
+ * The SunOS initscr() can't be called twice. Don't even think about
+ * using it. It fails in subtle ways (e.g. select(2) on fileno(stdin)
+ * stops working). (The SVID notes that applications should only call
+ * initscr() once.)
+ *
+ * XXX
+ * The HP/UX newterm doesn't support the NULL first argument, so we
+ * have to specify the terminal type.
+ */
+ errno = 0;
+ if (newterm(ttype, stdout, stdin) == NULL) {
+ if (errno)
+ msgq(sp, M_SYSERR, "%s", ttype);
+ else
+ msgq(sp, M_ERR, "%s: unknown terminal type", ttype);
+ return (1);
+ }
+
+ if (o_term == NULL)
+ unsetenv("TERM");
+ if (o_lines == NULL)
+ unsetenv("LINES");
+ if (o_cols == NULL)
+ unsetenv("COLUMNS");
+
+ /*
+ * XXX
+ * Someone got let out alone without adult supervision -- the SunOS
+ * newterm resets the signal handlers. There's a race, but it's not
+ * worth closing.
+ */
+ (void)sig_init(sp->gp, sp);
+
+ /*
+ * We use raw mode. What we want is 8-bit clean, however, signals
+ * and flow control should continue to work. Admittedly, it sounds
+ * like cbreak, but it isn't. Using cbreak() can get you additional
+ * things like IEXTEN, which turns on flags like DISCARD and LNEXT.
+ *
+ * !!!
+ * If raw isn't turning off echo and newlines, something's wrong.
+ * However, it shouldn't hurt.
+ */
+ noecho(); /* No character echo. */
+ nonl(); /* No CR/NL translation. */
+ raw(); /* 8-bit clean. */
+ idlok(stdscr, 1); /* Use hardware insert/delete line. */
+
+ /* Put the cursor keys into application mode. */
+ (void)keypad(stdscr, TRUE);
+
+ /*
+ * XXX
+ * The screen TI sequence just got sent. See the comment in
+ * cl_funcs.c:cl_attr().
+ */
+ clp->ti_te = TI_SENT;
+
+ /*
+ * XXX
+ * Historic implementations of curses handled SIGTSTP signals
+ * in one of three ways. They either:
+ *
+ * 1: Set their own handler, regardless.
+ * 2: Did not set a handler if a handler was already installed.
+ * 3: Set their own handler, but then called any previously set
+ * handler after completing their own cleanup.
+ *
+ * We don't try and figure out which behavior is in place, we force
+ * it to SIG_DFL after initializing the curses interface, which means
+ * that curses isn't going to take the signal. Since curses isn't
+ * reentrant (i.e., the whole curses SIGTSTP interface is a fantasy),
+ * we're doing The Right Thing.
+ */
+ (void)signal(SIGTSTP, SIG_DFL);
+
+ /*
+ * If flow control was on, turn it back on. Turn signals on. ISIG
+ * turns on VINTR, VQUIT, VDSUSP and VSUSP. The main curses code
+ * already installed a handler for VINTR. We're going to disable the
+ * other three.
+ *
+ * XXX
+ * We want to use ^Y as a vi scrolling command. If the user has the
+ * DSUSP character set to ^Y (common practice) clean it up. As it's
+ * equally possible that the user has VDSUSP set to 'a', we disable
+ * it regardless. It doesn't make much sense to suspend vi at read,
+ * so I don't think anyone will care. Alternatively, we could look
+ * it up in the table of legal command characters and turn it off if
+ * it matches one. VDSUSP wasn't in POSIX 1003.1-1990, so we test for
+ * it.
+ *
+ * XXX
+ * We don't check to see if the user had signals enabled originally.
+ * If they didn't, it's unclear what we're supposed to do here, but
+ * it's also pretty unlikely.
+ */
+ if (tcgetattr(STDIN_FILENO, &clp->vi_enter)) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+ if (clp->orig.c_iflag & IXON)
+ clp->vi_enter.c_iflag |= IXON;
+ if (clp->orig.c_iflag & IXOFF)
+ clp->vi_enter.c_iflag |= IXOFF;
+
+ clp->vi_enter.c_lflag |= ISIG;
+#ifdef VDSUSP
+ clp->vi_enter.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+ clp->vi_enter.c_cc[VQUIT] = _POSIX_VDISABLE;
+ clp->vi_enter.c_cc[VSUSP] = _POSIX_VDISABLE;
+
+ /*
+ * XXX
+ * OSF/1 doesn't turn off the <discard>, <literal-next> or <status>
+ * characters when curses switches into raw mode. It should be OK
+ * to do it explicitly for everyone.
+ */
+#ifdef VDISCARD
+ clp->vi_enter.c_cc[VDISCARD] = _POSIX_VDISABLE;
+#endif
+#ifdef VLNEXT
+ clp->vi_enter.c_cc[VLNEXT] = _POSIX_VDISABLE;
+#endif
+#ifdef VSTATUS
+ clp->vi_enter.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+
+ /* Initialize terminal based information. */
+ if (cl_term_init(sp))
+ goto err;
+
+fast: /* Set the terminal modes. */
+ if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->vi_enter)) {
+ msgq(sp, M_SYSERR, "tcsetattr");
+err: (void)cl_vi_end(sp->gp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * cl_vi_end --
+ * Shutdown the vi screen.
+ */
+static int
+cl_vi_end(gp)
+ GS *gp;
+{
+ CL_PRIVATE *clp;
+
+ clp = GCLP(gp);
+
+ /* Restore the cursor keys to normal mode. */
+ (void)keypad(stdscr, FALSE);
+
+ /*
+ * If we were running vi when we quit, scroll the screen up a single
+ * line so we don't lose any information.
+ *
+ * Move to the bottom of the window (some endwin implementations don't
+ * do this for you).
+ */
+ if (!F_ISSET(clp, CL_IN_EX)) {
+ (void)move(0, 0);
+ (void)deleteln();
+ (void)move(LINES - 1, 0);
+ (void)refresh();
+ }
+
+ cl_freecap(clp);
+
+ /* End curses window. */
+ (void)endwin();
+
+ /*
+ * XXX
+ * The screen TE sequence just got sent. See the comment in
+ * cl_funcs.c:cl_attr().
+ */
+ clp->ti_te = TE_SENT;
+
+ return (0);
+}
+
+/*
+ * cl_ex_init --
+ * Initialize the ex screen.
+ */
+static int
+cl_ex_init(sp)
+ SCR *sp;
+{
+ CL_PRIVATE *clp;
+
+ clp = CLP(sp);
+
+ /* If already initialized, just set the terminal modes. */
+ if (F_ISSET(clp, CL_SCR_EX_INIT))
+ goto fast;
+
+ /* If not reading from a file, we're done. */
+ if (!F_ISSET(clp, CL_STDIN_TTY))
+ return (0);
+
+ /* Get the ex termcap/terminfo strings. */
+ (void)cl_getcap(sp, "cup", &clp->cup);
+ (void)cl_getcap(sp, "smso", &clp->smso);
+ (void)cl_getcap(sp, "rmso", &clp->rmso);
+ (void)cl_getcap(sp, "el", &clp->el);
+ (void)cl_getcap(sp, "cuu1", &clp->cuu1);
+
+ /* Enter_standout_mode and exit_standout_mode are paired. */
+ if (clp->smso == NULL || clp->rmso == NULL) {
+ if (clp->smso != NULL) {
+ free(clp->smso);
+ clp->smso = NULL;
+ }
+ if (clp->rmso != NULL) {
+ free(clp->rmso);
+ clp->rmso = NULL;
+ }
+ }
+
+ /*
+ * Turn on canonical mode, with normal input and output processing.
+ * Start with the original terminal settings as the user probably
+ * had them (including any local extensions) set correctly for the
+ * current terminal.
+ *
+ * !!!
+ * We can't get everything that we need portably; for example, ONLCR,
+ * mapping <newline> to <carriage-return> on output isn't required
+ * by POSIX 1003.1b-1993. If this turns out to be a problem, then
+ * we'll either have to play some games on the mapping, or we'll have
+ * to make all ex printf's output \r\n instead of \n.
+ */
+ clp->ex_enter = clp->orig;
+ clp->ex_enter.c_lflag |= ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG;
+#ifdef ECHOCTL
+ clp->ex_enter.c_lflag |= ECHOCTL;
+#endif
+#ifdef ECHOKE
+ clp->ex_enter.c_lflag |= ECHOKE;
+#endif
+ clp->ex_enter.c_iflag |= ICRNL;
+ clp->ex_enter.c_oflag |= OPOST;
+#ifdef ONLCR
+ clp->ex_enter.c_oflag |= ONLCR;
+#endif
+
+fast: if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->ex_enter)) {
+ msgq(sp, M_SYSERR, "tcsetattr");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * cl_ex_end --
+ * Shutdown the ex screen.
+ */
+static int
+cl_ex_end(gp)
+ GS *gp;
+{
+ CL_PRIVATE *clp;
+
+ clp = GCLP(gp);
+
+ cl_freecap(clp);
+
+ return (0);
+}
+
+/*
+ * cl_getcap --
+ * Retrieve termcap/terminfo strings.
+ *
+ * PUBLIC: int cl_getcap __P((SCR *, char *, char **));
+ */
+int
+cl_getcap(sp, name, elementp)
+ SCR *sp;
+ char *name, **elementp;
+{
+ size_t len;
+ char *t;
+
+ if ((t = tigetstr(name)) != NULL &&
+ t != (char *)-1 && (len = strlen(t)) != 0) {
+ MALLOC_RET(sp, *elementp, char *, len + 1);
+ memmove(*elementp, t, len + 1);
+ }
+ return (0);
+}
+
+/*
+ * cl_freecap --
+ * Free any allocated termcap/terminfo strings.
+ */
+static void
+cl_freecap(clp)
+ CL_PRIVATE *clp;
+{
+ if (clp->el != NULL) {
+ free(clp->el);
+ clp->el = NULL;
+ }
+ if (clp->cup != NULL) {
+ free(clp->cup);
+ clp->cup = NULL;
+ }
+ if (clp->cuu1 != NULL) {
+ free(clp->cuu1);
+ clp->cuu1 = NULL;
+ }
+ if (clp->rmso != NULL) {
+ free(clp->rmso);
+ clp->rmso = NULL;
+ }
+ if (clp->smso != NULL) {
+ free(clp->smso);
+ clp->smso = NULL;
+ }
+}
+
+/*
+ * cl_putenv --
+ * Put a value into the environment.
+ */
+static int
+cl_putenv(name, str, value)
+ char *name, *str;
+ u_long value;
+
+{
+ char buf[40];
+
+ if (str == NULL) {
+ (void)snprintf(buf, sizeof(buf), "%lu", value);
+ return (setenv(name, buf, 1));
+ } else
+ return (setenv(name, str, 1));
+}
diff --git a/contrib/nvi/cl/cl_term.c b/contrib/nvi/cl/cl_term.c
new file mode 100644
index 000000000000..e4007403870e
--- /dev/null
+++ b/contrib/nvi/cl/cl_term.c
@@ -0,0 +1,459 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cl_term.c 10.22 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <curses.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "cl.h"
+
+static int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
+ */
+typedef struct _tklist {
+ char *ts; /* Key's termcap string. */
+ char *output; /* Corresponding vi command. */
+ char *name; /* Name. */
+ u_char value; /* Special value (for lookup). */
+} TKLIST;
+static TKLIST const c_tklist[] = { /* Command mappings. */
+ {"kil1", "O", "insert line"},
+ {"kdch1", "x", "delete character"},
+ {"kcud1", "j", "cursor down"},
+ {"kel", "D", "delete to eol"},
+ {"kind", "\004", "scroll down"}, /* ^D */
+ {"kll", "$", "go to eol"},
+ {"khome", "^", "go to sol"},
+ {"kich1", "i", "insert at cursor"},
+ {"kdl1", "dd", "delete line"},
+ {"kcub1", "h", "cursor left"},
+ {"knp", "\006", "page down"}, /* ^F */
+ {"kpp", "\002", "page up"}, /* ^B */
+ {"kri", "\025", "scroll up"}, /* ^U */
+ {"ked", "dG", "delete to end of screen"},
+ {"kcuf1", "l", "cursor right"},
+ {"kcuu1", "k", "cursor up"},
+ {NULL},
+};
+static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */
+ {NULL},
+};
+static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */
+ {"kcud1", "\033ja", "cursor down"}, /* ^[ja */
+ {"kcub1", "\033ha", "cursor left"}, /* ^[ha */
+ {"kcuu1", "\033ka", "cursor up"}, /* ^[ka */
+ {"kcuf1", "\033la", "cursor right"}, /* ^[la */
+ {NULL},
+};
+
+/*
+ * cl_term_init --
+ * Initialize the special keys defined by the termcap/terminfo entry.
+ *
+ * PUBLIC: int cl_term_init __P((SCR *));
+ */
+int
+cl_term_init(sp)
+ SCR *sp;
+{
+ KEYLIST *kp;
+ SEQ *qp;
+ TKLIST const *tkp;
+ char *t;
+
+ /* Command mappings. */
+ for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+ if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+ tkp->output, strlen(tkp->output), SEQ_COMMAND,
+ SEQ_NOOVERWRITE | SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings needing to be looked up. */
+ for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+ for (kp = keylist;; ++kp)
+ if (kp->value == tkp->value)
+ break;
+ if (kp == NULL)
+ continue;
+ if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+ &kp->ch, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings that are already set or are text deletions. */
+ for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+ /*
+ * !!!
+ * Some terminals' <cursor_left> keys send single <backspace>
+ * characters. This is okay in command mapping, but not okay
+ * in input mapping. That combination is the only one we'll
+ * ever see, hopefully, so kluge it here for now.
+ */
+ if (!strcmp(t, "\b"))
+ continue;
+ if (tkp->output == NULL) {
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), NULL, 0,
+ SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
+ return (1);
+ } else
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), tkp->output, strlen(tkp->output),
+ SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
+ return (1);
+ }
+
+ /*
+ * Rework any function key mappings that were set before the
+ * screen was initialized.
+ */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next)
+ if (F_ISSET(qp, SEQ_FUNCMAP))
+ (void)cl_pfmap(sp, qp->stype,
+ qp->input, qp->ilen, qp->output, qp->olen);
+ return (0);
+}
+
+/*
+ * cl_term_end --
+ * End the special keys defined by the termcap/terminfo entry.
+ *
+ * PUBLIC: int cl_term_end __P((GS *));
+ */
+int
+cl_term_end(gp)
+ GS *gp;
+{
+ SEQ *qp, *nqp;
+
+ /* Delete screen specific mappings. */
+ for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) {
+ nqp = qp->q.le_next;
+ if (F_ISSET(qp, SEQ_SCREEN))
+ (void)seq_mdel(qp);
+ }
+ return (0);
+}
+
+/*
+ * cl_fmap --
+ * Map a function key.
+ *
+ * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+ */
+int
+cl_fmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ seq_t stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ /* Ignore until the screen is running, do the real work then. */
+ if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
+ return (0);
+ if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
+ return (0);
+
+ return (cl_pfmap(sp, stype, from, flen, to, tlen));
+}
+
+/*
+ * cl_pfmap --
+ * Map a function key (private version).
+ */
+static int
+cl_pfmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ seq_t stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ size_t nlen;
+ char *p, keyname[64];
+
+ (void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1));
+ if ((p = tigetstr(keyname)) == NULL ||
+ p == (char *)-1 || strlen(p) == 0)
+ p = NULL;
+ if (p == NULL) {
+ msgq_str(sp, M_ERR, from, "233|This terminal has no %s key");
+ return (1);
+ }
+
+ nlen = snprintf(keyname,
+ sizeof(keyname), "function key %d", atoi(from + 1));
+ return (seq_set(sp, keyname, nlen,
+ p, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
+}
+
+/*
+ * cl_optchange --
+ * Curses screen specific "option changed" routine.
+ *
+ * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *));
+ */
+int
+cl_optchange(sp, opt, str, valp)
+ SCR *sp;
+ int opt;
+ char *str;
+ u_long *valp;
+{
+ CL_PRIVATE *clp;
+
+ clp = CLP(sp);
+
+ switch (opt) {
+ case O_COLUMNS:
+ case O_LINES:
+ case O_TERM:
+ /*
+ * Changing the columns, lines or terminal require that
+ * we restart the screen.
+ */
+ F_SET(sp->gp, G_SRESTART);
+ F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
+ break;
+ case O_MESG:
+ (void)cl_omesg(sp, clp, !*valp);
+ break;
+ case O_WINDOWNAME:
+ if (*valp) {
+ F_CLR(clp, CL_RENAME_OK);
+
+ (void)cl_rename(sp, NULL, 0);
+ } else {
+ F_SET(clp, CL_RENAME_OK);
+
+ /*
+ * If the screen is live, i.e. we're not reading the
+ * .exrc file, update the window.
+ */
+ if (sp->frp != NULL && sp->frp->name != NULL)
+ (void)cl_rename(sp, sp->frp->name, 1);
+ }
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cl_omesg --
+ * Turn the tty write permission on or off.
+ *
+ * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
+ */
+int
+cl_omesg(sp, clp, on)
+ SCR *sp;
+ CL_PRIVATE *clp;
+ int on;
+{
+ struct stat sb;
+ char *tty;
+
+ /* Find the tty, get the current permissions. */
+ if ((tty = ttyname(STDERR_FILENO)) == NULL) {
+ if (sp != NULL)
+ msgq(sp, M_SYSERR, "stderr");
+ return (1);
+ }
+ if (stat(tty, &sb) < 0) {
+ if (sp != NULL)
+ msgq(sp, M_SYSERR, "%s", tty);
+ return (1);
+ }
+
+ /* Save the original status if it's unknown. */
+ if (clp->tgw == TGW_UNKNOWN)
+ clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
+
+ /* Toggle the permissions. */
+ if (on) {
+ if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
+ if (sp != NULL)
+ msgq(sp, M_SYSERR,
+ "046|messages not turned on: %s", tty);
+ return (1);
+ }
+ } else
+ if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
+ if (sp != NULL)
+ msgq(sp, M_SYSERR,
+ "045|messages not turned off: %s", tty);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * cl_ssize --
+ * Return the terminal size.
+ *
+ * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
+ */
+int
+cl_ssize(sp, sigwinch, rowp, colp, changedp)
+ SCR *sp;
+ int sigwinch;
+ size_t *rowp, *colp;
+ int *changedp;
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+#endif
+ size_t col, row;
+ int rval;
+ char *p;
+
+ /* Assume it's changed. */
+ if (changedp != NULL)
+ *changedp = 1;
+
+ /*
+ * !!!
+ * sp may be NULL.
+ *
+ * Get the screen rows and columns. If the values are wrong, it's
+ * not a big deal -- as soon as the user sets them explicitly the
+ * environment will be set and the screen package will use the new
+ * values.
+ *
+ * Try TIOCGWINSZ.
+ */
+ row = col = 0;
+#ifdef TIOCGWINSZ
+ if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
+ row = win.ws_row;
+ col = win.ws_col;
+ }
+#endif
+ /* If here because of suspend or a signal, only trust TIOCGWINSZ. */
+ if (sigwinch) {
+ /*
+ * Somebody didn't get TIOCGWINSZ right, or has suspend
+ * without window resizing support. The user just lost,
+ * but there's nothing we can do.
+ */
+ if (row == 0 || col == 0) {
+ if (changedp != NULL)
+ *changedp = 0;
+ return (0);
+ }
+
+ /*
+ * SunOS systems deliver SIGWINCH when windows are uncovered
+ * as well as when they change size. In addition, we call
+ * here when continuing after being suspended since the window
+ * may have changed size. Since we don't want to background
+ * all of the screens just because the window was uncovered,
+ * ignore the signal if there's no change.
+ */
+ if (sp != NULL &&
+ row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
+ if (changedp != NULL)
+ *changedp = 0;
+ return (0);
+ }
+
+ if (rowp != NULL)
+ *rowp = row;
+ if (colp != NULL)
+ *colp = col;
+ return (0);
+ }
+
+ /*
+ * !!!
+ * If TIOCGWINSZ failed, or had entries of 0, try termcap. This
+ * routine is called before any termcap or terminal information
+ * has been set up. If there's no TERM environmental variable set,
+ * let it go, at least ex can run.
+ */
+ if (row == 0 || col == 0) {
+ if ((p = getenv("TERM")) == NULL)
+ goto noterm;
+ if (row == 0)
+ if ((rval = tigetnum("lines")) < 0)
+ msgq(sp, M_SYSERR, "tigetnum: lines");
+ else
+ row = rval;
+ if (col == 0)
+ if ((rval = tigetnum("cols")) < 0)
+ msgq(sp, M_SYSERR, "tigetnum: cols");
+ else
+ col = rval;
+ }
+
+ /* If nothing else, well, it's probably a VT100. */
+noterm: if (row == 0)
+ row = 24;
+ if (col == 0)
+ col = 80;
+
+ /*
+ * !!!
+ * POSIX 1003.2 requires the environment to override everything.
+ * Often, people can get nvi to stop messing up their screen by
+ * deleting the LINES and COLUMNS environment variables from their
+ * dot-files.
+ */
+ if ((p = getenv("LINES")) != NULL)
+ row = strtol(p, NULL, 10);
+ if ((p = getenv("COLUMNS")) != NULL)
+ col = strtol(p, NULL, 10);
+
+ if (rowp != NULL)
+ *rowp = row;
+ if (colp != NULL)
+ *colp = col;
+ return (0);
+}
+
+/*
+ * cl_putchar --
+ * Function version of putchar, for tputs.
+ *
+ * PUBLIC: int cl_putchar __P((int));
+ */
+int
+cl_putchar(ch)
+ int ch;
+{
+ return (putchar(ch));
+}
diff --git a/contrib/nvi/clib/bsearch.c b/contrib/nvi/clib/bsearch.c
new file mode 100644
index 000000000000..6b41ab5fcef8
--- /dev/null
+++ b/contrib/nvi/clib/bsearch.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)bsearch.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "port.h"
+
+/*
+ * Perform a binary search.
+ *
+ * The code below is a bit sneaky. After a comparison fails, we
+ * divide the work in half by moving either left or right. If lim
+ * is odd, moving left simply involves halving lim: e.g., when lim
+ * is 5 we look at item 2, so we change lim to 2 so that we will
+ * look at items 0 & 1. If lim is even, the same applies. If lim
+ * is odd, moving right again involes halving lim, this time moving
+ * the base up one item past p: e.g., when lim is 5 we change base
+ * to item 3 and make lim 2 so that we will look at items 3 and 4.
+ * If lim is even, however, we have to shrink it by one before
+ * halving: e.g., when lim is 4, we still looked at item 2, so we
+ * have to make lim 3, then halve, obtaining 1, so that we will only
+ * look at item 3.
+ *
+ * PUBLIC: #ifndef HAVE_BSEARCH
+ * PUBLIC: void *bsearch __P((const void *, const void *, size_t,
+ * PUBLIC: size_t, int (*)(const void *, const void *)));
+ * PUBLIC: #endif
+ */
+void *
+bsearch(key, base0, nmemb, size, compar)
+ register const void *key;
+ const void *base0;
+ size_t nmemb;
+ register size_t size;
+ register int (*compar) __P((const void *, const void *));
+{
+ register const char *base = base0;
+ register size_t lim;
+ register int cmp;
+ register const void *p;
+
+ for (lim = nmemb; lim != 0; lim >>= 1) {
+ p = base + (lim >> 1) * size;
+ cmp = (*compar)(key, p);
+ if (cmp == 0)
+ return ((void *)p);
+ if (cmp > 0) { /* key > p: move right */
+ base = (char *)p + size;
+ lim--;
+ } /* else move left */
+ }
+ return (NULL);
+}
diff --git a/contrib/nvi/clib/env.c b/contrib/nvi/clib/env.c
new file mode 100644
index 000000000000..5a45dc1760a6
--- /dev/null
+++ b/contrib/nvi/clib/env.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)getenv.c 8.1 (Berkeley) 6/4/93";
+static const char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * __findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ *
+ * This routine *should* be a static; don't use it.
+ */
+static char *
+__findenv(name, offset)
+ register char *name;
+ int *offset;
+{
+ extern char **environ;
+ register int len;
+ register char *np;
+ register char **p, *c;
+
+ if (name == NULL || environ == NULL)
+ return (NULL);
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (c = *p) != NULL; ++p)
+ if (strncmp(c, name, len) == 0 && c[len] == '=') {
+ *offset = p - environ;
+ return (c + len + 1);
+ }
+ return (NULL);
+}
+
+#ifndef HAVE_SETENV
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ *
+ * PUBLIC: #ifndef HAVE_SETENV
+ * PUBLIC: int setenv __P((const char *, const char *, int));
+ * PUBLIC: #endif
+ */
+setenv(name, value, rewrite)
+ register char *name;
+ register char *value;
+ int rewrite;
+{
+ extern char **environ;
+ static int alloced; /* if allocated space before */
+ register char *c;
+ int l_value, offset;
+
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = __findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return (0);
+ if (strlen(c) >= l_value) { /* old larger; copy over */
+ while (*c++ = *value++);
+ return (0);
+ }
+ } else { /* create new slot */
+ register int cnt;
+ register char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ }
+ else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
+ if (!p)
+ return (-1);
+ memmove(p, environ, cnt * sizeof(char *));
+ environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((size_t)((int)(c - name) + l_value + 2))))
+ return (-1);
+ for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
+ for (*c++ = '='; *c++ = *value++;);
+ return (0);
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ *
+ * PUBLIC: #ifndef HAVE_UNSETENV
+ * PUBLIC: void unsetenv __P((const char *));
+ * PUBLIC: #endif
+ */
+void
+unsetenv(name)
+ char *name;
+{
+ extern char **environ;
+ register char **p;
+ int offset;
+
+ while (__findenv(name, &offset)) /* if set multiple times */
+ for (p = &environ[offset];; ++p)
+ if (!(*p = *(p + 1)))
+ break;
+}
+#endif
diff --git a/contrib/nvi/clib/gethostname.c b/contrib/nvi/clib/gethostname.c
new file mode 100644
index 000000000000..5b8e85acf9f2
--- /dev/null
+++ b/contrib/nvi/clib/gethostname.c
@@ -0,0 +1,22 @@
+#include "config.h"
+
+/*
+ * Solaris doesn't include the gethostname call by default.
+ */
+#include <sys/utsname.h>
+#include <sys/systeminfo.h>
+
+#include <netdb.h>
+
+/*
+ * PUBLIC: #ifndef HAVE_GETHOSTNAME
+ * PUBLIC: int gethostname __P((char *, int));
+ * PUBLIC: #endif
+ */
+int
+gethostname(host, len)
+ char *host;
+ int len;
+{
+ return (sysinfo(SI_HOSTNAME, host, len) == -1 ? -1 : 0);
+}
diff --git a/contrib/nvi/clib/getopt.c b/contrib/nvi/clib/getopt.c
new file mode 100644
index 000000000000..b173017503e1
--- /dev/null
+++ b/contrib/nvi/clib/getopt.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1987, 1993, 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.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)getopt.c 8.2 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * PUBLIC: #ifndef HAVE_GETOPT
+ * PUBLIC: int getopt __P((int, char * const *, const char *));
+ * PUBLIC: #endif
+ */
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *progname;
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (!progname) {
+ if ((progname = strrchr(*nargv, '/')) == NULL)
+ progname = *nargv;
+ else
+ ++progname;
+ }
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (EOF);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return (EOF);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means EOF.
+ */
+ if (optopt == (int)'-')
+ return (EOF);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", progname, optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ progname, optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
diff --git a/contrib/nvi/clib/memchr.c b/contrib/nvi/clib/memchr.c
new file mode 100644
index 000000000000..b6d4c311bff6
--- /dev/null
+++ b/contrib/nvi/clib/memchr.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)memchr.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * PUBLIC: #ifndef HAVE_MEMCHR
+ * PUBLIC: void *memchr __P((const void *, int, size_t));
+ * PUBLIC: #endif
+ */
+void *
+memchr(s, c, n)
+ const void *s;
+ register unsigned char c;
+ register size_t n;
+{
+ if (n != 0) {
+ register const unsigned char *p = s;
+
+ do {
+ if (*p++ == c)
+ return ((void *)(p - 1));
+ } while (--n != 0);
+ }
+ return (NULL);
+}
diff --git a/contrib/nvi/clib/memmove.c b/contrib/nvi/clib/memmove.c
new file mode 100644
index 000000000000..7f4376272b67
--- /dev/null
+++ b/contrib/nvi/clib/memmove.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef int word; /* "word" used for optimal copy speed */
+
+#define wsize sizeof(word)
+#define wmask (wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ *
+ * PUBLIC: #ifndef HAVE_MEMCPY
+ * PUBLIC: void *memcpy __P((void *, const void *, size_t));
+ * PUBLIC: #endif
+ * PUBLIC: #ifndef HAVE_MEMMOVE
+ * PUBLIC: void *memmove __P((void *, const void *, size_t));
+ * PUBLIC: #endif
+ */
+#ifdef MEMCOPY
+void *
+memcpy(dst0, src0, length)
+#else
+#ifdef MEMMOVE
+void *
+memmove(dst0, src0, length)
+#else
+void
+bcopy(src0, dst0, length)
+#endif
+#endif
+ void *dst0;
+ const void *src0;
+ register size_t length;
+{
+ register char *dst = dst0;
+ register const char *src = src0;
+ register size_t t;
+
+ if (length == 0 || dst == src) /* nothing to do */
+ goto done;
+
+ /*
+ * Macros: loop-t-times; and loop-t-times, t>0
+ */
+#define TLOOP(s) if (t) TLOOP1(s)
+#define TLOOP1(s) do { s; } while (--t)
+
+ if ((unsigned long)dst < (unsigned long)src) {
+ /*
+ * Copy forward.
+ */
+ t = (int)src; /* only need low bits */
+ if ((t | (int)dst) & wmask) {
+ /*
+ * Try to align operands. This cannot be done
+ * unless the low bits match.
+ */
+ if ((t ^ (int)dst) & wmask || length < wsize)
+ t = length;
+ else
+ t = wsize - (t & wmask);
+ length -= t;
+ TLOOP1(*dst++ = *src++);
+ }
+ /*
+ * Copy whole words, then mop up any trailing bytes.
+ */
+ t = length / wsize;
+ TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
+ t = length & wmask;
+ TLOOP(*dst++ = *src++);
+ } else {
+ /*
+ * Copy backwards. Otherwise essentially the same.
+ * Alignment works as before, except that it takes
+ * (t&wmask) bytes to align, not wsize-(t&wmask).
+ */
+ src += length;
+ dst += length;
+ t = (int)src;
+ if ((t | (int)dst) & wmask) {
+ if ((t ^ (int)dst) & wmask || length <= wsize)
+ t = length;
+ else
+ t &= wmask;
+ length -= t;
+ TLOOP1(*--dst = *--src);
+ }
+ t = length / wsize;
+ TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
+ t = length & wmask;
+ TLOOP(*--dst = *--src);
+ }
+done:
+#if defined(MEMCOPY) || defined(MEMMOVE)
+ return (dst0);
+#else
+ return;
+#endif
+}
diff --git a/contrib/nvi/clib/memset.c b/contrib/nvi/clib/memset.c
new file mode 100644
index 000000000000..0013d66df06d
--- /dev/null
+++ b/contrib/nvi/clib/memset.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Hibler and Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)memset.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <string.h>
+
+/*
+ * PUBLIC: #ifndef HAVE_MEMSET
+ * PUBLIC: void *memset __P((void *, int, size_t));
+ * PUBLIC: #endif
+ */
+#define wsize sizeof(u_int)
+#define wmask (wsize - 1)
+
+#ifdef BZERO
+#define RETURN return
+#define VAL 0
+#define WIDEVAL 0
+
+void
+bzero(dst0, length)
+ void *dst0;
+ register size_t length;
+#else
+#define RETURN return (dst0)
+#define VAL c0
+#define WIDEVAL c
+
+void *
+memset(dst0, c0, length)
+ void *dst0;
+ register int c0;
+ register size_t length;
+#endif
+{
+ register size_t t;
+ register u_int c;
+ register u_char *dst;
+
+ dst = dst0;
+ /*
+ * If not enough words, just fill bytes. A length >= 2 words
+ * guarantees that at least one of them is `complete' after
+ * any necessary alignment. For instance:
+ *
+ * |-----------|-----------|-----------|
+ * |00|01|02|03|04|05|06|07|08|09|0A|00|
+ * ^---------------------^
+ * dst dst+length-1
+ *
+ * but we use a minimum of 3 here since the overhead of the code
+ * to do word writes is substantial.
+ */
+ if (length < 3 * wsize) {
+ while (length != 0) {
+ *dst++ = VAL;
+ --length;
+ }
+ RETURN;
+ }
+
+#ifndef BZERO
+ if ((c = (u_char)c0) != 0) { /* Fill the word. */
+ c = (c << 8) | c; /* u_int is 16 bits. */
+#if UINT_MAX > 0xffff
+ c = (c << 16) | c; /* u_int is 32 bits. */
+#endif
+#if UINT_MAX > 0xffffffff
+ c = (c << 32) | c; /* u_int is 64 bits. */
+#endif
+ }
+#endif
+ /* Align destination by filling in bytes. */
+ if ((t = (int)dst & wmask) != 0) {
+ t = wsize - t;
+ length -= t;
+ do {
+ *dst++ = VAL;
+ } while (--t != 0);
+ }
+
+ /* Fill words. Length was >= 2*words so we know t >= 1 here. */
+ t = length / wsize;
+ do {
+ *(u_int *)dst = WIDEVAL;
+ dst += wsize;
+ } while (--t != 0);
+
+ /* Mop up trailing bytes, if any. */
+ t = length & wmask;
+ if (t != 0)
+ do {
+ *dst++ = VAL;
+ } while (--t != 0);
+ RETURN;
+}
diff --git a/contrib/nvi/clib/mkstemp.c b/contrib/nvi/clib/mkstemp.c
new file mode 100644
index 000000000000..1faffddfbe2d
--- /dev/null
+++ b/contrib/nvi/clib/mkstemp.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+
+static int _gettemp();
+
+/*
+ * PUBLIC: #ifndef HAVE_MKSTEMP
+ * PUBLIC: int mkstemp __P((char *));
+ * PUBLIC: #endif
+ */
+mkstemp(path)
+ char *path;
+{
+ int fd;
+
+ return (_gettemp(path, &fd) ? fd : -1);
+}
+
+char *
+mktemp(path)
+ char *path;
+{
+ return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
+}
+
+static
+_gettemp(path, doopen)
+ char *path;
+ register int *doopen;
+{
+ extern int errno;
+ register char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
diff --git a/contrib/nvi/clib/mmap.c b/contrib/nvi/clib/mmap.c
new file mode 100644
index 000000000000..7cea169650b4
--- /dev/null
+++ b/contrib/nvi/clib/mmap.c
@@ -0,0 +1,50 @@
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This function fakes mmap() by reading `len' bytes from the file descriptor
+ * `fd' and returning a pointer to that memory. The "mapped" region can later
+ * be deallocated with munmap().
+ *
+ * Note: ONLY reading is supported and only reading of the exact size of the
+ * file will work.
+ *
+ * PUBLIC: #ifndef HAVE_MMAP
+ * PUBLIC: char *mmap __P((char *, size_t, int, int, int, off_t));
+ * PUBLIC: #endif
+ */
+char *
+mmap(addr, len, prot, flags, fd, off)
+ char *addr;
+ size_t len;
+ int prot, flags, fd;
+ off_t off;
+{
+ char *ptr;
+
+ if ((ptr = (char *)malloc(len)) == 0)
+ return ((char *)-1);
+ if (read(fd, ptr, len) < 0) {
+ free(ptr);
+ return ((char *)-1);
+ }
+ return (ptr);
+}
+
+/*
+ * PUBLIC: #ifndef HAVE_MMAP
+ * PUBLIC: int munmap __P((char *, size_t));
+ * PUBLIC: #endif
+ */
+int
+munmap(addr, len)
+ char *addr;
+ size_t len;
+{
+ free(addr);
+ return (0);
+}
diff --git a/contrib/nvi/clib/snprintf.c b/contrib/nvi/clib/snprintf.c
new file mode 100644
index 000000000000..935522ce5436
--- /dev/null
+++ b/contrib/nvi/clib/snprintf.c
@@ -0,0 +1,45 @@
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * PUBLIC: #ifndef HAVE_SNPRINTF
+ * PUBLIC: int snprintf __P((char *, size_t, const char *, ...));
+ * PUBLIC: #endif
+ */
+int
+#ifdef __STDC__
+snprintf(char *str, size_t n, const char *fmt, ...)
+#else
+snprintf(str, n, fmt, va_alist)
+ char *str;
+ size_t n;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ int rval;
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+#ifdef SPRINTF_RET_CHARPNT
+ (void)vsprintf(str, fmt, ap);
+ va_end(ap);
+ return (strlen(str));
+#else
+ rval = vsprintf(str, fmt, ap);
+ va_end(ap);
+ return (rval);
+#endif
+}
diff --git a/contrib/nvi/clib/strdup.c b/contrib/nvi/clib/strdup.c
new file mode 100644
index 000000000000..4bc2303cee54
--- /dev/null
+++ b/contrib/nvi/clib/strdup.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * PUBLIC: #ifndef HAVE_STRDUP
+ * PUBLIC: char *strdup __P((const char *));
+ * PUBLIC: #endif
+ */
+char *
+strdup(str)
+ const char *str;
+{
+ size_t len;
+ char *copy;
+
+ len = strlen(str) + 1;
+ if (!(copy = malloc((u_int)len)))
+ return (NULL);
+ memcpy(copy, str, len);
+ return (copy);
+}
diff --git a/contrib/nvi/clib/strerror.c b/contrib/nvi/clib/strerror.c
new file mode 100644
index 000000000000..cf5910d0fc83
--- /dev/null
+++ b/contrib/nvi/clib/strerror.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * PUBLIC: #ifndef HAVE_STRERROR
+ * PUBLIC: char *strerror __P((int));
+ * PUBLIC: #endif
+ */
+char *
+strerror(num)
+ int num;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+#define UPREFIX "Unknown error: "
+ static char ebuf[40] = UPREFIX; /* 64-bit number + slop */
+ register unsigned int errnum;
+ register char *p, *t;
+ char tmp[40];
+
+ errnum = num; /* convert to unsigned */
+ if (errnum < sys_nerr)
+ return(sys_errlist[errnum]);
+
+ /* Do this by hand, so we don't include stdio(3). */
+ t = tmp;
+ do {
+ *t++ = "0123456789"[errnum % 10];
+ } while (errnum /= 10);
+ for (p = ebuf + sizeof(UPREFIX) - 1;;) {
+ *p++ = *--t;
+ if (t <= tmp)
+ break;
+ }
+ return(ebuf);
+}
diff --git a/contrib/nvi/clib/strpbrk.c b/contrib/nvi/clib/strpbrk.c
new file mode 100644
index 000000000000..22abbb0da4b5
--- /dev/null
+++ b/contrib/nvi/clib/strpbrk.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strpbrk.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+
+/*
+ * Find the first occurrence in s1 of a character in s2 (excluding NUL).
+ *
+ * PUBLIC: #ifndef HAVE_STRPBRK
+ * PUBLIC: char *strpbrk __P((const char *, const char *));
+ * PUBLIC: #endif
+ */
+char *
+strpbrk(s1, s2)
+ register const char *s1, *s2;
+{
+ register const char *scanp;
+ register int c, sc;
+
+ while ((c = *s1++) != 0) {
+ for (scanp = s2; (sc = *scanp++) != 0;)
+ if (sc == c)
+ return ((char *)(s1 - 1));
+ }
+ return (NULL);
+}
diff --git a/contrib/nvi/clib/strsep.c b/contrib/nvi/clib/strsep.c
new file mode 100644
index 000000000000..33115f0e4121
--- /dev/null
+++ b/contrib/nvi/clib/strsep.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ *
+ * PUBLIC: #ifndef HAVE_STRSEP
+ * PUBLIC: char *strsep __P((char **, const char *));
+ * PUBLIC: #endif
+ */
+char *
+strsep(stringp, delim)
+ register char **stringp;
+ register const char *delim;
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
diff --git a/contrib/nvi/clib/strtol.c b/contrib/nvi/clib/strtol.c
new file mode 100644
index 000000000000..50c724ff432f
--- /dev/null
+++ b/contrib/nvi/clib/strtol.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ *
+ * PUBLIC: #ifndef HAVE_STRTOL
+ * PUBLIC: long strtol __P((const char *, char **, int));
+ * PUBLIC: #endif
+ */
+long
+strtol(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s = nptr;
+ register unsigned long acc;
+ register int c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/contrib/nvi/clib/strtoul.c b/contrib/nvi/clib/strtoul.c
new file mode 100644
index 000000000000..890bdbd747a7
--- /dev/null
+++ b/contrib/nvi/clib/strtoul.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ *
+ * PUBLIC: #ifndef HAVE_STRTOUL
+ * PUBLIC: unsigned long strtoul __P((const char *, char **, int));
+ * PUBLIC: #endif
+ */
+unsigned long
+strtoul(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s = nptr;
+ register unsigned long acc;
+ register int c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+ cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/contrib/nvi/clib/vsnprintf.c b/contrib/nvi/clib/vsnprintf.c
new file mode 100644
index 000000000000..a1b013ad196f
--- /dev/null
+++ b/contrib/nvi/clib/vsnprintf.c
@@ -0,0 +1,31 @@
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * PUBLIC: #ifndef HAVE_VSNPRINTF
+ * PUBLIC: int vsnprintf __P((char *, size_t, const char *, ...));
+ * PUBLIC: #endif
+ */
+int
+vsnprintf(str, n, fmt, ap)
+ char *str;
+ size_t n;
+ const char *fmt;
+ va_list ap;
+{
+#ifdef SPRINTF_RET_CHARPNT
+ (void)vsprintf(str, fmt, ap);
+ return (strlen(str));
+#else
+ return (vsprintf(str, fmt, ap));
+#endif
+}
diff --git a/contrib/nvi/common/api.c b/contrib/nvi/common/api.c
new file mode 100644
index 000000000000..35d9f0c8f66e
--- /dev/null
+++ b/contrib/nvi/common/api.c
@@ -0,0 +1,525 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1995
+ * George V. Neville-Neil. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)api.c 8.26 (Berkeley) 10/14/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+extern GS *__global_list; /* XXX */
+
+/*
+ * api_fscreen --
+ * Return a pointer to the screen specified by the screen id
+ * or a file name.
+ *
+ * PUBLIC: SCR *api_fscreen __P((int, char *));
+ */
+SCR *
+api_fscreen(id, name)
+ int id;
+ char *name;
+{
+ GS *gp;
+ SCR *tsp;
+
+ gp = __global_list;
+
+ /* Search the displayed list. */
+ for (tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (name == NULL) {
+ if (id == tsp->id)
+ return (tsp);
+ } else if (!strcmp(name, tsp->frp->name))
+ return (tsp);
+
+ /* Search the hidden list. */
+ for (tsp = gp->hq.cqh_first;
+ tsp != (void *)&gp->hq; tsp = tsp->q.cqe_next)
+ if (name == NULL) {
+ if (id == tsp->id)
+ return (tsp);
+ } else if (!strcmp(name, tsp->frp->name))
+ return (tsp);
+ return (NULL);
+}
+
+/*
+ * api_aline --
+ * Append a line.
+ *
+ * PUBLIC: int api_aline __P((SCR *, recno_t, char *, size_t));
+ */
+int
+api_aline(sp, lno, line, len)
+ SCR *sp;
+ recno_t lno;
+ char *line;
+ size_t len;
+{
+ return (db_append(sp, 1, lno, line, len));
+}
+
+/*
+ * api_dline --
+ * Delete a line.
+ *
+ * PUBLIC: int api_dline __P((SCR *, recno_t));
+ */
+int
+api_dline(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ return (db_delete(sp, lno));
+}
+
+/*
+ * api_gline --
+ * Get a line.
+ *
+ * PUBLIC: int api_gline __P((SCR *, recno_t, char **, size_t *));
+ */
+int
+api_gline(sp, lno, linepp, lenp)
+ SCR *sp;
+ recno_t lno;
+ char **linepp;
+ size_t *lenp;
+{
+ int isempty;
+
+ if (db_eget(sp, lno, linepp, lenp, &isempty)) {
+ if (isempty)
+ msgq(sp, M_ERR, "209|The file is empty");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * api_iline --
+ * Insert a line.
+ *
+ * PUBLIC: int api_iline __P((SCR *, recno_t, char *, size_t));
+ */
+int
+api_iline(sp, lno, line, len)
+ SCR *sp;
+ recno_t lno;
+ char *line;
+ size_t len;
+{
+ return (db_insert(sp, lno, line, len));
+}
+
+/*
+ * api_lline --
+ * Return the line number of the last line in the file.
+ *
+ * PUBLIC: int api_lline __P((SCR *, recno_t *));
+ */
+int
+api_lline(sp, lnop)
+ SCR *sp;
+ recno_t *lnop;
+{
+ return (db_last(sp, lnop));
+}
+
+/*
+ * api_sline --
+ * Set a line.
+ *
+ * PUBLIC: int api_sline __P((SCR *, recno_t, char *, size_t));
+ */
+int
+api_sline(sp, lno, line, len)
+ SCR *sp;
+ recno_t lno;
+ char *line;
+ size_t len;
+{
+ return (db_set(sp, lno, line, len));
+}
+
+/*
+ * api_getmark --
+ * Get the mark.
+ *
+ * PUBLIC: int api_getmark __P((SCR *, int, MARK *));
+ */
+int
+api_getmark(sp, markname, mp)
+ SCR *sp;
+ int markname;
+ MARK *mp;
+{
+ return (mark_get(sp, (ARG_CHAR_T)markname, mp, M_ERR));
+}
+
+/*
+ * api_setmark --
+ * Set the mark.
+ *
+ * PUBLIC: int api_setmark __P((SCR *, int, MARK *));
+ */
+int
+api_setmark(sp, markname, mp)
+ SCR *sp;
+ int markname;
+ MARK *mp;
+{
+ return (mark_set(sp, (ARG_CHAR_T)markname, mp, 1));
+}
+
+/*
+ * api_nextmark --
+ * Return the first mark if next not set, otherwise return the
+ * subsequent mark.
+ *
+ * PUBLIC: int api_nextmark __P((SCR *, int, char *));
+ */
+int
+api_nextmark(sp, next, namep)
+ SCR *sp;
+ int next;
+ char *namep;
+{
+ LMARK *mp;
+
+ mp = sp->ep->marks.lh_first;
+ if (next)
+ for (; mp != NULL; mp = mp->q.le_next)
+ if (mp->name == *namep) {
+ mp = mp->q.le_next;
+ break;
+ }
+ if (mp == NULL)
+ return (1);
+ *namep = mp->name;
+ return (0);
+}
+
+/*
+ * api_getcursor --
+ * Get the cursor.
+ *
+ * PUBLIC: int api_getcursor __P((SCR *, MARK *));
+ */
+int
+api_getcursor(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ mp->lno = sp->lno;
+ mp->cno = sp->cno;
+ return (0);
+}
+
+/*
+ * api_setcursor --
+ * Set the cursor.
+ *
+ * PUBLIC: int api_setcursor __P((SCR *, MARK *));
+ */
+int
+api_setcursor(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ size_t len;
+
+ if (db_get(sp, mp->lno, DBG_FATAL, NULL, &len))
+ return (1);
+ if (mp->cno < 0 || mp->cno > len) {
+ msgq(sp, M_ERR, "Cursor set to nonexistent column");
+ return (1);
+ }
+
+ /* Set the cursor. */
+ sp->lno = mp->lno;
+ sp->cno = mp->cno;
+ return (0);
+}
+
+/*
+ * api_emessage --
+ * Print an error message.
+ *
+ * PUBLIC: void api_emessage __P((SCR *, char *));
+ */
+void
+api_emessage(sp, text)
+ SCR *sp;
+ char *text;
+{
+ msgq(sp, M_ERR, "%s", text);
+}
+
+/*
+ * api_imessage --
+ * Print an informational message.
+ *
+ * PUBLIC: void api_imessage __P((SCR *, char *));
+ */
+void
+api_imessage(sp, text)
+ SCR *sp;
+ char *text;
+{
+ msgq(sp, M_INFO, "%s", text);
+}
+
+/*
+ * api_edit
+ * Create a new screen and return its id
+ * or edit a new file in the current screen.
+ *
+ * PUBLIC: int api_edit __P((SCR *, char *, SCR **, int));
+ */
+int
+api_edit(sp, file, spp, newscreen)
+ SCR *sp;
+ char *file;
+ SCR **spp;
+ int newscreen;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ if (file) {
+ ex_cinit(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, file, strlen(file));
+ } else
+ ex_cinit(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, NULL);
+ if (newscreen)
+ cmd.flags |= E_NEWSCREEN; /* XXX */
+ if (cmd.cmd->fn(sp, &cmd))
+ return (1);
+ *spp = sp->nextdisp;
+ return (0);
+}
+
+/*
+ * api_escreen
+ * End a screen.
+ *
+ * PUBLIC: int api_escreen __P((SCR *));
+ */
+int
+api_escreen(sp)
+ SCR *sp;
+{
+ EXCMD cmd;
+
+ /*
+ * XXX
+ * If the interpreter exits anything other than the current
+ * screen, vi isn't going to update everything correctly.
+ */
+ ex_cinit(&cmd, C_QUIT, 0, OOBLNO, OOBLNO, 0, NULL);
+ return (cmd.cmd->fn(sp, &cmd));
+}
+
+/*
+ * api_swscreen --
+ * Switch to a new screen.
+ *
+ * PUBLIC: int api_swscreen __P((SCR *, SCR *));
+ */
+int
+api_swscreen(sp, new)
+ SCR *sp, *new;
+{
+ /*
+ * XXX
+ * If the interpreter switches from anything other than the
+ * current screen, vi isn't going to update everything correctly.
+ */
+ sp->nextdisp = new;
+ F_SET(sp, SC_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * api_map --
+ * Map a key.
+ *
+ * PUBLIC: int api_map __P((SCR *, char *, char *, size_t));
+ */
+int
+api_map(sp, name, map, len)
+ SCR *sp;
+ char *name, *map;
+ size_t len;
+{
+ ARGS *ap[3], a, b;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_MAP, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, name, strlen(name));
+ ex_cadd(&cmd, &b, map, len);
+ return (cmd.cmd->fn(sp, &cmd));
+}
+
+/*
+ * api_unmap --
+ * Unmap a key.
+ *
+ * PUBLIC: int api_unmap __P((SCR *, char *));
+ */
+int
+api_unmap(sp, name)
+ SCR *sp;
+ char *name;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_UNMAP, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, name, strlen(name));
+ return (cmd.cmd->fn(sp, &cmd));
+}
+
+/*
+ * api_opts_get --
+ * Return a option value as a string, in allocated memory.
+ * If the option is of type boolean, boolvalue is (un)set
+ * according to the value; otherwise boolvalue is -1.
+ *
+ * PUBLIC: int api_opts_get __P((SCR *, char *, char **, int *));
+ */
+int
+api_opts_get(sp, name, value, boolvalue)
+ SCR *sp;
+ char *name, **value;
+ int *boolvalue;
+{
+ OPTLIST const *op;
+ int offset;
+
+ if ((op = opts_search(name)) == NULL) {
+ opts_nomatch(sp, name);
+ return (1);
+ }
+
+ offset = op - optlist;
+ if (boolvalue != NULL)
+ *boolvalue = -1;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ MALLOC_RET(sp, *value, char *, strlen(op->name) + 2 + 1);
+ (void)sprintf(*value,
+ "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
+ if (boolvalue != NULL)
+ *boolvalue = O_ISSET(sp, offset);
+ break;
+ case OPT_NUM:
+ MALLOC_RET(sp, *value, char *, 20);
+ (void)sprintf(*value, "%lu", (u_long)O_VAL(sp, offset));
+ break;
+ case OPT_STR:
+ if (O_STR(sp, offset) == NULL) {
+ MALLOC_RET(sp, *value, char *, 2);
+ value[0] = '\0';
+ } else {
+ MALLOC_RET(sp,
+ *value, char *, strlen(O_STR(sp, offset)) + 1);
+ (void)sprintf(*value, "%s", O_STR(sp, offset));
+ }
+ break;
+ }
+ return (0);
+}
+
+/*
+ * api_opts_set --
+ * Set options.
+ *
+ * PUBLIC: int api_opts_set __P((SCR *, char *, char *, u_long, int));
+ */
+int
+api_opts_set(sp, name, str_value, num_value, bool_value)
+ SCR *sp;
+ char *name, *str_value;
+ u_long num_value;
+ int bool_value;
+{
+ ARGS *ap[2], a, b;
+ OPTLIST const *op;
+ int rval;
+ size_t blen;
+ char *bp;
+
+ if ((op = opts_search(name)) == NULL) {
+ opts_nomatch(sp, name);
+ return (1);
+ }
+
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ GET_SPACE_RET(sp, bp, blen, 64);
+ a.len = snprintf(bp, 64, "%s%s", bool_value ? "" : "no", name);
+ break;
+ case OPT_NUM:
+ GET_SPACE_RET(sp, bp, blen, 64);
+ a.len = snprintf(bp, 64, "%s=%lu", name, num_value);
+ break;
+ case OPT_STR:
+ GET_SPACE_RET(sp, bp, blen, 1024);
+ a.len = snprintf(bp, 1024, "%s=%s", name, str_value);
+ break;
+ }
+ a.bp = bp;
+ b.len = 0;
+ b.bp = NULL;
+ ap[0] = &a;
+ ap[1] = &b;
+ rval = opts_set(sp, ap, NULL);
+
+ FREE_SPACE(sp, bp, blen);
+
+ return (rval);
+}
+
+/*
+ * api_run_str --
+ * Execute a string as an ex command.
+ *
+ * PUBLIC: int api_run_str __P((SCR *, char *));
+ */
+int
+api_run_str(sp, cmd)
+ SCR *sp;
+ char *cmd;
+{
+ return (ex_run_str(sp, NULL, cmd, strlen(cmd), 0, 0));
+}
diff --git a/contrib/nvi/common/args.h b/contrib/nvi/common/args.h
new file mode 100644
index 000000000000..e84dc2ca04c1
--- /dev/null
+++ b/contrib/nvi/common/args.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)args.h 10.2 (Berkeley) 3/6/96
+ */
+
+/*
+ * Structure for building "argc/argv" vector of arguments.
+ *
+ * !!!
+ * All arguments are nul terminated as well as having an associated length.
+ * The argument vector is NOT necessarily NULL terminated. The proper way
+ * to check the number of arguments is to use the argc value in the EXCMDARG
+ * structure or to walk the array until an ARGS structure with a length of 0
+ * is found.
+ */
+typedef struct _args {
+ CHAR_T *bp; /* Argument. */
+ size_t blen; /* Buffer length. */
+ size_t len; /* Argument length. */
+
+#define A_ALLOCATED 0x01 /* If allocated space. */
+ u_int8_t flags;
+} ARGS;
diff --git a/contrib/nvi/common/common.h b/contrib/nvi/common/common.h
new file mode 100644
index 000000000000..0e13fc80b844
--- /dev/null
+++ b/contrib/nvi/common/common.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)common.h 10.13 (Berkeley) 9/25/96
+ */
+
+/*
+ * Porting information built at configuration time. Included before
+ * any of nvi's include files.
+ */
+#include "port.h"
+
+/*
+ * Pseudo-local includes. These are files that are unlikely to exist
+ * on most machines to which we're porting vi, and we want to include
+ * them in a very specific order, regardless.
+ */
+#include <db.h>
+#include <regex.h>
+
+/*
+ * Forward structure declarations. Not pretty, but the include files
+ * are far too interrelated for a clean solution.
+ */
+typedef struct _cb CB;
+typedef struct _csc CSC;
+typedef struct _event EVENT;
+typedef struct _excmd EXCMD;
+typedef struct _exf EXF;
+typedef struct _fref FREF;
+typedef struct _gs GS;
+typedef struct _lmark LMARK;
+typedef struct _mark MARK;
+typedef struct _msg MSGS;
+typedef struct _option OPTION;
+typedef struct _optlist OPTLIST;
+typedef struct _scr SCR;
+typedef struct _script SCRIPT;
+typedef struct _seq SEQ;
+typedef struct _tag TAG;
+typedef struct _tagf TAGF;
+typedef struct _tagq TAGQ;
+typedef struct _text TEXT;
+
+/* Autoindent state. */
+typedef enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_t;
+
+/* Busy message types. */
+typedef enum { BUSY_ON = 1, BUSY_OFF, BUSY_UPDATE } busy_t;
+
+/*
+ * Routines that return a confirmation return:
+ *
+ * CONF_NO User answered no.
+ * CONF_QUIT User answered quit, eof or an error.
+ * CONF_YES User answered yes.
+ */
+typedef enum { CONF_NO, CONF_QUIT, CONF_YES } conf_t;
+
+/* Directions. */
+typedef enum { NOTSET, FORWARD, BACKWARD } dir_t;
+
+/* Line operations. */
+typedef enum { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET } lnop_t;
+
+/* Lock return values. */
+typedef enum { LOCK_FAILED, LOCK_SUCCESS, LOCK_UNAVAIL } lockr_t;
+
+/* Sequence types. */
+typedef enum { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT } seq_t;
+
+/*
+ * Local includes.
+ */
+#include "key.h" /* Required by args.h. */
+#include "args.h" /* Required by options.h. */
+#include "options.h" /* Required by screen.h. */
+
+#include "msg.h" /* Required by gs.h. */
+#include "cut.h" /* Required by gs.h. */
+#include "seq.h" /* Required by screen.h. */
+#include "util.h" /* Required by ex.h. */
+#include "mark.h" /* Required by gs.h. */
+#include "../ex/ex.h" /* Required by gs.h. */
+#include "gs.h" /* Required by screen.h. */
+#include "screen.h" /* Required by exf.h. */
+#include "exf.h"
+#include "log.h"
+#include "mem.h"
+
+#include "com_extern.h"
diff --git a/contrib/nvi/common/cut.c b/contrib/nvi/common/cut.c
new file mode 100644
index 000000000000..faceecd11166
--- /dev/null
+++ b/contrib/nvi/common/cut.c
@@ -0,0 +1,368 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)cut.c 10.10 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static void cb_rotate __P((SCR *));
+
+/*
+ * cut --
+ * Put a range of lines/columns into a TEXT buffer.
+ *
+ * There are two buffer areas, both found in the global structure. The first
+ * is the linked list of all the buffers the user has named, the second is the
+ * unnamed buffer storage. There is a pointer, too, which is the current
+ * default buffer, i.e. it may point to the unnamed buffer or a named buffer
+ * depending on into what buffer the last text was cut. Logically, in both
+ * delete and yank operations, if the user names a buffer, the text is cut
+ * into it. If it's a delete of information on more than a single line, the
+ * contents of the numbered buffers are rotated up one, the contents of the
+ * buffer named '9' are discarded, and the text is cut into the buffer named
+ * '1'. The text is always cut into the unnamed buffer.
+ *
+ * In all cases, upper-case buffer names are the same as lower-case names,
+ * with the exception that they cause the buffer to be appended to instead
+ * of replaced. Note, however, that if text is appended to a buffer, the
+ * default buffer only contains the appended text, not the entire contents
+ * of the buffer.
+ *
+ * !!!
+ * The contents of the default buffer would disappear after most operations
+ * in historic vi. It's unclear that this is useful, so we don't bother.
+ *
+ * When users explicitly cut text into the numeric buffers, historic vi became
+ * genuinely strange. I've never been able to figure out what was supposed to
+ * happen. It behaved differently if you deleted text than if you yanked text,
+ * and, in the latter case, the text was appended to the buffer instead of
+ * replacing the contents. Hopefully it's not worth getting right, and here
+ * we just treat the numeric buffers like any other named buffer.
+ *
+ * PUBLIC: int cut __P((SCR *, CHAR_T *, MARK *, MARK *, int));
+ */
+int
+cut(sp, namep, fm, tm, flags)
+ SCR *sp;
+ CHAR_T *namep;
+ MARK *fm, *tm;
+ int flags;
+{
+ CB *cbp;
+ CHAR_T name;
+ recno_t lno;
+ int append, copy_one, copy_def;
+
+ /*
+ * If the user specified a buffer, put it there. (This may require
+ * a copy into the numeric buffers. We do the copy so that we don't
+ * have to reference count and so we don't have to deal with things
+ * like appends to buffers that are used multiple times.)
+ *
+ * Otherwise, if it's supposed to be put in a numeric buffer (usually
+ * a delete) put it there. The rules for putting things in numeric
+ * buffers were historically a little strange. There were three cases.
+ *
+ * 1: Some motions are always line mode motions, which means
+ * that the cut always goes into the numeric buffers.
+ * 2: Some motions aren't line mode motions, e.g. d10w, but
+ * can cross line boundaries. For these commands, if the
+ * cut crosses a line boundary, it goes into the numeric
+ * buffers. This includes most of the commands.
+ * 3: Some motions aren't line mode motions, e.g. d`<char>,
+ * but always go into the numeric buffers, regardless. This
+ * was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
+ *
+ * Otherwise, put it in the unnamed buffer.
+ */
+ append = copy_one = copy_def = 0;
+ if (namep != NULL) {
+ name = *namep;
+ if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
+ (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
+ copy_one = 1;
+ cb_rotate(sp);
+ }
+ if ((append = isupper(name)) == 1) {
+ if (!copy_one)
+ copy_def = 1;
+ name = tolower(name);
+ }
+namecb: CBNAME(sp, cbp, name);
+ } else if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
+ (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
+ name = '1';
+ cb_rotate(sp);
+ goto namecb;
+ } else
+ cbp = &sp->gp->dcb_store;
+
+copyloop:
+ /*
+ * If this is a new buffer, create it and add it into the list.
+ * Otherwise, if it's not an append, free its current contents.
+ */
+ if (cbp == NULL) {
+ CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB));
+ cbp->name = name;
+ CIRCLEQ_INIT(&cbp->textq);
+ LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
+ } else if (!append) {
+ text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ }
+
+
+#define ENTIRE_LINE 0
+ /* In line mode, it's pretty easy, just cut the lines. */
+ if (LF_ISSET(CUT_LINEMODE)) {
+ cbp->flags |= CB_LMODE;
+ for (lno = fm->lno; lno <= tm->lno; ++lno)
+ if (cut_line(sp, lno, 0, 0, cbp))
+ goto cut_line_err;
+ } else {
+ /*
+ * Get the first line. A length of 0 causes cut_line
+ * to cut from the MARK to the end of the line.
+ */
+ if (cut_line(sp, fm->lno, fm->cno, fm->lno != tm->lno ?
+ ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp))
+ goto cut_line_err;
+
+ /* Get the intermediate lines. */
+ for (lno = fm->lno; ++lno < tm->lno;)
+ if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp))
+ goto cut_line_err;
+
+ /* Get the last line. */
+ if (tm->lno != fm->lno &&
+ cut_line(sp, lno, 0, tm->cno + 1, cbp))
+ goto cut_line_err;
+ }
+
+ append = 0; /* Only append to the named buffer. */
+ sp->gp->dcbp = cbp; /* Repoint the default buffer on each pass. */
+
+ if (copy_one) { /* Copy into numeric buffer 1. */
+ name = '1';
+ CBNAME(sp, cbp, name);
+ copy_one = 0;
+ goto copyloop;
+ }
+ if (copy_def) { /* Copy into the default buffer. */
+ cbp = &sp->gp->dcb_store;
+ copy_def = 0;
+ goto copyloop;
+ }
+ return (0);
+
+cut_line_err:
+ text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ return (1);
+}
+
+/*
+ * cb_rotate --
+ * Rotate the numbered buffers up one.
+ */
+static void
+cb_rotate(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);
+ }
+}
+
+/*
+ * cut_line --
+ * Cut a portion of a single line.
+ *
+ * PUBLIC: int cut_line __P((SCR *, recno_t, size_t, size_t, CB *));
+ */
+int
+cut_line(sp, lno, fcno, clen, cbp)
+ SCR *sp;
+ recno_t lno;
+ size_t fcno, clen;
+ CB *cbp;
+{
+ TEXT *tp;
+ size_t len;
+ char *p;
+
+ /* Get the line. */
+ if (db_get(sp, lno, DBG_FATAL, &p, &len))
+ return (1);
+
+ /* Create a TEXT structure that can hold the entire line. */
+ if ((tp = text_init(sp, NULL, 0, len)) == NULL)
+ return (1);
+
+ /*
+ * If the line isn't empty and it's not the entire line,
+ * copy the portion we want, and reset the TEXT length.
+ */
+ if (len != 0) {
+ if (clen == 0)
+ clen = len - fcno;
+ memcpy(tp->lb, p + fcno, clen);
+ tp->len = clen;
+ }
+
+ /* Append to the end of the cut buffer. */
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+
+ return (0);
+}
+
+/*
+ * cut_close --
+ * Discard all cut buffers.
+ *
+ * PUBLIC: void cut_close __P((GS *));
+ */
+void
+cut_close(gp)
+ GS *gp;
+{
+ CB *cbp;
+
+ /* Free cut buffer list. */
+ while ((cbp = gp->cutq.lh_first) != NULL) {
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ text_lfree(&cbp->textq);
+ LIST_REMOVE(cbp, q);
+ free(cbp);
+ }
+
+ /* Free default cut storage. */
+ cbp = &gp->dcb_store;
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ text_lfree(&cbp->textq);
+}
+
+/*
+ * text_init --
+ * Allocate a new TEXT structure.
+ *
+ * PUBLIC: TEXT *text_init __P((SCR *, const char *, size_t, size_t));
+ */
+TEXT *
+text_init(sp, p, len, total_len)
+ SCR *sp;
+ const char *p;
+ size_t len, total_len;
+{
+ TEXT *tp;
+
+ CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT));
+ if (tp == NULL)
+ return (NULL);
+ /* ANSI C doesn't define a call to malloc(3) for 0 bytes. */
+ if ((tp->lb_len = total_len) != 0) {
+ MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
+ if (tp->lb == NULL) {
+ free(tp);
+ return (NULL);
+ }
+ if (p != NULL && len != 0)
+ memcpy(tp->lb, p, len);
+ }
+ tp->len = len;
+ return (tp);
+}
+
+/*
+ * text_lfree --
+ * Free a chain of text structures.
+ *
+ * PUBLIC: void text_lfree __P((TEXTH *));
+ */
+void
+text_lfree(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.
+ *
+ * PUBLIC: void text_free __P((TEXT *));
+ */
+void
+text_free(tp)
+ TEXT *tp;
+{
+ if (tp->lb != NULL)
+ free(tp->lb);
+ free(tp);
+}
diff --git a/contrib/nvi/common/cut.h b/contrib/nvi/common/cut.h
new file mode 100644
index 000000000000..43f3ca817efd
--- /dev/null
+++ b/contrib/nvi/common/cut.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)cut.h 10.5 (Berkeley) 4/3/96
+ */
+
+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_int8_t flags;
+};
+
+/* Lines/blocks of text. */
+struct _text { /* Text: a linked list of lines. */
+ CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */
+ char *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: file line. */
+ size_t cno; /* 0-N: file character in line. */
+ size_t ai; /* 0-N: autoindent bytes. */
+ size_t insert; /* 0-N: bytes to insert (push). */
+ size_t offset; /* 0-N: initial, unerasable chars. */
+ size_t owrite; /* 0-N: chars to overwrite. */
+ size_t R_erase; /* 0-N: 'R' erase count. */
+ size_t sv_cno; /* 0-N: Saved line cursor. */
+ size_t sv_len; /* 0-N: Saved line length. */
+
+ /*
+ * These fields returns information from the vi text input routine.
+ *
+ * The termination condition. Note, this field is only valid if the
+ * text input routine returns success.
+ * TERM_BS: User backspaced over the prompt.
+ * TERM_CEDIT: User entered <edit-char>.
+ * TERM_CR: User entered <carriage-return>; no data.
+ * TERM_ESC: User entered <escape>; no data.
+ * TERM_OK: Data available.
+ * TERM_SEARCH: Incremental search.
+ */
+ enum {
+ TERM_BS, TERM_CEDIT, TERM_CR, TERM_ESC, TERM_OK, TERM_SEARCH
+ } term;
+};
+
+/*
+ * Get named buffer 'name'.
+ * Translate upper-case buffer names to lower-case buffer names.
+ */
+#define CBNAME(sp, cbp, nch) { \
+ CHAR_T L__name; \
+ L__name = isupper(nch) ? tolower(nch) : (nch); \
+ for (cbp = sp->gp->cutq.lh_first; \
+ cbp != NULL; cbp = cbp->q.le_next) \
+ if (cbp->name == L__name) \
+ break; \
+}
+
+/* Flags to the cut() routine. */
+#define CUT_LINEMODE 0x01 /* Cut in line mode. */
+#define CUT_NUMOPT 0x02 /* Numeric buffer: optional. */
+#define CUT_NUMREQ 0x04 /* Numeric buffer: required. */
diff --git a/contrib/nvi/common/delete.c b/contrib/nvi/common/delete.c
new file mode 100644
index 000000000000..001788f9bb38
--- /dev/null
+++ b/contrib/nvi/common/delete.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)delete.c 10.12 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * del --
+ * Delete a range of text.
+ *
+ * PUBLIC: int del __P((SCR *, MARK *, MARK *, int));
+ */
+int
+del(sp, fm, tm, lmode)
+ SCR *sp;
+ MARK *fm, *tm;
+ int lmode;
+{
+ recno_t lno;
+ size_t blen, len, nlen, tlen;
+ char *bp, *p;
+ int eof, rval;
+
+ bp = NULL;
+
+ /* Case 1 -- delete in line mode. */
+ if (lmode) {
+ for (lno = tm->lno; lno >= fm->lno; --lno) {
+ if (db_delete(sp, lno))
+ return (1);
+ ++sp->rptlines[L_DELETED];
+ if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
+ break;
+ }
+ goto done;
+ }
+
+ /*
+ * Case 2 -- delete to EOF. This is a special case because it's
+ * easier to pick it off than try and find it in the other cases.
+ */
+ if (db_last(sp, &lno))
+ return (1);
+ if (tm->lno >= lno) {
+ if (tm->lno == lno) {
+ if (db_get(sp, lno, DBG_FATAL, &p, &len))
+ return (1);
+ eof = tm->cno >= len ? 1 : 0;
+ } else
+ eof = 1;
+ if (eof) {
+ for (lno = tm->lno; lno > fm->lno; --lno) {
+ if (db_delete(sp, lno))
+ return (1);
+ ++sp->rptlines[L_DELETED];
+ if (lno %
+ INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
+ break;
+ }
+ if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
+ return (1);
+ GET_SPACE_RET(sp, bp, blen, fm->cno);
+ memcpy(bp, p, fm->cno);
+ if (db_set(sp, fm->lno, bp, fm->cno))
+ return (1);
+ goto done;
+ }
+ }
+
+ /* Case 3 -- delete within a single line. */
+ if (tm->lno == fm->lno) {
+ if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
+ return (1);
+ GET_SPACE_RET(sp, bp, blen, len);
+ if (fm->cno != 0)
+ memcpy(bp, p, fm->cno);
+ memcpy(bp + fm->cno, p + (tm->cno + 1), len - (tm->cno + 1));
+ if (db_set(sp, fm->lno,
+ bp, len - ((tm->cno - fm->cno) + 1)))
+ goto err;
+ goto done;
+ }
+
+ /*
+ * Case 4 -- delete over multiple lines.
+ *
+ * Copy the start partial line into place.
+ */
+ if ((tlen = fm->cno) != 0) {
+ if (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
+ return (1);
+ GET_SPACE_RET(sp, bp, blen, tlen + 256);
+ memcpy(bp, p, tlen);
+ }
+
+ /* Copy the end partial line into place. */
+ if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
+ goto err;
+ if (len != 0 && tm->cno != len - 1) {
+ /*
+ * XXX
+ * We can overflow memory here, if the total length is greater
+ * than SIZE_T_MAX. The only portable way I've found to test
+ * is depending on the overflow being less than the value.
+ */
+ nlen = (len - (tm->cno + 1)) + tlen;
+ if (tlen > nlen) {
+ msgq(sp, M_ERR, "002|Line length overflow");
+ goto err;
+ }
+ if (tlen == 0) {
+ GET_SPACE_RET(sp, bp, blen, nlen);
+ } else
+ ADD_SPACE_RET(sp, bp, blen, nlen);
+
+ memcpy(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
+ tlen += len - (tm->cno + 1);
+ }
+
+ /* Set the current line. */
+ if (db_set(sp, fm->lno, bp, tlen))
+ goto err;
+
+ /* Delete the last and intermediate lines. */
+ for (lno = tm->lno; lno > fm->lno; --lno) {
+ if (db_delete(sp, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
+ break;
+ }
+
+done: rval = 0;
+ if (0)
+err: rval = 1;
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/contrib/nvi/common/exf.c b/contrib/nvi/common/exf.c
new file mode 100644
index 000000000000..2993b0f4a8a5
--- /dev/null
+++ b/contrib/nvi/common/exf.c
@@ -0,0 +1,1498 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)exf.c 10.49 (Berkeley) 10/10/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+/*
+ * We include <sys/file.h>, because the flock(2) and open(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 <bitstring.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+static int file_backup __P((SCR *, char *, char *));
+static void file_cinit __P((SCR *));
+static void file_comment __P((SCR *));
+static int file_spath __P((SCR *, FREF *, struct stat *, int *));
+
+/*
+ * 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.
+ *
+ * PUBLIC: FREF *file_add __P((SCR *, CHAR_T *));
+ */
+FREF *
+file_add(sp, name)
+ SCR *sp;
+ CHAR_T *name;
+{
+ GS *gp;
+ FREF *frp, *tfrp;
+
+ /*
+ * Return it if it already exists. Note that we test against the
+ * user's name, whatever that happens to be, including if it's a
+ * temporary file.
+ *
+ * If the user added a file but was unable to initialize it, there
+ * can be file list entries where the name field is NULL. Discard
+ * them the next time we see them.
+ */
+ gp = sp->gp;
+ if (name != NULL)
+ for (frp = gp->frefq.cqh_first;
+ frp != (FREF *)&gp->frefq; frp = frp->q.cqe_next) {
+ if (frp->name == NULL) {
+ tfrp = frp->q.cqe_next;
+ CIRCLEQ_REMOVE(&gp->frefq, frp, q);
+ if (frp->name != NULL)
+ free(frp->name);
+ free(frp);
+ frp = tfrp;
+ continue;
+ }
+ if (!strcmp(frp->name, name))
+ 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.
+ */
+ if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
+ (frp->name = strdup(name)) == NULL) {
+ free(frp);
+ msgq(sp, M_SYSERR, NULL);
+ return (NULL);
+ }
+
+ /* Append into the chain of file names. */
+ CIRCLEQ_INSERT_TAIL(&gp->frefq, frp, q);
+
+ return (frp);
+}
+
+/*
+ * 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.
+ *
+ * PUBLIC: int file_init __P((SCR *, FREF *, char *, int));
+ */
+int
+file_init(sp, frp, rcv_name, flags)
+ SCR *sp;
+ FREF *frp;
+ char *rcv_name;
+ int flags;
+{
+ EXF *ep;
+ RECNOINFO oinfo;
+ struct stat sb;
+ size_t psize;
+ int fd, exists, open_err, readonly;
+ char *oname, tname[MAXPATHLEN];
+
+ open_err = readonly = 0;
+
+ /*
+ * If the file is a recovery file, let the recovery code handle it.
+ * Clear the FR_RECOVER flag first -- the recovery code does set up,
+ * and then calls us! If the recovery call fails, it's probably
+ * because the named file doesn't exist. So, move boldly forward,
+ * presuming that there's an error message the user will get to see.
+ */
+ if (F_ISSET(frp, FR_RECOVER)) {
+ F_CLR(frp, FR_RECOVER);
+ return (rcv_read(sp, frp));
+ }
+
+ /*
+ * Required FRP initialization; the only flag we keep is the
+ * cursor information.
+ */
+ F_CLR(frp, ~FR_CURSORSET);
+
+ /*
+ * Required EXF 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 = ep->fcntl_fd = -1;
+ F_SET(ep, F_FIRSTMODIFY);
+
+ /*
+ * Scan the user's path to find the file that we're going to
+ * try and open.
+ */
+ if (file_spath(sp, frp, &sb, &exists))
+ return (1);
+
+ /*
+ * If no name or backing file, for whatever reason, create a backing
+ * temporary file, saving the temp file name so we can later unlink
+ * it. If the user never named this file, copy the temporary file name
+ * to the real name (we display that until the user renames it).
+ */
+ oname = frp->name;
+ if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
+ if (opts_empty(sp, O_DIRECTORY, 0))
+ goto err;
+ (void)snprintf(tname, sizeof(tname),
+ "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
+ if ((fd = mkstemp(tname)) == -1) {
+ msgq(sp, M_SYSERR,
+ "237|Unable to create temporary file");
+ goto err;
+ }
+ (void)close(fd);
+
+ if (frp->name == NULL)
+ F_SET(frp, FR_TMPFILE);
+ if ((frp->tname = strdup(tname)) == NULL ||
+ frp->name == NULL && (frp->name = strdup(tname)) == NULL) {
+ if (frp->tname != NULL)
+ free(frp->tname);
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(tname);
+ goto err;
+ }
+ oname = frp->tname;
+ psize = 1024;
+ if (!LF_ISSET(FS_OPENERR))
+ F_SET(frp, FR_NEWFILE);
+
+ time(&ep->mtime);
+ } else {
+ /*
+ * XXX
+ * A seat of the pants calculation: try to keep the file in
+ * 15 pages or less. Don't use a page size larger than 10K
+ * (vi should have good locality) or smaller than 1K.
+ */
+ psize = ((sb.st_size / 15) + 1023) / 1024;
+ if (psize > 10)
+ psize = 10;
+ if (psize == 0)
+ psize = 1;
+ psize *= 1024;
+
+ F_SET(ep, F_DEVSET);
+ ep->mdev = sb.st_dev;
+ ep->minode = sb.st_ino;
+
+ ep->mtime = sb.st_mtime;
+
+ if (!S_ISREG(sb.st_mode))
+ msgq_str(sp, M_ERR, oname,
+ "238|Warning: %s is not a regular file");
+ }
+
+ /* 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, frp->name))
+ oinfo.bfname = ep->rcv_path;
+ } else {
+ if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ }
+ oinfo.bfname = ep->rcv_path;
+ F_SET(ep, F_MODIFIED);
+ }
+
+ /* Open a db structure. */
+ if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
+ O_NONBLOCK | O_RDONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
+ DB_RECNO, &oinfo)) == NULL) {
+ msgq_str(sp,
+ M_SYSERR, rcv_name == NULL ? oname : rcv_name, "%s");
+ /*
+ * !!!
+ * Historically, vi permitted users to edit files that couldn't
+ * be read. This isn't useful for single files from a command
+ * line, but it's quite useful for "vi *.c", since you can skip
+ * past files that you can't read.
+ */
+ open_err = 1;
+ goto oerr;
+ }
+
+ /*
+ * Do the remaining things that can cause failure of the new file,
+ * mark and logging initialization.
+ */
+ if (mark_init(sp, ep) || log_init(sp, ep))
+ goto err;
+
+ /*
+ * Set the alternate file name to be the file we're discarding.
+ *
+ * !!!
+ * Temporary files can't become alternate files, so there's no file
+ * name. This matches historical practice, although it could only
+ * happen in historical vi as the result of the initial command, i.e.
+ * if vi was executed without a file name.
+ */
+ if (LF_ISSET(FS_SETALT))
+ set_alt_name(sp, sp->frp == NULL ||
+ F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name);
+
+ /*
+ * Close the previous file; if that fails, close the new one and run
+ * for the border.
+ *
+ * !!!
+ * There's a nasty special case. If the user edits a temporary file,
+ * and then does an ":e! %", we need to re-initialize the backing
+ * file, but we can't change the name. (It's worse -- we're dealing
+ * with *names* here, we can't even detect that it happened.) Set a
+ * flag so that the file_end routine ignores the backing information
+ * of the old file if it happens to be the same as the new one.
+ *
+ * !!!
+ * Side-effect: after the call to file_end(), sp->frp may be NULL.
+ */
+ if (sp->ep != NULL) {
+ F_SET(frp, FR_DONTDELETE);
+ if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) {
+ (void)file_end(sp, ep, 1);
+ goto err;
+ }
+ F_CLR(frp, FR_DONTDELETE);
+ }
+
+ /*
+ * Lock the file; if it's a recovery file, it should already be
+ * locked. Note, we acquire the lock after the previous file
+ * has been ended, so that we don't get an "already locked" error
+ * for ":edit!".
+ *
+ * XXX
+ * While the user can't interrupt us between the open and here,
+ * there's a race between the dbopen() and the lock. Not much
+ * we can do about it.
+ *
+ * XXX
+ * We don't make a big deal of not being able to lock the file. As
+ * locking rarely works over NFS, and often fails if the file was
+ * mmap(2)'d, it's far too common to do anything like print an error
+ * message, let alone make the file readonly. At some future time,
+ * when locking is a little more reliable, this should change to be
+ * an error.
+ */
+ if (rcv_name == NULL)
+ switch (file_lock(sp, oname,
+ &ep->fcntl_fd, ep->db->fd(ep->db), 0)) {
+ case LOCK_FAILED:
+ F_SET(frp, FR_UNLOCKED);
+ break;
+ case LOCK_UNAVAIL:
+ readonly = 1;
+ msgq_str(sp, M_INFO, oname,
+ "239|%s already locked, session is read-only");
+ break;
+ case LOCK_SUCCESS:
+ break;
+ }
+
+ /*
+ * Historically, the readonly edit option was set per edit buffer in
+ * vi, unless the -R command-line option was specified or the program
+ * was executed as "view". (Well, to be truthful, if the letter 'w'
+ * occurred anywhere in the program name, but let's not get into that.)
+ * So, the persistant readonly state has to be stored in the screen
+ * structure, and the edit option value toggles with the contents of
+ * the edit buffer. If the persistant readonly flag is set, set the
+ * readonly edit option.
+ *
+ * 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 (readonly || F_ISSET(sp, SC_READONLY) ||
+ !F_ISSET(frp, FR_NEWFILE) &&
+ (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
+ access(frp->name, W_OK)))
+ O_SET(sp, O_READONLY);
+ else
+ O_CLR(sp, O_READONLY);
+
+ /* Switch... */
+ ++ep->refcnt;
+ sp->ep = ep;
+ sp->frp = frp;
+
+ /* Set the initial cursor position, queue initial command. */
+ file_cinit(sp);
+
+ /* Redraw the screen from scratch, schedule a welcome message. */
+ F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
+
+ return (0);
+
+err: if (frp->name != NULL) {
+ free(frp->name);
+ frp->name = NULL;
+ }
+ if (frp->tname != NULL) {
+ (void)unlink(frp->tname);
+ free(frp->tname);
+ frp->tname = NULL;
+ }
+
+oerr: if (F_ISSET(ep, F_RCV_ON))
+ (void)unlink(ep->rcv_path);
+ if (ep->rcv_path != NULL) {
+ free(ep->rcv_path);
+ ep->rcv_path = NULL;
+ }
+ if (ep->db != NULL)
+ (void)ep->db->close(ep->db);
+ free(ep);
+
+ return (open_err ?
+ file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1);
+}
+
+/*
+ * file_spath --
+ * Scan the user's path to find the file that we're going to
+ * try and open.
+ */
+static int
+file_spath(sp, frp, sbp, existsp)
+ SCR *sp;
+ FREF *frp;
+ struct stat *sbp;
+ int *existsp;
+{
+ CHAR_T savech;
+ size_t len;
+ int found;
+ char *name, *p, *t, path[MAXPATHLEN];
+
+ /*
+ * If the name is NULL or an explicit reference (i.e., the first
+ * component is . or ..) ignore the O_PATH option.
+ */
+ name = frp->name;
+ if (name == NULL) {
+ *existsp = 0;
+ return (0);
+ }
+ if (name[0] == '/' || name[0] == '.' &&
+ (name[1] == '/' || name[1] == '.' && name[2] == '/')) {
+ *existsp = !stat(name, sbp);
+ return (0);
+ }
+
+ /* Try . */
+ if (!stat(name, sbp)) {
+ *existsp = 1;
+ return (0);
+ }
+
+ /* Try the O_PATH option values. */
+ for (found = 0, p = t = O_STR(sp, O_PATH);; ++p)
+ if (*p == ':' || *p == '\0') {
+ if (t < p - 1) {
+ savech = *p;
+ *p = '\0';
+ len = snprintf(path,
+ sizeof(path), "%s/%s", t, name);
+ *p = savech;
+ if (!stat(path, sbp)) {
+ found = 1;
+ break;
+ }
+ }
+ t = p + 1;
+ if (*p == '\0')
+ break;
+ }
+
+ /* If we found it, build a new pathname and discard the old one. */
+ if (found) {
+ MALLOC_RET(sp, p, char *, len + 1);
+ memcpy(p, path, len + 1);
+ free(frp->name);
+ frp->name = p;
+ }
+ *existsp = found;
+ return (0);
+}
+
+/*
+ * file_cinit --
+ * Set up the initial cursor position.
+ */
+static void
+file_cinit(sp)
+ SCR *sp;
+{
+ GS *gp;
+ MARK m;
+ size_t len;
+ int nb;
+
+ /* Set some basic defaults. */
+ sp->lno = 1;
+ sp->cno = 0;
+
+ /*
+ * Historically, initial commands (the -c option) weren't executed
+ * until a file was loaded, e.g. "vi +10 nofile", followed by an
+ * :edit or :tag command, would execute the +10 on the file loaded
+ * by the subsequent command, (assuming that it existed). This
+ * applied as well to files loaded using the tag commands, and we
+ * follow that historic practice. Also, all initial commands were
+ * ex commands and were always executed on the last line of the file.
+ *
+ * Otherwise, if no initial command for this file:
+ * If in ex mode, move to the last line, first nonblank character.
+ * If the file has previously been edited, move to the last known
+ * position, and check it for validity.
+ * Otherwise, move to the first line, first nonblank.
+ *
+ * This gets called by the file init code, because we may be in a
+ * file of ex commands and we want to execute them from the right
+ * location in the file.
+ */
+ nb = 0;
+ gp = sp->gp;
+ if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) {
+ if (db_last(sp, &sp->lno))
+ return;
+ if (sp->lno == 0) {
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+ if (ex_run_str(sp,
+ "-c option", gp->c_option, strlen(gp->c_option), 1, 1))
+ return;
+ gp->c_option = NULL;
+ } else if (F_ISSET(sp, SC_EX)) {
+ if (db_last(sp, &sp->lno))
+ return;
+ if (sp->lno == 0) {
+ sp->lno = 1;
+ sp->cno = 0;
+ return;
+ }
+ nb = 1;
+ } else {
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ /* If returning to a file in vi, center the line. */
+ F_SET(sp, SC_SCR_CENTER);
+ } else {
+ if (O_ISSET(sp, O_COMMENT))
+ file_comment(sp);
+ else
+ sp->lno = 1;
+ nb = 1;
+ }
+ if (db_get(sp, sp->lno, 0, NULL, &len)) {
+ sp->lno = 1;
+ sp->cno = 0;
+ return;
+ }
+ if (!nb && sp->cno > len)
+ nb = 1;
+ }
+ if (nb) {
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ }
+
+ /*
+ * !!!
+ * The initial column is also the most attractive column.
+ */
+ sp->rcm = sp->cno;
+
+ /*
+ * !!!
+ * Historically, vi initialized the absolute mark, but ex did not.
+ * Which meant, that if the first command in ex mode was "visual",
+ * or if an ex command was executed first (e.g. vi +10 file) vi was
+ * entered without the mark being initialized. For consistency, if
+ * the file isn't empty, we initialize it for everyone, believing
+ * that it can't hurt, and is generally useful. Not initializing it
+ * if the file is empty is historic practice, although it has always
+ * been possible to set (and use) marks in empty vi files.
+ */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ (void)mark_set(sp, ABSMARK1, &m, 0);
+}
+
+/*
+ * file_end --
+ * Stop editing a file.
+ *
+ * PUBLIC: int file_end __P((SCR *, EXF *, int));
+ */
+int
+file_end(sp, ep, force)
+ SCR *sp;
+ EXF *ep;
+ int force;
+{
+ FREF *frp;
+
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ * (If argument ep is NULL, use sp->ep.)
+ *
+ * If multiply referenced, just decrement the count and return.
+ */
+ if (ep == NULL)
+ ep = sp->ep;
+ if (--ep->refcnt != 0)
+ return (0);
+
+ /*
+ *
+ * Clean up the FREF structure.
+ *
+ * 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);
+
+ /*
+ * We may no longer need the temporary backing file, so clean it
+ * up. We don't need the FREF structure either, if the file was
+ * never named, so lose it.
+ *
+ * !!!
+ * Re: FR_DONTDELETE, see the comment above in file_init().
+ */
+ if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
+ if (unlink(frp->tname))
+ msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove");
+ free(frp->tname);
+ frp->tname = NULL;
+ if (F_ISSET(frp, FR_TMPFILE)) {
+ CIRCLEQ_REMOVE(&sp->gp->frefq, frp, q);
+ if (frp->name != NULL)
+ free(frp->name);
+ free(frp);
+ }
+ sp->frp = NULL;
+ }
+
+ /*
+ * Clean up the EXF structure.
+ *
+ * Close the db structure.
+ */
+ if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
+ msgq_str(sp, M_SYSERR, frp->name, "241|%s: close");
+ ++ep->refcnt;
+ return (1);
+ }
+
+ /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
+
+ /* Stop logging. */
+ (void)log_end(sp, ep);
+
+ /* Free up any marks. */
+ (void)mark_end(sp, ep);
+
+ /*
+ * Delete recovery files, close the open descriptor, free recovery
+ * memory. See recover.c for a description of the protocol.
+ *
+ * XXX
+ * Unlink backup file first, we can detect that the recovery file
+ * doesn't reference anything when the user tries to recover it.
+ * There's a race, here, obviously, but it's fairly small.
+ */
+ if (!F_ISSET(ep, F_RCV_NORM)) {
+ if (ep->rcv_path != NULL && unlink(ep->rcv_path))
+ msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove");
+ if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
+ msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove");
+ }
+ if (ep->fcntl_fd != -1)
+ (void)close(ep->fcntl_fd);
+ 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);
+
+ free(ep);
+ 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.
+ *
+ * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int));
+ */
+int
+file_write(sp, fm, tm, name, flags)
+ SCR *sp;
+ MARK *fm, *tm;
+ char *name;
+ int flags;
+{
+ enum { NEWFILE, OLDFILE } mtype;
+ struct stat sb;
+ EXF *ep;
+ FILE *fp;
+ FREF *frp;
+ MARK from, to;
+ size_t len;
+ u_long nlno, nch;
+ int fd, nf, noname, oflags, rval;
+ char *p, *s, *t, buf[MAXPATHLEN + 64];
+ const char *msgstr;
+
+ ep = sp->ep;
+ frp = sp->frp;
+
+ /*
+ * Writing '%', or naming the current file explicitly, has the
+ * same semantics as writing without a name.
+ */
+ if (name == NULL || !strcmp(name, frp->name)) {
+ noname = 1;
+ name = frp->name;
+ } else
+ noname = 0;
+
+ /* Can't write files marked read-only, unless forced. */
+ if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) {
+ msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
+ "244|Read-only file, not written; use ! to override" :
+ "245|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 ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
+ !stat(name, &sb)) {
+ msgq_str(sp, M_ERR, name,
+ LF_ISSET(FS_POSSIBLE) ?
+ "246|%s exists, not written; use ! to override" :
+ "247|%s exists, not written");
+ 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) && noname && !stat(name, &sb)) {
+ msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
+ "248|Partial file, not written; use ! to override" :
+ "249|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.
+ *
+ * One final test. If we're not forcing or appending the current file,
+ * and we have a saved modification time, object if the file changed
+ * since we last edited or wrote it, and make them force it.
+ */
+ if (stat(name, &sb))
+ mtype = NEWFILE;
+ else {
+ if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) &&
+ (F_ISSET(ep, F_DEVSET) &&
+ (sb.st_dev != ep->mdev || sb.st_ino != ep->minode) ||
+ sb.st_mtime != ep->mtime)) {
+ msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ?
+"250|%s: file modified more recently than this copy; use ! to override" :
+"251|%s: file modified more recently than this copy");
+ return (1);
+ }
+
+ mtype = OLDFILE;
+ }
+
+ /* Set flags to create, write, and either append or truncate. */
+ oflags = O_CREAT | O_WRONLY |
+ (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC);
+
+ /* Backup the file if requested. */
+ if (!opts_empty(sp, O_BACKUP, 1) &&
+ file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE))
+ return (1);
+
+ /* Open the file. */
+ SIGBLOCK;
+ if ((fd = open(name, oflags,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) {
+ msgq_str(sp, M_SYSERR, name, "%s");
+ SIGUNBLOCK;
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* Try and get a lock. */
+ if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL)
+ msgq_str(sp, M_ERR, name,
+ "252|%s: write lock was unavailable");
+
+#if __linux__
+ /*
+ * XXX
+ * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set).
+ * This bug is fixed in libc 4.6.x.
+ *
+ * This code works around this problem for libc 4.5.x users.
+ * Note that this code is harmless if you're using libc 4.6.x.
+ */
+ if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) {
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+#endif
+
+ /*
+ * Use stdio for buffering.
+ *
+ * XXX
+ * SVR4.2 requires the fdopen mode exactly match the original open
+ * mode, i.e. you have to open with "a" if appending.
+ */
+ if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) {
+ msgq_str(sp, M_SYSERR, name, "%s");
+ (void)close(fd);
+ return (1);
+ }
+
+ /* Build fake addresses, if necessary. */
+ if (fm == NULL) {
+ from.lno = 1;
+ from.cno = 0;
+ fm = &from;
+ if (db_last(sp, &to.lno))
+ return (1);
+ to.cno = 0;
+ tm = &to;
+ }
+
+ rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0);
+
+ /*
+ * Save the new last modification time -- even if the write fails
+ * we re-init the time. That way the user can clean up the disk
+ * and rewrite without having to force it.
+ */
+ if (noname)
+ if (stat(name, &sb))
+ time(&ep->mtime);
+ else {
+ F_SET(ep, F_DEVSET);
+ ep->mdev = sb.st_dev;
+ ep->minode = sb.st_ino;
+
+ ep->mtime = sb.st_mtime;
+ }
+
+ /*
+ * If the write failed, complain loudly. ex_writefp() has already
+ * complained about the actual error, reinforce it if data was lost.
+ */
+ if (rval) {
+ if (!LF_ISSET(FS_APPEND))
+ msgq_str(sp, M_ERR, name,
+ "254|%s: WARNING: FILE TRUNCATED");
+ 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_CLR(frp, FR_NAMECHANGE);
+
+ /*
+ * If wrote the entire file, and it wasn't by appending it to a file,
+ * clear the modified bit. If the file was written to the original
+ * file name and the file is a temporary, set the "no exit" bit. This
+ * permits the user to write the file and use it in the context of the
+ * filesystem, but still keeps them from discarding their changes by
+ * exiting.
+ */
+ if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) {
+ F_CLR(ep, F_MODIFIED);
+ if (F_ISSET(frp, FR_TMPFILE))
+ if (noname)
+ F_SET(frp, FR_TMPEXIT);
+ else
+ F_CLR(frp, FR_TMPEXIT);
+ }
+
+ p = msg_print(sp, name, &nf);
+ switch (mtype) {
+ case NEWFILE:
+ msgstr = msg_cat(sp,
+ "256|%s: new file: %lu lines, %lu characters", NULL);
+ len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
+ break;
+ case OLDFILE:
+ msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ?
+ "315|%s: appended: %lu lines, %lu characters" :
+ "257|%s: %lu lines, %lu characters", NULL);
+ len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * There's a nasty problem with long path names. Cscope and tags files
+ * can result in long paths and vi will request a continuation key from
+ * the user. Unfortunately, the user has typed ahead, and chaos will
+ * result. If we assume that the characters in the filenames only take
+ * a single screen column each, we can trim the filename.
+ */
+ s = buf;
+ if (len >= sp->cols) {
+ for (s = buf, t = buf + strlen(p); s < t &&
+ (*s != '/' || len >= sp->cols - 3); ++s, --len);
+ if (s == t)
+ s = buf;
+ else {
+ *--s = '.'; /* Leading ellipses. */
+ *--s = '.';
+ *--s = '.';
+ }
+ }
+ msgq(sp, M_INFO, s);
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ return (0);
+}
+
+/*
+ * file_backup --
+ * Backup the about-to-be-written file.
+ *
+ * XXX
+ * We do the backup by copying the entire file. It would be nice to do
+ * a rename instead, but: (1) both files may not fit and we want to fail
+ * before doing the rename; (2) the backup file may not be on the same
+ * disk partition as the file being written; (3) there may be optional
+ * file information (MACs, DACs, whatever) that we won't get right if we
+ * recreate the file. So, let's not risk it.
+ */
+static int
+file_backup(sp, name, bname)
+ SCR *sp;
+ char *name, *bname;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ EXCMD cmd;
+ off_t off;
+ size_t blen;
+ int flags, maxnum, nr, num, nw, rfd, wfd, version;
+ char *bp, *estr, *p, *pct, *slash, *t, *wfname, buf[8192];
+
+ rfd = wfd = -1;
+ bp = estr = wfname = NULL;
+
+ /*
+ * Open the current file for reading. Do this first, so that
+ * we don't exec a shell before the most likely failure point.
+ * If it doesn't exist, it's okay, there's just nothing to back
+ * up.
+ */
+ errno = 0;
+ if ((rfd = open(name, O_RDONLY, 0)) < 0) {
+ if (errno == ENOENT)
+ return (0);
+ estr = name;
+ goto err;
+ }
+
+ /*
+ * If the name starts with an 'N' character, add a version number
+ * to the name. Strip the leading N from the string passed to the
+ * expansion routines, for no particular reason. It would be nice
+ * to permit users to put the version number anywhere in the backup
+ * name, but there isn't a special character that we can use in the
+ * name, and giving a new character a special meaning leads to ugly
+ * hacks both here and in the supporting ex routines.
+ *
+ * Shell and file name expand the option's value.
+ */
+ argv_init(sp, &cmd);
+ ex_cinit(&cmd, 0, 0, 0, 0, 0, NULL);
+ if (bname[0] == 'N') {
+ version = 1;
+ ++bname;
+ } else
+ version = 0;
+ if (argv_exp2(sp, &cmd, bname, strlen(bname)))
+ return (1);
+
+ /*
+ * 0 args: impossible.
+ * 1 args: use it.
+ * >1 args: object, too many args.
+ */
+ if (cmd.argc != 1) {
+ msgq_str(sp, M_ERR, bname,
+ "258|%s expanded into too many file names");
+ (void)close(rfd);
+ return (1);
+ }
+
+ /*
+ * If appending a version number, read through the directory, looking
+ * for file names that match the name followed by a number. Make all
+ * of the other % characters in name literal, so the user doesn't get
+ * surprised and sscanf doesn't drop core indirecting through pointers
+ * that don't exist. If any such files are found, increment its number
+ * by one.
+ */
+ if (version) {
+ GET_SPACE_GOTO(sp, bp, blen, cmd.argv[0]->len * 2 + 50);
+ for (t = bp, slash = NULL,
+ p = cmd.argv[0]->bp; p[0] != '\0'; *t++ = *p++)
+ if (p[0] == '%') {
+ if (p[1] != '%')
+ *t++ = '%';
+ } else if (p[0] == '/')
+ slash = t;
+ pct = t;
+ *t++ = '%';
+ *t++ = 'd';
+ *t = '\0';
+
+ if (slash == NULL) {
+ dirp = opendir(".");
+ p = bp;
+ } else {
+ *slash = '\0';
+ dirp = opendir(bp);
+ *slash = '/';
+ p = slash + 1;
+ }
+ if (dirp == NULL) {
+ estr = cmd.argv[0]->bp;
+ goto err;
+ }
+
+ for (maxnum = 0; (dp = readdir(dirp)) != NULL;)
+ if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum)
+ maxnum = num;
+ (void)closedir(dirp);
+
+ /* Format the backup file name. */
+ (void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1);
+ wfname = bp;
+ } else {
+ bp = NULL;
+ wfname = cmd.argv[0]->bp;
+ }
+
+ /* Open the backup file, avoiding lurkers. */
+ if (stat(wfname, &sb) == 0) {
+ if (!S_ISREG(sb.st_mode)) {
+ msgq_str(sp, M_ERR, bname,
+ "259|%s: not a regular file");
+ goto err;
+ }
+ if (sb.st_uid != getuid()) {
+ msgq_str(sp, M_ERR, bname, "260|%s: not owned by you");
+ goto err;
+ }
+ if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
+ msgq_str(sp, M_ERR, bname,
+ "261|%s: accessible by a user other than the owner");
+ goto err;
+ }
+ flags = O_TRUNC;
+ } else
+ flags = O_CREAT | O_EXCL;
+ if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
+ estr = bname;
+ goto err;
+ }
+
+ /* Copy the file's current contents to its backup value. */
+ while ((nr = read(rfd, buf, sizeof(buf))) > 0)
+ for (off = 0; nr != 0; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, nr)) < 0) {
+ estr = wfname;
+ goto err;
+ }
+ if (nr < 0) {
+ estr = name;
+ goto err;
+ }
+
+ if (close(rfd)) {
+ estr = name;
+ goto err;
+ }
+ if (close(wfd)) {
+ estr = wfname;
+ goto err;
+ }
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (0);
+
+alloc_err:
+err: if (rfd != -1)
+ (void)close(rfd);
+ if (wfd != -1) {
+ (void)unlink(wfname);
+ (void)close(wfd);
+ }
+ if (estr)
+ msgq_str(sp, M_SYSERR, estr, "%s");
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+}
+
+/*
+ * file_comment --
+ * Skip the first comment.
+ */
+static void
+file_comment(sp)
+ SCR *sp;
+{
+ recno_t lno;
+ size_t len;
+ char *p;
+
+ for (lno = 1; !db_get(sp, lno, 0, &p, &len) && len == 0; ++lno);
+ if (p == NULL)
+ return;
+ if (p[0] == '#') {
+ F_SET(sp, SC_SCR_TOP);
+ while (!db_get(sp, ++lno, 0, &p, &len))
+ if (len < 1 || p[0] != '#') {
+ sp->lno = lno;
+ return;
+ }
+ } else if (len > 1 && p[0] == '/' && p[1] == '*') {
+ F_SET(sp, SC_SCR_TOP);
+ do {
+ for (; len > 1; --len, ++p)
+ if (p[0] == '*' && p[1] == '/') {
+ sp->lno = lno;
+ return;
+ }
+ } while (!db_get(sp, ++lno, 0, &p, &len));
+ } else if (len > 1 && p[0] == '/' && p[1] == '/') {
+ F_SET(sp, SC_SCR_TOP);
+ p += 2;
+ len -= 2;
+ do {
+ for (; len > 1; --len, ++p)
+ if (p[0] == '/' && p[1] == '/') {
+ sp->lno = lno;
+ return;
+ }
+ } while (!db_get(sp, ++lno, 0, &p, &len));
+ }
+}
+
+/*
+ * file_m1 --
+ * First modification check routine. The :next, :prev, :rewind, :tag,
+ * :tagpush, :tagpop, ^^ modifications check.
+ *
+ * PUBLIC: int file_m1 __P((SCR *, int, int));
+ */
+int
+file_m1(sp, force, flags)
+ SCR *sp;
+ int force, flags;
+{
+ EXF *ep;
+
+ ep = sp->ep;
+
+ /* If no file loaded, return no modifications. */
+ if (ep == NULL)
+ return (0);
+
+ /*
+ * If the file has been modified, we'll want to write it back or
+ * fail. If autowrite is set, we'll write it back automatically,
+ * unless force is also set. Otherwise, we fail unless forced or
+ * there's another open screen on this file.
+ */
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (!force && file_aw(sp, flags))
+ return (1);
+ } else if (ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
+"262|File modified since last complete write; write or use ! to override" :
+"263|File modified since last complete write; write or use :edit! to override");
+ return (1);
+ }
+
+ return (file_m3(sp, force));
+}
+
+/*
+ * file_m2 --
+ * Second modification check routine. The :edit, :quit, :recover
+ * modifications check.
+ *
+ * PUBLIC: int file_m2 __P((SCR *, int));
+ */
+int
+file_m2(sp, force)
+ SCR *sp;
+ int force;
+{
+ EXF *ep;
+
+ ep = sp->ep;
+
+ /* If no file loaded, return no modifications. */
+ if (ep == NULL)
+ return (0);
+
+ /*
+ * If the file has been modified, we'll want to fail, unless forced
+ * or there's another open screen on this file.
+ */
+ if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+"264|File modified since last complete write; write or use ! to override");
+ return (1);
+ }
+
+ return (file_m3(sp, force));
+}
+
+/*
+ * file_m3 --
+ * Third modification check routine.
+ *
+ * PUBLIC: int file_m3 __P((SCR *, int));
+ */
+int
+file_m3(sp, force)
+ SCR *sp;
+ int force;
+{
+ EXF *ep;
+
+ ep = sp->ep;
+
+ /* If no file loaded, return no modifications. */
+ if (ep == NULL)
+ return (0);
+
+ /*
+ * Don't exit while in a temporary files if the file was ever modified.
+ * The problem is that if the user does a ":wq", we write and quit,
+ * unlinking the temporary file. Not what the user had in mind at all.
+ * We permit writing to temporary files, so that user maps using file
+ * system names work with temporary files.
+ */
+ if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+ "265|File is a temporary; exit will discard modifications");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * file_aw --
+ * Autowrite routine. If modified, autowrite is set and the readonly bit
+ * is not set, write the file. A routine so there's a place to put the
+ * comment.
+ *
+ * PUBLIC: int file_aw __P((SCR *, int));
+ */
+int
+file_aw(sp, flags)
+ SCR *sp;
+ int flags;
+{
+ if (!F_ISSET(sp->ep, F_MODIFIED))
+ return (0);
+ if (!O_ISSET(sp, O_AUTOWRITE))
+ return (0);
+
+ /*
+ * !!!
+ * Historic 4BSD vi attempted to write the file if autowrite was set,
+ * regardless of the writeability of the file (as defined by the file
+ * readonly flag). System V changed this as some point, not attempting
+ * autowrite if the file was readonly. This feels like a bug fix to
+ * me (e.g. the principle of least surprise is violated if readonly is
+ * set and vi writes the file), so I'm compatible with System V.
+ */
+ if (O_ISSET(sp, O_READONLY)) {
+ msgq(sp, M_INFO,
+ "266|File readonly, modifications not auto-written");
+ return (1);
+ }
+ return (file_write(sp, NULL, NULL, NULL, flags));
+}
+
+/*
+ * set_alt_name --
+ * Set the alternate pathname.
+ *
+ * Set the alternate pathname. It's a routine because I wanted some place
+ * to hang this comment. The alternate pathname (normally referenced using
+ * the special character '#' during file expansion and in the vi ^^ command)
+ * is set by almost all ex commands that take file names as arguments. The
+ * rules go something like this:
+ *
+ * 1: If any ex command takes a file name as an argument (except for the
+ * :next command), the alternate pathname is set to that file name.
+ * This excludes the command ":e" and ":w !command" as no file name
+ * was specified. Note, historically, the :source command did not set
+ * the alternate pathname. It does in nvi, for consistency.
+ *
+ * 2: However, if any ex command sets the current pathname, e.g. the
+ * ":e file" or ":rew" commands succeed, then the alternate pathname
+ * is set to the previous file's current pathname, if it had one.
+ * This includes the ":file" command and excludes the ":e" command.
+ * So, by rule #1 and rule #2, if ":edit foo" fails, the alternate
+ * pathname will be "foo", if it succeeds, the alternate pathname will
+ * be the previous current pathname. The ":e" command will not set
+ * the alternate or current pathnames regardless.
+ *
+ * 3: However, if it's a read or write command with a file argument and
+ * the current pathname has not yet been set, the file name becomes
+ * the current pathname, and the alternate pathname is unchanged.
+ *
+ * If the user edits a temporary file, there may be times when there is no
+ * alternative file name. A name argument of NULL turns it off.
+ *
+ * PUBLIC: void set_alt_name __P((SCR *, char *));
+ */
+void
+set_alt_name(sp, name)
+ SCR *sp;
+ char *name;
+{
+ if (sp->alt_name != NULL)
+ free(sp->alt_name);
+ if (name == NULL)
+ sp->alt_name = NULL;
+ else if ((sp->alt_name = strdup(name)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+}
+
+/*
+ * file_lock --
+ * Get an exclusive lock on a file.
+ *
+ * XXX
+ * The default 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.
+ *
+ * Furthermore, the semantics of fcntl(2) are wrong. The problems are
+ * two-fold: you can't close any file descriptor associated with the file
+ * without losing all of the locks, and you can't get an exclusive lock
+ * unless you have the file open for writing. Someone ought to be shot,
+ * but it's probably too late, they may already have reproduced. To get
+ * around these problems, nvi opens the files for writing when it can and
+ * acquires a second file descriptor when it can't. The recovery files
+ * are examples of the former, they're always opened for writing. The DB
+ * files can't be opened for writing because the semantics of DB are that
+ * files opened for writing are flushed back to disk when the DB session
+ * is ended. So, in that case we have to acquire an extra file descriptor.
+ *
+ * PUBLIC: lockr_t file_lock __P((SCR *, char *, int *, int, int));
+ */
+lockr_t
+file_lock(sp, name, fdp, fd, iswrite)
+ SCR *sp;
+ char *name;
+ int *fdp, fd, iswrite;
+{
+ if (!O_ISSET(sp, O_LOCKFILES))
+ return (LOCK_SUCCESS);
+
+#ifdef HAVE_LOCK_FLOCK /* Hurrah! We've got flock(2). */
+ /*
+ * !!!
+ * We need to distinguish a lock not being available for the file
+ * from the file system not supporting locking. Flock is documented
+ * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume
+ * they are the former. There's no portable way to do this.
+ */
+ errno = 0;
+ return (flock(fd, LOCK_EX | LOCK_NB) ? errno == EAGAIN
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ ? LOCK_UNAVAIL : LOCK_FAILED : LOCK_SUCCESS);
+#endif
+#ifdef HAVE_LOCK_FCNTL /* Gag me. We've got fcntl(2). */
+{
+ struct flock arg;
+ int didopen, sverrno;
+
+ arg.l_type = F_WRLCK;
+ arg.l_whence = 0; /* SEEK_SET */
+ arg.l_start = arg.l_len = 0;
+ arg.l_pid = 0;
+
+ /*
+ * If the file descriptor isn't opened for writing, it must fail.
+ * If we fail because we can't get a read/write file descriptor,
+ * we return LOCK_SUCCESS, believing that the file is readonly
+ * and that will be sufficient to warn the user.
+ */
+ if (!iswrite) {
+ if (name == NULL || fdp == NULL)
+ return (LOCK_FAILED);
+ if ((fd = open(name, O_RDWR, 0)) == -1)
+ return (LOCK_SUCCESS);
+ *fdp = fd;
+ didopen = 1;
+ }
+
+ errno = 0;
+ if (!fcntl(fd, F_SETLK, &arg))
+ return (LOCK_SUCCESS);
+ if (didopen) {
+ sverrno = errno;
+ (void)close(fd);
+ errno = sverrno;
+ }
+
+ /*
+ * !!!
+ * We need to distinguish a lock not being available for the file
+ * from the file system not supporting locking. Fcntl is documented
+ * as returning EACCESS and EAGAIN; add EWOULDBLOCK for good measure,
+ * and assume they are the former. There's no portable way to do this.
+ */
+ return (errno == EACCES || errno == EAGAIN
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ ? LOCK_UNAVAIL : LOCK_FAILED);
+}
+#endif
+#if !defined(HAVE_LOCK_FLOCK) && !defined(HAVE_LOCK_FCNTL)
+ return (LOCK_SUCCESS);
+#endif
+}
diff --git a/contrib/nvi/common/exf.h b/contrib/nvi/common/exf.h
new file mode 100644
index 000000000000..cdfaa8294485
--- /dev/null
+++ b/contrib/nvi/common/exf.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)exf.h 10.7 (Berkeley) 7/9/96
+ */
+ /* 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. */
+ dir_t lundo; /* Last undo direction. */
+
+ LIST_HEAD(_markh, _lmark) marks;/* Linked list of file MARK's. */
+
+ /*
+ * XXX
+ * Mtime should be a struct timespec, but time_t is more portable.
+ */
+ dev_t mdev; /* Device. */
+ ino_t minode; /* Inode. */
+ time_t mtime; /* Last modification time. */
+
+ int fcntl_fd; /* Fcntl locking fd; see exf.c. */
+
+ /*
+ * Recovery in general, and these fields specifically, are described
+ * in recover.c.
+ */
+#define RCV_PERIOD 120 /* Sync every two minutes. */
+ char *rcv_path; /* Recover file name. */
+ char *rcv_mpath; /* Recover mail file name. */
+ int rcv_fd; /* Locked mail file descriptor. */
+
+#define F_DEVSET 0x001 /* mdev/minode fields initialized. */
+#define F_FIRSTMODIFY 0x002 /* File not yet modified. */
+#define F_MODIFIED 0x004 /* File is currently dirty. */
+#define F_MULTILOCK 0x008 /* Multiple processes running, lock. */
+#define F_NOLOG 0x010 /* Logging turned off. */
+#define F_RCV_NORM 0x020 /* Don't delete recovery files. */
+#define F_RCV_ON 0x040 /* Recovery is possible. */
+#define F_UNDO 0x080 /* No change since last undo. */
+ u_int8_t flags;
+};
+
+/* Flags to db_get(). */
+#define DBG_FATAL 0x001 /* If DNE, error message. */
+#define DBG_NOCACHE 0x002 /* Ignore the front-end cache. */
+
+/* Flags to file_init() and file_write(). */
+#define FS_ALL 0x001 /* Write the entire file. */
+#define FS_APPEND 0x002 /* Append to the file. */
+#define FS_FORCE 0x004 /* Force is set. */
+#define FS_OPENERR 0x008 /* Open failed, try it again. */
+#define FS_POSSIBLE 0x010 /* Force could have been set. */
+#define FS_SETALT 0x020 /* Set alternate file name. */
+
+/* Flags to rcv_sync(). */
+#define RCV_EMAIL 0x01 /* Send the user email, IFF file modified. */
+#define RCV_ENDSESSION 0x02 /* End the file session. */
+#define RCV_PRESERVE 0x04 /* Preserve backup file, IFF file modified. */
+#define RCV_SNAPSHOT 0x08 /* Snapshot the recovery, and send email. */
diff --git a/contrib/nvi/common/gs.h b/contrib/nvi/common/gs.h
new file mode 100644
index 000000000000..e5a43a656ac2
--- /dev/null
+++ b/contrib/nvi/common/gs.h
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)gs.h 10.34 (Berkeley) 9/24/96
+ */
+
+#define TEMPORARY_FILE_STRING "/tmp" /* Default temporary file name. */
+
+/*
+ * File reference structure (FREF). The structure contains the name of the
+ * file, along with the information that follows the name.
+ *
+ * !!!
+ * The read-only bit follows the file name, not the file itself.
+ */
+struct _fref {
+ CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */
+ char *name; /* File name. */
+ char *tname; /* Backing temporary file name. */
+
+ recno_t lno; /* 1-N: file cursor line. */
+ size_t cno; /* 0-N: file cursor column. */
+
+#define FR_CURSORSET 0x0001 /* If lno/cno values valid. */
+#define FR_DONTDELETE 0x0002 /* Don't delete the temporary file. */
+#define FR_EXNAMED 0x0004 /* Read/write renamed the file. */
+#define FR_NAMECHANGE 0x0008 /* If the name changed. */
+#define FR_NEWFILE 0x0010 /* File doesn't really exist yet. */
+#define FR_RECOVER 0x0020 /* File is being recovered. */
+#define FR_TMPEXIT 0x0040 /* Modified temporary file, no exit. */
+#define FR_TMPFILE 0x0080 /* If file has no name. */
+#define FR_UNLOCKED 0x0100 /* File couldn't be locked. */
+ u_int16_t flags;
+};
+
+/* Action arguments to scr_exadjust(). */
+typedef enum { EX_TERM_CE, EX_TERM_SCROLL } exadj_t;
+
+/* Screen attribute arguments to scr_attr(). */
+typedef enum { SA_ALTERNATE, SA_INVERSE } scr_attr_t;
+
+/* Key type arguments to scr_keyval(). */
+typedef enum { KEY_VEOF, KEY_VERASE, KEY_VKILL, KEY_VWERASE } scr_keyval_t;
+
+/*
+ * GS:
+ *
+ * Structure that describes global state of the running program.
+ */
+struct _gs {
+ char *progname; /* Programe name. */
+
+ int id; /* Last allocated screen id. */
+ CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */
+ CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */
+
+ SCR *ccl_sp; /* Colon command-line screen. */
+
+ void *perl_interp; /* Perl interpreter. */
+ void *tcl_interp; /* Tcl_Interp *: Tcl interpreter. */
+
+ void *cl_private; /* Curses support private area. */
+ void *ip_private; /* IP support private area. */
+ void *tk_private; /* Tk/Tcl support private area. */
+
+ /* File references. */
+ CIRCLEQ_HEAD(_frefh, _fref) frefq;
+
+#define GO_COLUMNS 0 /* Global options: columns. */
+#define GO_LINES 1 /* Global options: lines. */
+#define GO_SECURE 2 /* Global options: secure. */
+#define GO_TERM 3 /* Global options: terminal type. */
+ OPTION opts[GO_TERM + 1];
+
+ DB *msg; /* Message catalog DB. */
+ MSGH msgq; /* User message list. */
+#define DEFAULT_NOPRINT '\1' /* Emergency non-printable character. */
+ CHAR_T noprint; /* Cached, unprintable character. */
+
+ char *tmp_bp; /* Temporary buffer. */
+ size_t tmp_blen; /* Temporary buffer size. */
+
+ /*
+ * Ex command structures (EXCMD). Defined here because ex commands
+ * exist outside of any particular screen or file.
+ */
+#define EXCMD_RUNNING(gp) ((gp)->ecq.lh_first->clen != 0)
+ LIST_HEAD(_excmdh, _excmd) ecq; /* Ex command linked list. */
+ EXCMD excmd; /* Default ex command structure. */
+ char *if_name; /* Current associated file. */
+ recno_t if_lno; /* Current associated line number. */
+
+ char *c_option; /* Ex initial, command-line command. */
+
+#ifdef DEBUG
+ FILE *tracefp; /* Trace file pointer. */
+#endif
+
+ EVENT *i_event; /* Array of input events. */
+ size_t i_nelem; /* Number of array elements. */
+ size_t i_cnt; /* Count of events. */
+ size_t i_next; /* Offset of next event. */
+
+ CB *dcbp; /* Default cut buffer pointer. */
+ CB dcb_store; /* Default cut buffer storage. */
+ LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */
+
+#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 MAX_FAST_KEY 254 /* Max fast check character.*/
+#define KEY_LEN(sp, ch) \
+ ((unsigned char)(ch) <= MAX_FAST_KEY ? \
+ sp->gp->cname[(unsigned char)ch].len : v_key_len(sp, ch))
+#define KEY_NAME(sp, ch) \
+ ((unsigned char)(ch) <= MAX_FAST_KEY ? \
+ sp->gp->cname[(unsigned char)ch].name : v_key_name(sp, ch))
+ struct {
+ CHAR_T name[MAX_CHARACTER_COLUMNS + 1];
+ u_int8_t len;
+ } cname[MAX_FAST_KEY + 1]; /* Fast lookup table. */
+
+#define KEY_VAL(sp, ch) \
+ ((unsigned char)(ch) <= MAX_FAST_KEY ? \
+ sp->gp->special_key[(unsigned char)ch] : \
+ (unsigned char)(ch) > sp->gp->max_special ? 0 : v_key_val(sp,ch))
+ CHAR_T max_special; /* Max special character. */
+ u_char /* Fast lookup table. */
+ special_key[MAX_FAST_KEY + 1];
+
+/* Flags. */
+#define G_ABBREV 0x0001 /* If have abbreviations. */
+#define G_BELLSCHED 0x0002 /* Bell scheduled. */
+#define G_INTERRUPTED 0x0004 /* Interrupted. */
+#define G_RECOVER_SET 0x0008 /* Recover system initialized. */
+#define G_SCRIPTED 0x0010 /* Ex script session. */
+#define G_SCRWIN 0x0020 /* Scripting windows running. */
+#define G_SNAPSHOT 0x0040 /* Always snapshot files. */
+#define G_SRESTART 0x0080 /* Screen restarted. */
+#define G_TMP_INUSE 0x0100 /* Temporary buffer in use. */
+ u_int32_t flags;
+
+ /* Screen interface functions. */
+ /* Add a string to the screen. */
+ int (*scr_addstr) __P((SCR *, const char *, size_t));
+ /* Toggle a screen attribute. */
+ int (*scr_attr) __P((SCR *, scr_attr_t, int));
+ /* Terminal baud rate. */
+ int (*scr_baud) __P((SCR *, u_long *));
+ /* Beep/bell/flash the terminal. */
+ int (*scr_bell) __P((SCR *));
+ /* Display a busy message. */
+ void (*scr_busy) __P((SCR *, const char *, busy_t));
+ /* Clear to the end of the line. */
+ int (*scr_clrtoeol) __P((SCR *));
+ /* Return the cursor location. */
+ int (*scr_cursor) __P((SCR *, size_t *, size_t *));
+ /* Delete a line. */
+ int (*scr_deleteln) __P((SCR *));
+ /* Get a keyboard event. */
+ int (*scr_event) __P((SCR *, EVENT *, u_int32_t, int));
+ /* Ex: screen adjustment routine. */
+ int (*scr_ex_adjust) __P((SCR *, exadj_t));
+ int (*scr_fmap) /* Set a function key. */
+ __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+ /* Get terminal key value. */
+ int (*scr_keyval) __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+ /* Insert a line. */
+ int (*scr_insertln) __P((SCR *));
+ /* Handle an option change. */
+ int (*scr_optchange) __P((SCR *, int, char *, u_long *));
+ /* Move the cursor. */
+ int (*scr_move) __P((SCR *, size_t, size_t));
+ /* Message or ex output. */
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ /* Refresh the screen. */
+ int (*scr_refresh) __P((SCR *, int));
+ /* Rename the file. */
+ int (*scr_rename) __P((SCR *, char *, int));
+ /* Set the screen type. */
+ int (*scr_screen) __P((SCR *, u_int32_t));
+ /* Suspend the editor. */
+ int (*scr_suspend) __P((SCR *, int *));
+ /* Print usage message. */
+ void (*scr_usage) __P((void));
+};
+
+/*
+ * XXX
+ * Block signals if there are asynchronous events. Used to keep DB system calls
+ * from being interrupted and not restarted, as that will result in consistency
+ * problems. This should be handled by DB.
+ */
+#ifdef BLOCK_SIGNALS
+#include <signal.h>
+extern sigset_t __sigblockset;
+#define SIGBLOCK \
+ (void)sigprocmask(SIG_BLOCK, &__sigblockset, NULL)
+#define SIGUNBLOCK \
+ (void)sigprocmask(SIG_UNBLOCK, &__sigblockset, NULL);
+#else
+#define SIGBLOCK
+#define SIGUNBLOCK
+#endif
diff --git a/contrib/nvi/common/key.c b/contrib/nvi/common/key.c
new file mode 100644
index 000000000000..e1311ab571b0
--- /dev/null
+++ b/contrib/nvi/common/key.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)key.c 10.33 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../vi/vi.h"
+
+static int v_event_append __P((SCR *, EVENT *));
+static int v_event_grow __P((SCR *, int));
+static int v_key_cmp __P((const void *, const void *));
+static void v_keyval __P((SCR *, int, scr_keyval_t));
+static void v_sync __P((SCR *, int));
+
+/*
+ * !!!
+ * Historic vi always used:
+ *
+ * ^D: autoindent deletion
+ * ^H: last character deletion
+ * ^W: last word deletion
+ * ^Q: quote the next character (if not used in flow control).
+ * ^V: quote the next character
+ *
+ * regardless of the user's choices for these characters. The user's erase
+ * and kill characters worked in addition to these characters. Nvi wires
+ * down the above characters, but in addition permits the VEOF, VERASE, VKILL
+ * and VWERASE characters described by the user's termios structure.
+ *
+ * Ex was not consistent with this scheme, as it historically ran in tty
+ * cooked mode. This meant that the scroll command and autoindent erase
+ * characters were mapped to the user's EOF character, and the character
+ * and word deletion characters were the user's tty character and word
+ * deletion characters. This implementation makes it all consistent, as
+ * described above for vi.
+ *
+ * !!!
+ * This means that all screens share a special key set.
+ */
+KEYLIST keylist[] = {
+ {K_BACKSLASH, '\\'}, /* \ */
+ {K_CARAT, '^'}, /* ^ */
+ {K_CNTRLD, '\004'}, /* ^D */
+ {K_CNTRLR, '\022'}, /* ^R */
+ {K_CNTRLT, '\024'}, /* ^T */
+ {K_CNTRLZ, '\032'}, /* ^Z */
+ {K_COLON, ':'}, /* : */
+ {K_CR, '\r'}, /* \r */
+ {K_ESCAPE, '\033'}, /* ^[ */
+ {K_FORMFEED, '\f'}, /* \f */
+ {K_HEXCHAR, '\030'}, /* ^X */
+ {K_NL, '\n'}, /* \n */
+ {K_RIGHTBRACE, '}'}, /* } */
+ {K_RIGHTPAREN, ')'}, /* ) */
+ {K_TAB, '\t'}, /* \t */
+ {K_VERASE, '\b'}, /* \b */
+ {K_VKILL, '\025'}, /* ^U */
+ {K_VLNEXT, '\021'}, /* ^Q */
+ {K_VLNEXT, '\026'}, /* ^V */
+ {K_VWERASE, '\027'}, /* ^W */
+ {K_ZERO, '0'}, /* 0 */
+
+#define ADDITIONAL_CHARACTERS 4
+ {K_NOTUSED, 0}, /* VEOF, VERASE, VKILL, VWERASE */
+ {K_NOTUSED, 0},
+ {K_NOTUSED, 0},
+ {K_NOTUSED, 0},
+};
+static int nkeylist =
+ (sizeof(keylist) / sizeof(keylist[0])) - ADDITIONAL_CHARACTERS;
+
+/*
+ * v_key_init --
+ * Initialize the special key lookup table.
+ *
+ * PUBLIC: int v_key_init __P((SCR *));
+ */
+int
+v_key_init(sp)
+ SCR *sp;
+{
+ CHAR_T ch;
+ GS *gp;
+ KEYLIST *kp;
+ int cnt;
+
+ gp = sp->gp;
+
+ /*
+ * XXX
+ * 8-bit only, for now. Recompilation should get you any 8-bit
+ * character set, as long as nul isn't a character.
+ */
+ (void)setlocale(LC_ALL, "");
+#if __linux__
+ /*
+ * In libc 4.5.26, setlocale(LC_ALL, ""), doesn't setup the table
+ * for ctype(3c) correctly. This bug is fixed in libc 4.6.x.
+ *
+ * This code works around this problem for libc 4.5.x users.
+ * Note that this code is harmless if you're using libc 4.6.x.
+ */
+ (void)setlocale(LC_CTYPE, "");
+#endif
+ v_key_ilookup(sp);
+
+ v_keyval(sp, K_CNTRLD, KEY_VEOF);
+ v_keyval(sp, K_VERASE, KEY_VERASE);
+ v_keyval(sp, K_VKILL, KEY_VKILL);
+ v_keyval(sp, K_VWERASE, KEY_VWERASE);
+
+ /* Sort the special key list. */
+ qsort(keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
+
+ /* Initialize the fast lookup table. */
+ for (gp->max_special = 0, kp = keylist, cnt = nkeylist; 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;
+ }
+
+ /* Find a non-printable character to use as a message separator. */
+ for (ch = 1; ch <= MAX_CHAR_T; ++ch)
+ if (!isprint(ch)) {
+ gp->noprint = ch;
+ break;
+ }
+ if (ch != gp->noprint) {
+ msgq(sp, M_ERR, "079|No non-printable character found");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * v_keyval --
+ * Set key values.
+ *
+ * We've left some open slots in the keylist table, and if these values exist,
+ * we put them into place. Note, they may reset (or duplicate) values already
+ * in the table, so we check for that first.
+ */
+static void
+v_keyval(sp, val, name)
+ SCR *sp;
+ int val;
+ scr_keyval_t name;
+{
+ KEYLIST *kp;
+ CHAR_T ch;
+ int dne;
+
+ /* Get the key's value from the screen. */
+ if (sp->gp->scr_keyval(sp, name, &ch, &dne))
+ return;
+ if (dne)
+ return;
+
+ /* Check for duplication. */
+ for (kp = keylist; kp->value != K_NOTUSED; ++kp)
+ if (kp->ch == ch) {
+ kp->value = val;
+ return;
+ }
+
+ /* Add a new entry. */
+ if (kp->value == K_NOTUSED) {
+ keylist[nkeylist].ch = ch;
+ keylist[nkeylist].value = val;
+ ++nkeylist;
+ }
+}
+
+/*
+ * v_key_ilookup --
+ * Build the fast-lookup key display array.
+ *
+ * PUBLIC: void v_key_ilookup __P((SCR *));
+ */
+void
+v_key_ilookup(sp)
+ SCR *sp;
+{
+ CHAR_T ch, *p, *t;
+ GS *gp;
+ size_t len;
+
+ for (gp = sp->gp, ch = 0; ch <= MAX_FAST_KEY; ++ch)
+ for (p = gp->cname[ch].name, t = v_key_name(sp, ch),
+ len = gp->cname[ch].len = sp->clen; len--;)
+ *p++ = *t++;
+}
+
+/*
+ * v_key_len --
+ * Return the length of the string that will display the key.
+ * This routine is the backup for the KEY_LEN() macro.
+ *
+ * PUBLIC: size_t v_key_len __P((SCR *, ARG_CHAR_T));
+ */
+size_t
+v_key_len(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ (void)v_key_name(sp, ch);
+ return (sp->clen);
+}
+
+/*
+ * v_key_name --
+ * Return the string that will display the key. This routine
+ * is the backup for the KEY_NAME() macro.
+ *
+ * PUBLIC: CHAR_T *v_key_name __P((SCR *, ARG_CHAR_T));
+ */
+CHAR_T *
+v_key_name(sp, ach)
+ SCR *sp;
+ ARG_CHAR_T ach;
+{
+ static const CHAR_T hexdigit[] = "0123456789abcdef";
+ static const CHAR_T octdigit[] = "01234567";
+ CHAR_T ch, *chp, mask;
+ size_t len;
+ int cnt, shift;
+
+ ch = ach;
+
+ /* See if the character was explicitly declared printable or not. */
+ if ((chp = O_STR(sp, O_PRINT)) != NULL)
+ for (; *chp != '\0'; ++chp)
+ if (*chp == ch)
+ goto pr;
+ if ((chp = O_STR(sp, O_NOPRINT)) != NULL)
+ for (; *chp != '\0'; ++chp)
+ if (*chp == ch)
+ goto nopr;
+
+ /*
+ * Historical (ARPA standard) mappings. Printable characters are left
+ * alone. Control characters less than 0x20 are represented as '^'
+ * followed by the character offset from the '@' character in the ASCII
+ * character set. Del (0x7f) is represented as '^' followed by '?'.
+ *
+ * XXX
+ * The following code depends on the current locale being identical to
+ * the ASCII map from 0x40 to 0x5f (since 0x1f + 0x40 == 0x5f). I'm
+ * told that this is a reasonable assumption...
+ *
+ * XXX
+ * This code will only work with CHAR_T's that are multiples of 8-bit
+ * bytes.
+ *
+ * XXX
+ * NB: There's an assumption here that all printable characters take
+ * up a single column on the screen. This is not always correct.
+ */
+ if (isprint(ch)) {
+pr: sp->cname[0] = ch;
+ len = 1;
+ goto done;
+ }
+nopr: if (iscntrl(ch) && (ch < 0x20 || ch == 0x7f)) {
+ sp->cname[0] = '^';
+ sp->cname[1] = ch == 0x7f ? '?' : '@' + ch;
+ len = 2;
+ } else if (O_ISSET(sp, O_OCTAL)) {
+#define BITS (sizeof(CHAR_T) * 8)
+#define SHIFT (BITS - BITS % 3)
+#define TOPMASK (BITS % 3 == 2 ? 3 : 1) << (BITS - BITS % 3)
+ sp->cname[0] = '\\';
+ sp->cname[1] = octdigit[(ch & TOPMASK) >> SHIFT];
+ shift = SHIFT - 3;
+ for (len = 2, mask = 7 << (SHIFT - 3),
+ cnt = BITS / 3; cnt-- > 0; mask >>= 3, shift -= 3)
+ sp->cname[len++] = octdigit[(ch & mask) >> shift];
+ } else {
+ sp->cname[0] = '\\';
+ sp->cname[1] = 'x';
+ for (len = 2, chp = (u_int8_t *)&ch,
+ cnt = sizeof(CHAR_T); cnt-- > 0; ++chp) {
+ sp->cname[len++] = hexdigit[(*chp & 0xf0) >> 4];
+ sp->cname[len++] = hexdigit[*chp & 0x0f];
+ }
+ }
+done: sp->cname[sp->clen = len] = '\0';
+ return (sp->cname);
+}
+
+/*
+ * v_key_val --
+ * Fill in the value for a key. This routine is the backup
+ * for the KEY_VAL() macro.
+ *
+ * PUBLIC: int v_key_val __P((SCR *, ARG_CHAR_T));
+ */
+int
+v_key_val(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ KEYLIST k, *kp;
+
+ k.ch = ch;
+ kp = bsearch(&k, keylist, nkeylist, sizeof(keylist[0]), v_key_cmp);
+ return (kp == NULL ? K_NOTUSED : kp->value);
+}
+
+/*
+ * v_event_push --
+ * Push events/keys onto the front of the buffer.
+ *
+ * There is a single input buffer in ex/vi. Characters are put onto the
+ * end of the buffer by the terminal input routines, and pushed onto the
+ * front of the buffer by various other functions in ex/vi. Each key has
+ * an associated flag value, which indicates if it has already been quoted,
+ * and if it is the result of a mapping or an abbreviation.
+ *
+ * PUBLIC: int v_event_push __P((SCR *, EVENT *, CHAR_T *, size_t, u_int));
+ */
+int
+v_event_push(sp, p_evp, p_s, nitems, flags)
+ SCR *sp;
+ EVENT *p_evp; /* Push event. */
+ CHAR_T *p_s; /* Push characters. */
+ size_t nitems; /* Number of items to push. */
+ u_int flags; /* CH_* flags. */
+{
+ EVENT *evp;
+ GS *gp;
+ size_t total;
+
+ /* If we have room, stuff the items into the buffer. */
+ gp = sp->gp;
+ if (nitems <= gp->i_next ||
+ (gp->i_event != NULL && gp->i_cnt == 0 && nitems <= gp->i_nelem)) {
+ if (gp->i_cnt != 0)
+ gp->i_next -= nitems;
+ goto copy;
+ }
+
+ /*
+ * If there are currently items in the queue, shift them up,
+ * leaving some extra room. Get enough space plus a little
+ * extra.
+ */
+#define TERM_PUSH_SHIFT 30
+ total = gp->i_cnt + gp->i_next + nitems + TERM_PUSH_SHIFT;
+ if (total >= gp->i_nelem && v_event_grow(sp, MAX(total, 64)))
+ return (1);
+ if (gp->i_cnt)
+ MEMMOVE(gp->i_event + TERM_PUSH_SHIFT + nitems,
+ gp->i_event + gp->i_next, gp->i_cnt);
+ gp->i_next = TERM_PUSH_SHIFT;
+
+ /* Put the new items into the queue. */
+copy: gp->i_cnt += nitems;
+ for (evp = gp->i_event + gp->i_next; nitems--; ++evp) {
+ if (p_evp != NULL)
+ *evp = *p_evp++;
+ else {
+ evp->e_event = E_CHARACTER;
+ evp->e_c = *p_s++;
+ evp->e_value = KEY_VAL(sp, evp->e_c);
+ F_INIT(&evp->e_ch, flags);
+ }
+ }
+ return (0);
+}
+
+/*
+ * v_event_append --
+ * Append events onto the tail of the buffer.
+ */
+static int
+v_event_append(sp, argp)
+ SCR *sp;
+ EVENT *argp;
+{
+ CHAR_T *s; /* Characters. */
+ EVENT *evp;
+ GS *gp;
+ size_t nevents; /* Number of events. */
+
+ /* Grow the buffer as necessary. */
+ nevents = argp->e_event == E_STRING ? argp->e_len : 1;
+ gp = sp->gp;
+ if (gp->i_event == NULL ||
+ nevents > gp->i_nelem - (gp->i_next + gp->i_cnt))
+ v_event_grow(sp, MAX(nevents, 64));
+ evp = gp->i_event + gp->i_next + gp->i_cnt;
+ gp->i_cnt += nevents;
+
+ /* Transform strings of characters into single events. */
+ if (argp->e_event == E_STRING)
+ for (s = argp->e_csp; nevents--; ++evp) {
+ evp->e_event = E_CHARACTER;
+ evp->e_c = *s++;
+ evp->e_value = KEY_VAL(sp, evp->e_c);
+ evp->e_flags = 0;
+ }
+ else
+ *evp = *argp;
+ return (0);
+}
+
+/* Remove events from the queue. */
+#define QREM(len) { \
+ if ((gp->i_cnt -= len) == 0) \
+ gp->i_next = 0; \
+ else \
+ gp->i_next += len; \
+}
+
+/*
+ * v_event_get --
+ * Return the next event.
+ *
+ * !!!
+ * The flag EC_NODIGIT probably needs some explanation. First, the idea of
+ * mapping keys is that one or more keystrokes act like a function key.
+ * What's going on is that vi is reading a number, and the character following
+ * the number may or may not be mapped (EC_MAPCOMMAND). For example, if the
+ * user is entering the z command, a valid command is "z40+", and we don't want
+ * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it
+ * into "z40xxx". However, if the user enters "35x", we want to put all of the
+ * characters through the mapping code.
+ *
+ * Historical practice is a bit muddled here. (Surprise!) It always permitted
+ * mapping digits as long as they weren't the first character of the map, e.g.
+ * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9
+ * (the digit 0 was a special case as it doesn't indicate the start of a count)
+ * as the first character of the map, but then ignored those mappings. While
+ * it's probably stupid to map digits, vi isn't your mother.
+ *
+ * The way this works is that the EC_MAPNODIGIT causes term_key to return the
+ * end-of-digit without "looking" at the next character, i.e. leaving it as the
+ * user entered it. Presumably, the next term_key call will tell us how the
+ * user wants it handled.
+ *
+ * There is one more complication. Users might map keys to digits, and, as
+ * it's described above, the commands:
+ *
+ * :map g 1G
+ * d2g
+ *
+ * would return the keys "d2<end-of-digits>1G", when the user probably wanted
+ * "d21<end-of-digits>G". So, if a map starts off with a digit we continue as
+ * before, otherwise, we pretend we haven't mapped the character, and return
+ * <end-of-digits>.
+ *
+ * Now that that's out of the way, let's talk about Energizer Bunny macros.
+ * It's easy to create macros that expand to a loop, e.g. map x 3x. It's
+ * fairly easy to detect this example, because it's all internal to term_key.
+ * If we're expanding a macro and it gets big enough, at some point we can
+ * assume it's looping and kill it. The examples that are tough are the ones
+ * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion
+ * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then
+ * find the looping macro again. There is no way that we can detect this
+ * without doing a full parse of the command, because the character that might
+ * cause the loop (in this case 'x') may be a literal character, e.g. the map
+ * map x "ayy"xyy"byy is perfectly legal and won't cause a loop.
+ *
+ * Historic vi tried to detect looping macros by disallowing obvious cases in
+ * the map command, maps that that ended with the same letter as they started
+ * (which wrongly disallowed "map x 'x"), and detecting macros that expanded
+ * too many times before keys were returned to the command parser. It didn't
+ * get many (most?) of the tricky cases right, however, and it was certainly
+ * possible to create macros that ran forever. And, even if it did figure out
+ * what was going on, the user was usually tossed into ex mode. Finally, any
+ * changes made before vi realized that the macro was recursing were left in
+ * place. We recover gracefully, but the only recourse the user has in an
+ * infinite macro loop is to interrupt.
+ *
+ * !!!
+ * It is historic practice that mapping characters to themselves as the first
+ * part of the mapped string was legal, and did not cause infinite loops, i.e.
+ * ":map! { {^M^T" and ":map n nz." were known to work. The initial, matching
+ * characters were returned instead of being remapped.
+ *
+ * !!!
+ * It is also historic practice that the macro "map ] ]]^" caused a single ]
+ * keypress to behave as the command ]] (the ^ got the map past the vi check
+ * for "tail recursion"). Conversely, the mapping "map n nn^" went recursive.
+ * What happened was that, in the historic vi, maps were expanded as the keys
+ * were retrieved, but not all at once and not centrally. So, the keypress ]
+ * pushed ]]^ on the stack, and then the first ] from the stack was passed to
+ * the ]] command code. The ]] command then retrieved a key without entering
+ * the mapping code. This could bite us anytime a user has a map that depends
+ * on secondary keys NOT being mapped. I can't see any possible way to make
+ * this work in here without the complete abandonment of Rationality Itself.
+ *
+ * XXX
+ * The final issue is recovery. It would be possible to undo all of the work
+ * that was done by the macro if we entered a record into the log so that we
+ * knew when the macro started, and, in fact, this might be worth doing at some
+ * point. Given that this might make the log grow unacceptably (consider that
+ * cursor keys are done with maps), for now we leave any changes made in place.
+ *
+ * PUBLIC: int v_event_get __P((SCR *, EVENT *, int, u_int32_t));
+ */
+int
+v_event_get(sp, argp, timeout, flags)
+ SCR *sp;
+ EVENT *argp;
+ int timeout;
+ u_int32_t flags;
+{
+ EVENT *evp, ev;
+ GS *gp;
+ SEQ *qp;
+ int init_nomap, ispartial, istimeout, remap_cnt;
+
+ gp = sp->gp;
+
+ /* If simply checking for interrupts, argp may be NULL. */
+ if (argp == NULL)
+ argp = &ev;
+
+retry: istimeout = remap_cnt = 0;
+
+ /*
+ * If the queue isn't empty and we're timing out for characters,
+ * return immediately.
+ */
+ if (gp->i_cnt != 0 && LF_ISSET(EC_TIMEOUT))
+ return (0);
+
+ /*
+ * If the queue is empty, we're checking for interrupts, or we're
+ * timing out for characters, get more events.
+ */
+ if (gp->i_cnt == 0 || LF_ISSET(EC_INTERRUPT | EC_TIMEOUT)) {
+ /*
+ * If we're reading new characters, check any scripting
+ * windows for input.
+ */
+ if (F_ISSET(gp, G_SCRWIN) && sscr_input(sp))
+ return (1);
+loop: if (gp->scr_event(sp, argp,
+ LF_ISSET(EC_INTERRUPT | EC_QUOTED | EC_RAW), timeout))
+ return (1);
+ switch (argp->e_event) {
+ case E_ERR:
+ case E_SIGHUP:
+ case E_SIGTERM:
+ /*
+ * Fatal conditions cause the file to be synced to
+ * disk immediately.
+ */
+ v_sync(sp, RCV_ENDSESSION | RCV_PRESERVE |
+ (argp->e_event == E_SIGTERM ? 0: RCV_EMAIL));
+ return (1);
+ case E_TIMEOUT:
+ istimeout = 1;
+ break;
+ case E_INTERRUPT:
+ /* Set the global interrupt flag. */
+ F_SET(sp->gp, G_INTERRUPTED);
+
+ /*
+ * If the caller was interested in interrupts, return
+ * immediately.
+ */
+ if (LF_ISSET(EC_INTERRUPT))
+ return (0);
+ goto append;
+ default:
+append: if (v_event_append(sp, argp))
+ return (1);
+ break;
+ }
+ }
+
+ /*
+ * If the caller was only interested in interrupts or timeouts, return
+ * immediately. (We may have gotten characters, and that's okay, they
+ * were queued up for later use.)
+ */
+ if (LF_ISSET(EC_INTERRUPT | EC_TIMEOUT))
+ return (0);
+
+newmap: evp = &gp->i_event[gp->i_next];
+
+ /*
+ * If the next event in the queue isn't a character event, return
+ * it, we're done.
+ */
+ if (evp->e_event != E_CHARACTER) {
+ *argp = *evp;
+ QREM(1);
+ return (0);
+ }
+
+ /*
+ * If the key isn't mappable because:
+ *
+ * + ... the timeout has expired
+ * + ... it's not a mappable key
+ * + ... neither the command or input map flags are set
+ * + ... there are no maps that can apply to it
+ *
+ * return it forthwith.
+ */
+ if (istimeout || F_ISSET(&evp->e_ch, CH_NOMAP) ||
+ !LF_ISSET(EC_MAPCOMMAND | EC_MAPINPUT) ||
+ evp->e_c < MAX_BIT_SEQ && !bit_test(gp->seqb, evp->e_c))
+ goto nomap;
+
+ /* Search the map. */
+ qp = seq_find(sp, NULL, evp, NULL, gp->i_cnt,
+ LF_ISSET(EC_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, &ispartial);
+
+ /*
+ * If get a partial match, get more characters and retry the map.
+ * If time out without further characters, return the characters
+ * unmapped.
+ *
+ * !!!
+ * <escape> characters are a problem. Cursor keys start with <escape>
+ * characters, so there's almost always a map in place that begins with
+ * an <escape> character. If we timeout <escape> keys in the same way
+ * that we timeout other keys, the user will get a noticeable pause as
+ * they enter <escape> to terminate input mode. If key timeout is set
+ * for a slow link, users will get an even longer pause. Nvi used to
+ * simply timeout <escape> characters at 1/10th of a second, but this
+ * loses over PPP links where the latency is greater than 100Ms.
+ */
+ if (ispartial) {
+ if (O_ISSET(sp, O_TIMEOUT))
+ timeout = (evp->e_value == K_ESCAPE ?
+ O_VAL(sp, O_ESCAPETIME) :
+ O_VAL(sp, O_KEYTIME)) * 100;
+ else
+ timeout = 0;
+ goto loop;
+ }
+
+ /* If no map, return the character. */
+ if (qp == NULL) {
+nomap: if (!isdigit(evp->e_c) && LF_ISSET(EC_MAPNODIGIT))
+ goto not_digit;
+ *argp = *evp;
+ QREM(1);
+ return (0);
+ }
+
+ /*
+ * If looking for the end of a digit string, and the first character
+ * of the map is it, pretend we haven't seen the character.
+ */
+ if (LF_ISSET(EC_MAPNODIGIT) &&
+ qp->output != NULL && !isdigit(qp->output[0])) {
+not_digit: argp->e_c = CH_NOT_DIGIT;
+ argp->e_value = K_NOTUSED;
+ argp->e_event = E_CHARACTER;
+ F_INIT(&argp->e_ch, 0);
+ return (0);
+ }
+
+ /* Find out if the initial segments are identical. */
+ init_nomap = !e_memcmp(qp->output, &gp->i_event[gp->i_next], qp->ilen);
+
+ /* Delete the mapped characters from the queue. */
+ QREM(qp->ilen);
+
+ /* If keys mapped to nothing, go get more. */
+ if (qp->output == NULL)
+ goto retry;
+
+ /* If remapping characters... */
+ if (O_ISSET(sp, O_REMAP)) {
+ /*
+ * Periodically check for interrupts. Always check the first
+ * time through, because it's possible to set up a map that
+ * will return a character every time, but will expand to more,
+ * e.g. "map! a aaaa" will always return a 'a', but we'll never
+ * get anywhere useful.
+ */
+ if ((++remap_cnt == 1 || remap_cnt % 10 == 0) &&
+ (gp->scr_event(sp, &ev,
+ EC_INTERRUPT, 0) || ev.e_event == E_INTERRUPT)) {
+ F_SET(sp->gp, G_INTERRUPTED);
+ argp->e_event = E_INTERRUPT;
+ return (0);
+ }
+
+ /*
+ * If an initial part of the characters mapped, they are not
+ * further remapped -- return the first one. Push the rest
+ * of the characters, or all of the characters if no initial
+ * part mapped, back on the queue.
+ */
+ if (init_nomap) {
+ if (v_event_push(sp, NULL, qp->output + qp->ilen,
+ qp->olen - qp->ilen, CH_MAPPED))
+ return (1);
+ if (v_event_push(sp, NULL,
+ qp->output, qp->ilen, CH_NOMAP | CH_MAPPED))
+ return (1);
+ evp = &gp->i_event[gp->i_next];
+ goto nomap;
+ }
+ if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED))
+ return (1);
+ goto newmap;
+ }
+
+ /* Else, push the characters on the queue and return one. */
+ if (v_event_push(sp, NULL, qp->output, qp->olen, CH_MAPPED | CH_NOMAP))
+ return (1);
+
+ goto nomap;
+}
+
+/*
+ * v_sync --
+ * Walk the screen lists, sync'ing files to their backup copies.
+ */
+static void
+v_sync(sp, flags)
+ SCR *sp;
+ int flags;
+{
+ GS *gp;
+
+ gp = sp->gp;
+ for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
+ rcv_sync(sp, flags);
+ for (sp = gp->hq.cqh_first; sp != (void *)&gp->hq; sp = sp->q.cqe_next)
+ rcv_sync(sp, flags);
+}
+
+/*
+ * v_event_err --
+ * Unexpected event.
+ *
+ * PUBLIC: void v_event_err __P((SCR *, EVENT *));
+ */
+void
+v_event_err(sp, evp)
+ SCR *sp;
+ EVENT *evp;
+{
+ switch (evp->e_event) {
+ case E_CHARACTER:
+ msgq(sp, M_ERR, "276|Unexpected character event");
+ break;
+ case E_EOF:
+ msgq(sp, M_ERR, "277|Unexpected end-of-file event");
+ break;
+ case E_INTERRUPT:
+ msgq(sp, M_ERR, "279|Unexpected interrupt event");
+ break;
+ case E_QUIT:
+ msgq(sp, M_ERR, "280|Unexpected quit event");
+ break;
+ case E_REPAINT:
+ msgq(sp, M_ERR, "281|Unexpected repaint event");
+ break;
+ case E_STRING:
+ msgq(sp, M_ERR, "285|Unexpected string event");
+ break;
+ case E_TIMEOUT:
+ msgq(sp, M_ERR, "286|Unexpected timeout event");
+ break;
+ case E_WRESIZE:
+ msgq(sp, M_ERR, "316|Unexpected resize event");
+ break;
+ case E_WRITE:
+ msgq(sp, M_ERR, "287|Unexpected write event");
+ break;
+
+ /*
+ * Theoretically, none of these can occur, as they're handled at the
+ * top editor level.
+ */
+ case E_ERR:
+ case E_SIGHUP:
+ case E_SIGTERM:
+ default:
+ abort();
+ }
+
+ /* Free any allocated memory. */
+ if (evp->e_asp != NULL)
+ free(evp->e_asp);
+}
+
+/*
+ * v_event_flush --
+ * Flush any flagged keys, returning if any keys were flushed.
+ *
+ * PUBLIC: int v_event_flush __P((SCR *, u_int));
+ */
+int
+v_event_flush(sp, flags)
+ SCR *sp;
+ u_int flags;
+{
+ GS *gp;
+ int rval;
+
+ for (rval = 0, gp = sp->gp; gp->i_cnt != 0 &&
+ F_ISSET(&gp->i_event[gp->i_next].e_ch, flags); rval = 1)
+ QREM(1);
+ return (rval);
+}
+
+/*
+ * v_event_grow --
+ * Grow the terminal queue.
+ */
+static int
+v_event_grow(sp, add)
+ SCR *sp;
+ int add;
+{
+ GS *gp;
+ size_t new_nelem, olen;
+
+ gp = sp->gp;
+ new_nelem = gp->i_nelem + add;
+ olen = gp->i_nelem * sizeof(gp->i_event[0]);
+ BINC_RET(sp, gp->i_event, olen, new_nelem * sizeof(gp->i_event[0]));
+ gp->i_nelem = olen / sizeof(gp->i_event[0]);
+ return (0);
+}
+
+/*
+ * v_key_cmp --
+ * Compare two keys for sorting.
+ */
+static int
+v_key_cmp(ap, bp)
+ const void *ap, *bp;
+{
+ return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
+}
diff --git a/contrib/nvi/common/key.h b/contrib/nvi/common/key.h
new file mode 100644
index 000000000000..76fb64f8e1ec
--- /dev/null
+++ b/contrib/nvi/common/key.h
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)key.h 10.18 (Berkeley) 6/30/96
+ */
+
+/*
+ * 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
+
+/*
+ * Event types.
+ *
+ * The program structure depends on the event loop being able to return
+ * E_EOF/E_ERR multiple times -- eventually enough things will end due
+ * to the events that vi will reach the command level for the screen, at
+ * which point the exit flags will be set and vi will exit.
+ */
+typedef enum {
+ E_NOTUSED = 0, /* Not set. */
+ E_CHARACTER, /* Input character: e_c set. */
+ E_EOF, /* End of input (NOT ^D). */
+ E_ERR, /* Input error. */
+ E_INTERRUPT, /* Interrupt. */
+ E_QUIT, /* Quit. */
+ E_REPAINT, /* Repaint: e_flno, e_tlno set. */
+ E_SIGHUP, /* SIGHUP. */
+ E_SIGTERM, /* SIGTERM. */
+ E_STRING, /* Input string: e_csp, e_len set. */
+ E_TIMEOUT, /* Timeout. */
+ E_WRESIZE, /* Window resize. */
+ E_WRITE /* Write. */
+} e_event_t;
+
+/*
+ * Character values.
+ */
+typedef enum {
+ K_NOTUSED = 0, /* Not set. */
+ K_BACKSLASH, /* \ */
+ K_CARAT, /* ^ */
+ K_CNTRLD, /* ^D */
+ K_CNTRLR, /* ^R */
+ K_CNTRLT, /* ^T */
+ K_CNTRLZ, /* ^Z */
+ K_COLON, /* : */
+ K_CR, /* \r */
+ K_ESCAPE, /* ^[ */
+ K_FORMFEED, /* \f */
+ K_HEXCHAR, /* ^X */
+ K_NL, /* \n */
+ K_RIGHTBRACE, /* } */
+ K_RIGHTPAREN, /* ) */
+ K_TAB, /* \t */
+ K_VERASE, /* set from tty: default ^H */
+ K_VKILL, /* set from tty: default ^U */
+ K_VLNEXT, /* set from tty: default ^V */
+ K_VWERASE, /* set from tty: default ^W */
+ K_ZERO /* 0 */
+} e_key_t;
+
+struct _event {
+ TAILQ_ENTRY(_event) q; /* Linked list of events. */
+ e_event_t e_event; /* Event type. */
+ union {
+ struct { /* Input character. */
+ CHAR_T c; /* Character. */
+ e_key_t value; /* Key type. */
+
+#define CH_ABBREVIATED 0x01 /* Character is from an abbreviation. */
+#define CH_MAPPED 0x02 /* Character is from a map. */
+#define CH_NOMAP 0x04 /* Do not map the character. */
+#define CH_QUOTED 0x08 /* Character is already quoted. */
+ u_int8_t flags;
+ } _e_ch;
+#define e_ch _u_event._e_ch /* !!! The structure, not the char. */
+#define e_c _u_event._e_ch.c
+#define e_value _u_event._e_ch.value
+#define e_flags _u_event._e_ch.flags
+
+ struct { /* Screen position, size. */
+ size_t lno1; /* Line number. */
+ size_t cno1; /* Column number. */
+ size_t lno2; /* Line number. */
+ size_t cno2; /* Column number. */
+ } _e_mark;
+#define e_lno _u_event._e_mark.lno1 /* Single location. */
+#define e_cno _u_event._e_mark.cno1
+#define e_flno _u_event._e_mark.lno1 /* Text region. */
+#define e_fcno _u_event._e_mark.cno1
+#define e_tlno _u_event._e_mark.lno2
+#define e_tcno _u_event._e_mark.cno2
+
+ struct { /* Input string. */
+ CHAR_T *asp; /* Allocated string. */
+ CHAR_T *csp; /* String. */
+ size_t len; /* String length. */
+ } _e_str;
+#define e_asp _u_event._e_str.asp
+#define e_csp _u_event._e_str.csp
+#define e_len _u_event._e_str.len
+ } _u_event;
+};
+
+typedef struct _keylist {
+ e_key_t value; /* Special value. */
+ CHAR_T ch; /* Key. */
+} KEYLIST;
+extern KEYLIST keylist[];
+
+ /* Return if more keys in queue. */
+#define KEYS_WAITING(sp) ((sp)->gp->i_cnt != 0)
+#define MAPPED_KEYS_WAITING(sp) \
+ (KEYS_WAITING(sp) && \
+ F_ISSET(&sp->gp->i_event[sp->gp->i_next].e_ch, CH_MAPPED))
+
+/*
+ * 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
+
+/* The "standard" tab width, for displaying things to users. */
+#define STANDARD_TAB 6
+
+/* Various special characters, messages. */
+#define CH_BSEARCH '?' /* Backward search prompt. */
+#define CH_CURSOR ' ' /* Cursor character. */
+#define CH_ENDMARK '$' /* End of a range. */
+#define CH_EXPROMPT ':' /* Ex prompt. */
+#define CH_FSEARCH '/' /* Forward search prompt. */
+#define CH_HEX '\030' /* Leading hex character. */
+#define CH_LITERAL '\026' /* ASCII ^V. */
+#define CH_NO 'n' /* No. */
+#define CH_NOT_DIGIT 'a' /* A non-isdigit() character. */
+#define CH_QUIT 'q' /* Quit. */
+#define CH_YES 'y' /* Yes. */
+
+/*
+ * Checking for interrupts means that we look at the bit that gets set if the
+ * screen code supports asynchronous events, and call back into the event code
+ * so that non-asynchronous screens get a chance to post the interrupt.
+ *
+ * INTERRUPT_CHECK is the number of lines "operated" on before checking for
+ * interrupts.
+ */
+#define INTERRUPT_CHECK 100
+#define INTERRUPTED(sp) \
+ (F_ISSET((sp)->gp, G_INTERRUPTED) || \
+ (!v_event_get(sp, NULL, 0, EC_INTERRUPT) && \
+ F_ISSET((sp)->gp, G_INTERRUPTED)))
+#define CLR_INTERRUPT(sp) \
+ F_CLR((sp)->gp, G_INTERRUPTED)
+
+/* Flags describing types of characters being requested. */
+#define EC_INTERRUPT 0x001 /* Checking for interrupts. */
+#define EC_MAPCOMMAND 0x002 /* Apply the command map. */
+#define EC_MAPINPUT 0x004 /* Apply the input map. */
+#define EC_MAPNODIGIT 0x008 /* Return to a digit. */
+#define EC_QUOTED 0x010 /* Try to quote next character */
+#define EC_RAW 0x020 /* Any next character. XXX: not used. */
+#define EC_TIMEOUT 0x040 /* Timeout to next character. */
+
+/* Flags describing text input special cases. */
+#define TXT_ADDNEWLINE 0x00000001 /* Replay starts on a new line. */
+#define TXT_AICHARS 0x00000002 /* Leading autoindent chars. */
+#define TXT_ALTWERASE 0x00000004 /* Option: altwerase. */
+#define TXT_APPENDEOL 0x00000008 /* Appending after EOL. */
+#define TXT_AUTOINDENT 0x00000010 /* Autoindent set this line. */
+#define TXT_BACKSLASH 0x00000020 /* Backslashes escape characters. */
+#define TXT_BEAUTIFY 0x00000040 /* Only printable characters. */
+#define TXT_BS 0x00000080 /* Backspace returns the buffer. */
+#define TXT_CEDIT 0x00000100 /* Can return TERM_CEDIT. */
+#define TXT_CNTRLD 0x00000200 /* Control-D is a command. */
+#define TXT_CNTRLT 0x00000400 /* Control-T is an indent special. */
+#define TXT_CR 0x00000800 /* CR returns the buffer. */
+#define TXT_DOTTERM 0x00001000 /* Leading '.' terminates the input. */
+#define TXT_EMARK 0x00002000 /* End of replacement mark. */
+#define TXT_EOFCHAR 0x00004000 /* ICANON set, return EOF character. */
+#define TXT_ESCAPE 0x00008000 /* Escape returns the buffer. */
+#define TXT_FILEC 0x00010000 /* Option: filec. */
+#define TXT_INFOLINE 0x00020000 /* Editing the info line. */
+#define TXT_MAPINPUT 0x00040000 /* Apply the input map. */
+#define TXT_NLECHO 0x00080000 /* Echo the newline. */
+#define TXT_NUMBER 0x00100000 /* Number the line. */
+#define TXT_OVERWRITE 0x00200000 /* Overwrite characters. */
+#define TXT_PROMPT 0x00400000 /* Display a prompt. */
+#define TXT_RECORD 0x00800000 /* Record for replay. */
+#define TXT_REPLACE 0x01000000 /* Replace; don't delete overwrite. */
+#define TXT_REPLAY 0x02000000 /* Replay the last input. */
+#define TXT_RESOLVE 0x04000000 /* Resolve the text into the file. */
+#define TXT_SEARCHINCR 0x08000000 /* Incremental search. */
+#define TXT_SHOWMATCH 0x10000000 /* Option: showmatch. */
+#define TXT_TTYWERASE 0x20000000 /* Option: ttywerase. */
+#define TXT_WRAPMARGIN 0x40000000 /* Option: wrapmargin. */
diff --git a/contrib/nvi/common/line.c b/contrib/nvi/common/line.c
new file mode 100644
index 000000000000..bcb9e0c86bcb
--- /dev/null
+++ b/contrib/nvi/common/line.c
@@ -0,0 +1,576 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)line.c 10.21 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "../vi/vi.h"
+
+static int scr_update __P((SCR *, recno_t, lnop_t, int));
+
+/*
+ * db_eget --
+ * Front-end to db_get, special case handling for empty files.
+ *
+ * PUBLIC: int db_eget __P((SCR *, recno_t, char **, size_t *, int *));
+ */
+int
+db_eget(sp, lno, pp, lenp, isemptyp)
+ SCR *sp;
+ recno_t lno; /* Line number. */
+ char **pp; /* Pointer store. */
+ size_t *lenp; /* Length store. */
+ int *isemptyp;
+{
+ recno_t l1;
+
+ if (isemptyp != NULL)
+ *isemptyp = 0;
+
+ /* If the line exists, simply return it. */
+ if (!db_get(sp, lno, 0, pp, lenp))
+ return (0);
+
+ /*
+ * If the user asked for line 0 or line 1, i.e. the only possible
+ * line in an empty file, find the last line of the file; db_last
+ * fails loudly.
+ */
+ if ((lno == 0 || lno == 1) && db_last(sp, &l1))
+ return (1);
+
+ /* If the file isn't empty, fail loudly. */
+ if (lno != 0 && lno != 1 || l1 != 0) {
+ db_err(sp, lno);
+ return (1);
+ }
+
+ if (isemptyp != NULL)
+ *isemptyp = 1;
+
+ return (1);
+}
+
+/*
+ * db_get --
+ * Look in the text buffers for a line, followed by the cache, followed
+ * by the database.
+ *
+ * PUBLIC: int db_get __P((SCR *, recno_t, u_int32_t, char **, size_t *));
+ */
+int
+db_get(sp, lno, flags, pp, lenp)
+ SCR *sp;
+ recno_t lno; /* Line number. */
+ u_int32_t flags;
+ char **pp; /* Pointer store. */
+ size_t *lenp; /* Length store. */
+{
+ DBT data, key;
+ EXF *ep;
+ 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)
+ goto err1;
+
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ goto err3;
+ }
+
+ if (LF_ISSET(DBG_NOCACHE))
+ goto nocache;
+
+ /*
+ * Look-aside into the TEXT buffers and see if the line we want
+ * is there.
+ */
+ if (F_ISSET(sp, SC_TINPUT)) {
+ l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
+ l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
+ if (l1 <= lno && l2 >= lno) {
+#if defined(DEBUG) && 0
+ TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno);
+#endif
+ for (tp = sp->tiq.cqh_first;
+ tp->lno != lno; tp = tp->q.cqe_next);
+ if (lenp != NULL)
+ *lenp = tp->len;
+ if (pp != NULL)
+ *pp = tp->lb;
+ return (0);
+ }
+ /*
+ * Adjust the line number for the number of lines used
+ * by the text input buffers.
+ */
+ if (lno > l2)
+ lno -= l2 - l1;
+ }
+
+ /* Look-aside into the cache, and see if the line we want is there. */
+ if (lno == ep->c_lno) {
+#if defined(DEBUG) && 0
+ TRACE(sp, "retrieve cached line %lu\n", (u_long)lno);
+#endif
+ if (lenp != NULL)
+ *lenp = ep->c_len;
+ if (pp != NULL)
+ *pp = ep->c_lp;
+ return (0);
+ }
+ ep->c_lno = OOBLNO;
+
+nocache:
+ /* 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:
+ goto err2;
+ case 1:
+err1: if (LF_ISSET(DBG_FATAL))
+err2: db_err(sp, lno);
+err3: if (lenp != NULL)
+ *lenp = 0;
+ if (pp != NULL)
+ *pp = NULL;
+ return (1);
+ }
+
+ /* Reset the cache. */
+ ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "retrieve DB line %lu\n", (u_long)lno);
+#endif
+ if (lenp != NULL)
+ *lenp = data.size;
+ if (pp != NULL)
+ *pp = ep->c_lp;
+ return (0);
+}
+
+/*
+ * db_delete --
+ * Delete a line from the file.
+ *
+ * PUBLIC: int db_delete __P((SCR *, recno_t));
+ */
+int
+db_delete(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ DBT key;
+ EXF *ep;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "delete line %lu\n", (u_long)lno);
+#endif
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ /* Update marks, @ and global commands. */
+ if (mark_insdel(sp, LINE_DELETE, lno))
+ return (1);
+ if (ex_g_insdel(sp, LINE_DELETE, lno))
+ return (1);
+
+ /* Log change. */
+ log_line(sp, lno, LOG_LINE_DELETE);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ SIGBLOCK;
+ if (ep->db->del(ep->db, &key, 0) == 1) {
+ msgq(sp, M_SYSERR,
+ "003|unable to delete line %lu", (u_long)lno);
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* 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 modified. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp);
+ F_SET(ep, F_MODIFIED);
+
+ /* Update screen. */
+ return (scr_update(sp, lno, LINE_DELETE, 1));
+}
+
+/*
+ * db_append --
+ * Append a line into the file.
+ *
+ * PUBLIC: int db_append __P((SCR *, int, recno_t, char *, size_t));
+ */
+int
+db_append(sp, update, lno, p, len)
+ SCR *sp;
+ int update;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ EXF *ep;
+ int rval;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK;
+ if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
+ msgq(sp, M_SYSERR,
+ "004|unable to append to line %lu", (u_long)lno);
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* 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);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, lno + 1, LOG_LINE_APPEND);
+
+ /* Update marks, @ and global commands. */
+ rval = 0;
+ if (mark_insdel(sp, LINE_INSERT, lno + 1))
+ rval = 1;
+ if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
+ rval = 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, lno, LINE_APPEND, update) || rval);
+}
+
+/*
+ * db_insert --
+ * Insert a line into the file.
+ *
+ * PUBLIC: int db_insert __P((SCR *, recno_t, char *, size_t));
+ */
+int
+db_insert(sp, lno, p, len)
+ SCR *sp;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ EXF *ep;
+ int rval;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "insert before %lu: len %lu {%.*s}\n",
+ (u_long)lno, (u_long)len, MIN(len, 20), p);
+#endif
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK;
+ if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
+ msgq(sp, M_SYSERR,
+ "005|unable to insert at line %lu", (u_long)lno);
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* 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);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, lno, LOG_LINE_INSERT);
+
+ /* Update marks, @ and global commands. */
+ rval = 0;
+ if (mark_insdel(sp, LINE_INSERT, lno))
+ rval = 1;
+ if (ex_g_insdel(sp, LINE_INSERT, lno))
+ rval = 1;
+
+ /* Update screen. */
+ return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
+}
+
+/*
+ * db_set --
+ * Store a line in the file.
+ *
+ * PUBLIC: int db_set __P((SCR *, recno_t, char *, size_t));
+ */
+int
+db_set(sp, lno, p, len)
+ SCR *sp;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ EXF *ep;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "replace line %lu: len %lu {%.*s}\n",
+ (u_long)lno, (u_long)len, MIN(len, 20), p);
+#endif
+
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ /* Log before change. */
+ log_line(sp, lno, LOG_LINE_RESET_B);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK;
+ if (ep->db->put(ep->db, &key, &data, 0) == -1) {
+ msgq(sp, M_SYSERR,
+ "006|unable to store line %lu", (u_long)lno);
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* 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);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log after change. */
+ log_line(sp, lno, LOG_LINE_RESET_F);
+
+ /* Update screen. */
+ return (scr_update(sp, lno, LINE_RESET, 1));
+}
+
+/*
+ * db_exist --
+ * Return if a line exists.
+ *
+ * PUBLIC: int db_exist __P((SCR *, recno_t));
+ */
+int
+db_exist(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ EXF *ep;
+
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ if (lno == OOBLNO)
+ return (0);
+
+ /*
+ * Check the last-line number cache. Adjust the cached line
+ * number for the lines used by the text input buffers.
+ */
+ if (ep->c_nlines != OOBLNO)
+ return (lno <= (F_ISSET(sp, SC_TINPUT) ?
+ ep->c_nlines + (((TEXT *)sp->tiq.cqh_last)->lno -
+ ((TEXT *)sp->tiq.cqh_first)->lno) : ep->c_nlines));
+
+ /* Go get the line. */
+ return (!db_get(sp, lno, 0, NULL, NULL));
+}
+
+/*
+ * db_last --
+ * Return the number of lines in the file.
+ *
+ * PUBLIC: int db_last __P((SCR *, recno_t *));
+ */
+int
+db_last(sp, lnop)
+ SCR *sp;
+ recno_t *lnop;
+{
+ DBT data, key;
+ EXF *ep;
+ recno_t lno;
+
+ /* Check for no underlying file. */
+ if ((ep = sp->ep) == NULL) {
+ ex_emsg(sp, NULL, EXM_NOFILEYET);
+ return (1);
+ }
+
+ /*
+ * Check the last-line number cache. Adjust the cached line
+ * number for the lines used by the text input buffers.
+ */
+ if (ep->c_nlines != OOBLNO) {
+ *lnop = ep->c_nlines;
+ if (F_ISSET(sp, SC_TINPUT))
+ *lnop += ((TEXT *)sp->tiq.cqh_last)->lno -
+ ((TEXT *)sp->tiq.cqh_first)->lno;
+ return (0);
+ }
+
+ key.data = &lno;
+ key.size = sizeof(lno);
+
+ switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
+ case -1:
+ msgq(sp, M_SYSERR, "007|unable to get last line");
+ *lnop = 0;
+ return (1);
+ case 1:
+ *lnop = 0;
+ return (0);
+ default:
+ break;
+ }
+
+ /* Fill the cache. */
+ memcpy(&lno, key.data, sizeof(lno));
+ ep->c_nlines = ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+ /* Return the value. */
+ *lnop = (F_ISSET(sp, SC_TINPUT) &&
+ ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
+ ((TEXT *)sp->tiq.cqh_last)->lno : lno);
+ return (0);
+}
+
+/*
+ * db_err --
+ * Report a line error.
+ *
+ * PUBLIC: void db_err __P((SCR *, recno_t));
+ */
+void
+db_err(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ msgq(sp, M_ERR,
+ "008|Error: unable to retrieve line %lu", (u_long)lno);
+}
+
+/*
+ * scr_update --
+ * Update all of the screens that are backed by the file that
+ * just changed.
+ */
+static int
+scr_update(sp, lno, op, current)
+ SCR *sp;
+ recno_t lno;
+ lnop_t op;
+ int current;
+{
+ EXF *ep;
+ SCR *tsp;
+
+ if (F_ISSET(sp, SC_EX))
+ return (0);
+
+ ep = sp->ep;
+ 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)
+ if (vs_change(tsp, lno, op))
+ return (1);
+ return (current ? vs_change(sp, lno, op) : 0);
+}
diff --git a/contrib/nvi/common/log.c b/contrib/nvi/common/log.c
new file mode 100644
index 000000000000..9a9fe793ffb8
--- /dev/null
+++ b/contrib/nvi/common/log.c
@@ -0,0 +1,717 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)log.c 10.8 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * The log consists of records, each containing a type byte and a variable
+ * length byte string, as follows:
+ *
+ * LOG_CURSOR_INIT MARK
+ * LOG_CURSOR_END MARK
+ * LOG_LINE_APPEND 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 LMARK
+ *
+ * We do before image physical logging. This means that the editor layer
+ * MAY NOT modify records in place, even if simply deleting or overwriting
+ * characters. Since the smallest unit of logging is a line, we're using
+ * up lots of space. This may eventually have to be reduced, probably by
+ * doing logical logging, which is a much cooler database phrase.
+ *
+ * The implementation of the historic vi 'u' command, using roll-forward and
+ * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
+ * followed by a number of other records, followed by a LOG_CURSOR_END record.
+ * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
+ * record, and is the line before the change. The second is LOG_LINE_RESET_F,
+ * and is the line after the change. Roll-back is done by backing up to the
+ * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
+ * similar fashion.
+ *
+ * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
+ * record for a line different from the current one. It should be noted that
+ * this means that a subsequent 'u' command will make a change based on the
+ * new position of the log's cursor. This is okay, and, in fact, historic vi
+ * behaved that way.
+ */
+
+static int log_cursor1 __P((SCR *, int));
+static void log_err __P((SCR *, char *, 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 { \
+ log_err(sp, __FILE__, __LINE__); \
+ return (1); \
+}
+
+/*
+ * log_init --
+ * Initialize the logging subsystem.
+ *
+ * PUBLIC: int log_init __P((SCR *, EXF *));
+ */
+int
+log_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ *
+ * Initialize the buffer. The logging subsystem has its own
+ * buffers because the global ones are almost by definition
+ * going to be in use when the log runs.
+ */
+ 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_SYSERR, "009|Log file");
+ F_SET(ep, F_NOLOG);
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * log_end --
+ * Close the logging subsystem.
+ *
+ * PUBLIC: int log_end __P((SCR *, EXF *));
+ */
+int
+log_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ */
+ 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.
+ *
+ * PUBLIC: int log_cursor __P((SCR *));
+ */
+int
+log_cursor(sp)
+ SCR *sp;
+{
+ EXF *ep;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /*
+ * If any changes were made since the last cursor init,
+ * put out the ending cursor record.
+ */
+ if (ep->l_cursor.lno == OOBLNO) {
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (log_cursor1(sp, LOG_CURSOR_END));
+ }
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (0);
+}
+
+/*
+ * log_cursor1 --
+ * Actually push a cursor record out.
+ */
+static int
+log_cursor1(sp, type)
+ SCR *sp;
+ int type;
+{
+ DBT data, key;
+ EXF *ep;
+
+ ep = sp->ep;
+ 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.
+ *
+ * PUBLIC: int log_line __P((SCR *, recno_t, u_int));
+ */
+int
+log_line(sp, lno, action)
+ SCR *sp;
+ recno_t lno;
+ u_int action;
+{
+ DBT data, key;
+ EXF *ep;
+ size_t len;
+ char *lp;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /*
+ * XXX
+ *
+ * Kluge for vi. Clear the EXF undo flag so that the
+ * next 'u' command does a roll-back, regardless.
+ */
+ F_CLR(ep, F_UNDO);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ /*
+ * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
+ * special case, avoid the caches. Also, if it fails and it's
+ * line 1, it just means that the user started with an empty file,
+ * so fake an empty length line.
+ */
+ if (action == LOG_LINE_RESET_B) {
+ if (db_get(sp, lno, DBG_NOCACHE, &lp, &len)) {
+ if (lno != 1) {
+ db_err(sp, lno);
+ return (1);
+ }
+ len = 0;
+ lp = "";
+ }
+ } else
+ if (db_get(sp, lno, DBG_FATAL, &lp, &len))
+ 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.
+ *
+ * PUBLIC: int log_mark __P((SCR *, LMARK *));
+ */
+int
+log_mark(sp, lmp)
+ SCR *sp;
+ LMARK *lmp;
+{
+ DBT data, key;
+ EXF *ep;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ BINC_RET(sp, ep->l_lp,
+ ep->l_len, sizeof(u_char) + sizeof(LMARK));
+ ep->l_lp[0] = LOG_MARK;
+ memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = sizeof(u_char) + sizeof(LMARK);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "%lu: mark %c: %lu/%u\n",
+ ep->l_cur, lmp->name, lmp->lno, lmp->cno);
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+ return (0);
+}
+
+/*
+ * Log_backward --
+ * Roll the log backward one operation.
+ *
+ * PUBLIC: int log_backward __P((SCR *, MARK *));
+ */
+int
+log_backward(sp, rp)
+ SCR *sp;
+ MARK *rp;
+{
+ DBT key, data;
+ EXF *ep;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "010|Logging not being performed, undo not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == 1) {
+ msgq(sp, M_BERR, "011|No changes to undo");
+ return (1);
+ }
+
+ 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 (db_delete(sp, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_DELETE:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (db_insert(sp, 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 (db_set(sp, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_setline --
+ * Reset the line to its original appearance.
+ *
+ * XXX
+ * There's a bug in this code due to our not logging cursor movements
+ * unless a change was made. If you do a change, move off the line,
+ * then move back on and do a 'U', the line will be restored to the way
+ * it was before the original change.
+ *
+ * PUBLIC: int log_setline __P((SCR *));
+ */
+int
+log_setline(sp)
+ SCR *sp;
+{
+ DBT key, data;
+ EXF *ep;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ u_char *p;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "012|Logging not being performed, undo not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == 1)
+ return (1);
+
+ 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 &&
+ db_set(sp, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ case LOG_MARK:
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_forward --
+ * Roll the log forward one operation.
+ *
+ * PUBLIC: int log_forward __P((SCR *, MARK *));
+ */
+int
+log_forward(sp, rp)
+ SCR *sp;
+ MARK *rp;
+{
+ DBT key, data;
+ EXF *ep;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ ep = sp->ep;
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "013|Logging not being performed, roll-forward not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == ep->l_high) {
+ msgq(sp, M_BERR, "014|No changes to re-do");
+ return (1);
+ }
+
+ 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 (db_insert(sp, 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 (db_delete(sp, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_RESET_B:
+ break;
+ case LOG_LINE_RESET_F:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (db_set(sp, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * log_err --
+ * Try and restart the log on failure, i.e. if we run out of memory.
+ */
+static void
+log_err(sp, file, line)
+ SCR *sp;
+ char *file;
+ int line;
+{
+ EXF *ep;
+
+ msgq(sp, M_SYSERR, "015|%s/%d: log put error", tail(file), line);
+ ep = sp->ep;
+ (void)ep->log->close(ep->log);
+ if (!log_init(sp, ep))
+ msgq(sp, M_ERR, "267|Log restarted");
+}
+
+#if defined(DEBUG) && 0
+static void
+log_trace(sp, msg, rno, p)
+ SCR *sp;
+ char *msg;
+ recno_t rno;
+ u_char *p;
+{
+ LMARK lm;
+ 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(&lm, p + sizeof(u_char), sizeof(LMARK));
+ TRACE(sp,
+ "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
+ break;
+ default:
+ abort();
+ }
+}
+#endif
diff --git a/contrib/nvi/common/log.h b/contrib/nvi/common/log.h
new file mode 100644
index 000000000000..df307319b1d3
--- /dev/null
+++ b/contrib/nvi/common/log.h
@@ -0,0 +1,20 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)log.h 10.2 (Berkeley) 3/6/96
+ */
+
+#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
diff --git a/contrib/nvi/common/main.c b/contrib/nvi/common/main.c
new file mode 100644
index 000000000000..6fb2ed1fe2f0
--- /dev/null
+++ b/contrib/nvi/common/main.c
@@ -0,0 +1,617 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n\
+@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996\n\
+ Keith Bostic. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)main.c 10.48 (Berkeley) 10/11/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../vi/vi.h"
+#include "pathnames.h"
+
+static void attach __P((GS *));
+static void v_estr __P((char *, int, char *));
+static int v_obsolete __P((char *, char *[]));
+
+/*
+ * editor --
+ * Main editor routine.
+ *
+ * PUBLIC: int editor __P((GS *, int, char *[]));
+ */
+int
+editor(gp, argc, argv)
+ GS *gp;
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ const char *p;
+ EVENT ev;
+ FREF *frp;
+ SCR *sp;
+ size_t len;
+ u_int flags;
+ int ch, flagchk, lflag, secure, startup, readonly, rval, silent;
+ char *tag_f, *wsizearg, path[256];
+
+ /* Initialize the busy routine, if not defined by the screen. */
+ if (gp->scr_busy == NULL)
+ gp->scr_busy = vs_busy;
+ /* Initialize the message routine, if not defined by the screen. */
+ if (gp->scr_msg == NULL)
+ gp->scr_msg = vs_msg;
+
+ /* Common global structure initialization. */
+ CIRCLEQ_INIT(&gp->dq);
+ CIRCLEQ_INIT(&gp->hq);
+ LIST_INIT(&gp->ecq);
+ LIST_INSERT_HEAD(&gp->ecq, &gp->excmd, q);
+ gp->noprint = DEFAULT_NOPRINT;
+
+ /* Structures shared by screens so stored in the GS structure. */
+ CIRCLEQ_INIT(&gp->frefq);
+ CIRCLEQ_INIT(&gp->dcb_store.textq);
+ LIST_INIT(&gp->cutq);
+ LIST_INIT(&gp->seqq);
+
+ /* Set initial screen type and mode based on the program name. */
+ readonly = 0;
+ if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex"))
+ LF_INIT(SC_EX);
+ else {
+ /* Nview, view are readonly. */
+ if (!strcmp(gp->progname, "nview") ||
+ !strcmp(gp->progname, "view"))
+ readonly = 1;
+
+ /* Vi is the default. */
+ LF_INIT(SC_VI);
+ }
+
+ /* Convert old-style arguments into new-style ones. */
+ if (v_obsolete(gp->progname, argv))
+ return (1);
+
+ /* Parse the arguments. */
+ flagchk = '\0';
+ tag_f = wsizearg = NULL;
+ lflag = secure = silent = 0;
+ startup = 1;
+
+ /* Set the file snapshot flag. */
+ F_SET(gp, G_SNAPSHOT);
+
+#ifdef DEBUG
+ while ((ch = getopt(argc, argv, "c:D:eFlRrSsT:t:vw:")) != EOF)
+#else
+ while ((ch = getopt(argc, argv, "c:eFlRrSst:vw:")) != EOF)
+#endif
+ switch (ch) {
+ case 'c': /* Run the command. */
+ /*
+ * XXX
+ * We should support multiple -c options.
+ */
+ if (gp->c_option != NULL) {
+ v_estr(gp->progname, 0,
+ "only one -c command may be specified.");
+ return (1);
+ }
+ gp->c_option = optarg;
+ break;
+#ifdef DEBUG
+ case 'D':
+ switch (optarg[0]) {
+ case 's':
+ startup = 0;
+ break;
+ case 'w':
+ attach(gp);
+ break;
+ default:
+ v_estr(gp->progname, 0,
+ "usage: -D requires s or w argument.");
+ return (1);
+ }
+ break;
+#endif
+ case 'e': /* Ex mode. */
+ LF_CLR(SC_VI);
+ LF_SET(SC_EX);
+ break;
+ case 'F': /* No snapshot. */
+ F_CLR(gp, G_SNAPSHOT);
+ break;
+ case 'l': /* Set lisp, showmatch options. */
+ lflag = 1;
+ break;
+ case 'R': /* Readonly. */
+ readonly = 1;
+ break;
+ case 'r': /* Recover. */
+ if (flagchk == 't') {
+ v_estr(gp->progname, 0,
+ "only one of -r and -t may be specified.");
+ return (1);
+ }
+ flagchk = 'r';
+ break;
+ case 'S':
+ secure = 1;
+ break;
+ case 's':
+ silent = 1;
+ break;
+#ifdef DEBUG
+ case 'T': /* Trace. */
+ if ((gp->tracefp = fopen(optarg, "w")) == NULL) {
+ v_estr(gp->progname, errno, optarg);
+ goto err;
+ }
+ (void)fprintf(gp->tracefp,
+ "\n===\ntrace: open %s\n", optarg);
+ break;
+#endif
+ case 't': /* Tag. */
+ if (flagchk == 'r') {
+ v_estr(gp->progname, 0,
+ "only one of -r and -t may be specified.");
+ return (1);
+ }
+ if (flagchk == 't') {
+ v_estr(gp->progname, 0,
+ "only one tag file may be specified.");
+ return (1);
+ }
+ flagchk = 't';
+ tag_f = optarg;
+ break;
+ case 'v': /* Vi mode. */
+ LF_CLR(SC_EX);
+ LF_SET(SC_VI);
+ break;
+ case 'w':
+ wsizearg = optarg;
+ break;
+ case '?':
+ default:
+ (void)gp->scr_usage();
+ return (1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * -s option is only meaningful to ex.
+ *
+ * If not reading from a terminal, it's like -s was specified.
+ */
+ if (silent && !LF_ISSET(SC_EX)) {
+ v_estr(gp->progname, 0, "-s option is only applicable to ex.");
+ goto err;
+ }
+ if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED))
+ silent = 1;
+
+ /*
+ * Build and initialize the first/current screen. This is a bit
+ * tricky. If an error is returned, we may or may not have a
+ * screen structure. If we have a screen structure, put it on a
+ * display queue so that the error messages get displayed.
+ *
+ * !!!
+ * Everything we do until we go interactive is done in ex mode.
+ */
+ if (screen_init(gp, NULL, &sp)) {
+ if (sp != NULL)
+ CIRCLEQ_INSERT_HEAD(&gp->dq, sp, q);
+ goto err;
+ }
+ F_SET(sp, SC_EX);
+ CIRCLEQ_INSERT_HEAD(&gp->dq, sp, q);
+
+ if (v_key_init(sp)) /* Special key initialization. */
+ goto err;
+
+ { int oargs[5], *oargp = oargs;
+ if (lflag) { /* Command-line options. */
+ *oargp++ = O_LISP;
+ *oargp++ = O_SHOWMATCH;
+ }
+ if (readonly)
+ *oargp++ = O_READONLY;
+ if (secure)
+ *oargp++ = O_SECURE;
+ *oargp = -1; /* Options initialization. */
+ if (opts_init(sp, oargs))
+ goto err;
+ }
+ if (wsizearg != NULL) {
+ ARGS *av[2], a, b;
+ (void)snprintf(path, sizeof(path), "window=%s", wsizearg);
+ a.bp = (CHAR_T *)path;
+ a.len = strlen(path);
+ b.bp = NULL;
+ b.len = 0;
+ av[0] = &a;
+ av[1] = &b;
+ (void)opts_set(sp, av, NULL);
+ }
+ if (silent) { /* Ex batch mode option values. */
+ O_CLR(sp, O_AUTOPRINT);
+ O_CLR(sp, O_PROMPT);
+ O_CLR(sp, O_VERBOSE);
+ O_CLR(sp, O_WARN);
+ F_SET(sp, SC_EX_SILENT);
+ }
+
+ sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */
+ sp->cols = O_VAL(sp, O_COLUMNS);
+
+ if (!silent && startup) { /* Read EXINIT, exrc files. */
+ if (ex_exrc(sp))
+ goto err;
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
+ if (screen_end(sp))
+ goto err;
+ goto done;
+ }
+ }
+
+ /*
+ * List recovery files if -r specified without file arguments.
+ * Note, options must be initialized and startup information
+ * read before doing this.
+ */
+ if (flagchk == 'r' && argv[0] == NULL) {
+ if (rcv_list(sp))
+ goto err;
+ if (screen_end(sp))
+ goto err;
+ goto done;
+ }
+
+ /*
+ * !!!
+ * Initialize the default ^D, ^U scrolling value here, after the
+ * user has had every opportunity to set the window option.
+ *
+ * It's historic practice that changing the value of the window
+ * option did not alter the default scrolling value, only giving
+ * a count to ^D/^U did that.
+ */
+ sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2;
+
+ /*
+ * If we don't have a command-line option, switch into the right
+ * editor now, so that we position default files correctly, and
+ * so that any tags file file-already-locked messages are in the
+ * vi screen, not the ex screen.
+ *
+ * XXX
+ * If we have a command-line option, the error message can end
+ * up in the wrong place, but I think that the combination is
+ * unlikely.
+ */
+ if (gp->c_option == NULL) {
+ F_CLR(sp, SC_EX | SC_VI);
+ F_SET(sp, LF_ISSET(SC_EX | SC_VI));
+ }
+
+ /* Open a tag file if specified. */
+ if (tag_f != NULL && ex_tag_first(sp, tag_f))
+ goto err;
+
+ /*
+ * Append any remaining arguments as file names. Files are recovery
+ * files if -r specified. If the tag option or ex startup commands
+ * loaded a file, then any file arguments are going to come after it.
+ */
+ if (*argv != NULL) {
+ if (sp->frp != NULL) {
+ /* Cheat -- we know we have an extra argv slot. */
+ MALLOC_NOMSG(sp,
+ *--argv, char *, strlen(sp->frp->name) + 1);
+ if (*argv == NULL) {
+ v_estr(gp->progname, errno, NULL);
+ goto err;
+ }
+ (void)strcpy(*argv, sp->frp->name);
+ }
+ sp->argv = sp->cargv = argv;
+ F_SET(sp, SC_ARGNOFREE);
+ if (flagchk == 'r')
+ F_SET(sp, SC_ARGRECOVER);
+ }
+
+ /*
+ * If the ex startup commands and or/the tag option haven't already
+ * created a file, create one. If no command-line files were given,
+ * use a temporary file.
+ */
+ if (sp->frp == NULL) {
+ if (sp->argv == NULL) {
+ if ((frp = file_add(sp, NULL)) == NULL)
+ goto err;
+ } else {
+ if ((frp = file_add(sp, (CHAR_T *)sp->argv[0])) == NULL)
+ goto err;
+ if (F_ISSET(sp, SC_ARGRECOVER))
+ F_SET(frp, FR_RECOVER);
+ }
+
+ if (file_init(sp, frp, NULL, 0))
+ goto err;
+ if (EXCMD_RUNNING(gp)) {
+ (void)ex_cmd(sp);
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
+ if (screen_end(sp))
+ goto err;
+ goto done;
+ }
+ }
+ }
+
+ /*
+ * Check to see if we need to wait for ex. If SC_SCR_EX is set, ex
+ * was forced to initialize the screen during startup. We'd like to
+ * wait for a single character from the user, but we can't because
+ * we're not in raw mode. We can't switch to raw mode because the
+ * vi initialization will switch to xterm's alternate screen, causing
+ * us to lose the messages we're pausing to make sure the user read.
+ * So, wait for a complete line.
+ */
+ if (F_ISSET(sp, SC_SCR_EX)) {
+ p = msg_cmsg(sp, CMSG_CONT_R, &len);
+ (void)write(STDOUT_FILENO, p, len);
+ for (;;) {
+ if (v_event_get(sp, &ev, 0, 0))
+ goto err;
+ if (ev.e_event == E_INTERRUPT ||
+ ev.e_event == E_CHARACTER &&
+ (ev.e_value == K_CR || ev.e_value == K_NL))
+ break;
+ (void)gp->scr_bell(sp);
+ }
+ }
+
+ /* Switch into the right editor, regardless. */
+ F_CLR(sp, SC_EX | SC_VI);
+ F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT);
+
+ /*
+ * Main edit loop. Vi handles split screens itself, we only return
+ * here when switching editor modes or restarting the screen.
+ */
+ while (sp != NULL)
+ if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp))
+ goto err;
+
+done: rval = 0;
+ if (0)
+err: rval = 1;
+
+ /* Clean out the global structure. */
+ v_end(gp);
+
+ return (rval);
+}
+
+/*
+ * v_end --
+ * End the program, discarding screens and most of the global area.
+ *
+ * PUBLIC: void v_end __P((GS *));
+ */
+void
+v_end(gp)
+ GS *gp;
+{
+ MSGS *mp;
+ SCR *sp;
+
+ /* If there are any remaining screens, kill them off. */
+ if (gp->ccl_sp != NULL) {
+ (void)file_end(gp->ccl_sp, NULL, 1);
+ (void)screen_end(gp->ccl_sp);
+ }
+ while ((sp = gp->dq.cqh_first) != (void *)&gp->dq)
+ (void)screen_end(sp);
+ while ((sp = gp->hq.cqh_first) != (void *)&gp->hq)
+ (void)screen_end(sp);
+
+#ifdef HAVE_PERL_INTERP
+ perl_end(gp);
+#endif
+
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ { FREF *frp;
+ /* Free FREF's. */
+ while ((frp = gp->frefq.cqh_first) != (FREF *)&gp->frefq) {
+ CIRCLEQ_REMOVE(&gp->frefq, frp, q);
+ if (frp->name != NULL)
+ free(frp->name);
+ if (frp->tname != NULL)
+ free(frp->tname);
+ free(frp);
+ }
+ }
+
+ /* Free key input queue. */
+ if (gp->i_event != NULL)
+ free(gp->i_event);
+
+ /* Free cut buffers. */
+ cut_close(gp);
+
+ /* Free map sequences. */
+ seq_close(gp);
+
+ /* Free default buffer storage. */
+ (void)text_lfree(&gp->dcb_store.textq);
+
+ /* Close message catalogs. */
+ msg_close(gp);
+#endif
+
+ /* Ring the bell if scheduled. */
+ if (F_ISSET(gp, G_BELLSCHED))
+ (void)fprintf(stderr, "\07"); /* \a */
+
+ /*
+ * Flush any remaining messages. If a message is here, it's almost
+ * certainly the message about the event that killed us (although
+ * it's possible that the user is sourcing a file that exits from the
+ * editor).
+ */
+ while ((mp = gp->msgq.lh_first) != NULL) {
+ (void)fprintf(stderr, "%s%.*s",
+ mp->mtype == M_ERR ? "ex/vi: " : "", (int)mp->len, mp->buf);
+ LIST_REMOVE(mp, q);
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ free(mp->buf);
+ free(mp);
+#endif
+ }
+
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ /* Free any temporary space. */
+ if (gp->tmp_bp != NULL)
+ free(gp->tmp_bp);
+
+#if defined(DEBUG)
+ /* Close debugging file descriptor. */
+ if (gp->tracefp != NULL)
+ (void)fclose(gp->tracefp);
+#endif
+#endif
+}
+
+/*
+ * v_obsolete --
+ * Convert historic arguments into something getopt(3) will like.
+ */
+static int
+v_obsolete(name, argv)
+ char *name, *argv[];
+{
+ size_t len;
+ char *p;
+
+ /*
+ * Translate old style arguments into something getopt will like.
+ * Make sure it's not text space memory, because ex modifies the
+ * strings.
+ * Change "+" into "-c$".
+ * Change "+<anything else>" into "-c<anything else>".
+ * Change "-" into "-s"
+ * The c, T, t and w options take arguments so they can't be
+ * special arguments.
+ *
+ * Stop if we find "--" as an argument, the user may want to edit
+ * a file named "+foo".
+ */
+ while (*++argv && strcmp(argv[0], "--"))
+ if (argv[0][0] == '+') {
+ if (argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 4);
+ if (argv[0] == NULL)
+ goto nomem;
+ (void)strcpy(argv[0], "-c$");
+ } else {
+ p = argv[0];
+ len = strlen(argv[0]);
+ MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
+ if (argv[0] == NULL)
+ goto nomem;
+ argv[0][0] = '-';
+ argv[0][1] = 'c';
+ (void)strcpy(argv[0] + 2, p + 1);
+ }
+ } else if (argv[0][0] == '-')
+ if (argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 3);
+ if (argv[0] == NULL) {
+nomem: v_estr(name, errno, NULL);
+ return (1);
+ }
+ (void)strcpy(argv[0], "-s");
+ } else
+ if ((argv[0][1] == 'c' || argv[0][1] == 'T' ||
+ argv[0][1] == 't' || argv[0][1] == 'w') &&
+ argv[0][2] == '\0')
+ ++argv;
+ return (0);
+}
+
+#ifdef DEBUG
+static void
+attach(gp)
+ GS *gp;
+{
+ int fd;
+ char ch;
+
+ if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
+ v_estr(gp->progname, errno, _PATH_TTY);
+ return;
+ }
+
+ (void)printf("process %lu waiting, enter <CR> to continue: ",
+ (u_long)getpid());
+ (void)fflush(stdout);
+
+ do {
+ if (read(fd, &ch, 1) != 1) {
+ (void)close(fd);
+ return;
+ }
+ } while (ch != '\n' && ch != '\r');
+ (void)close(fd);
+}
+#endif
+
+static void
+v_estr(name, eno, msg)
+ char *name, *msg;
+ int eno;
+{
+ (void)fprintf(stderr, "%s", name);
+ if (msg != NULL)
+ (void)fprintf(stderr, ": %s", msg);
+ if (eno)
+ (void)fprintf(stderr, ": %s", strerror(errno));
+ (void)fprintf(stderr, "\n");
+}
diff --git a/contrib/nvi/common/mark.c b/contrib/nvi/common/mark.c
new file mode 100644
index 000000000000..0ac1fc28bf9c
--- /dev/null
+++ b/contrib/nvi/common/mark.c
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)mark.c 10.13 (Berkeley) 7/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+static LMARK *mark_find __P((SCR *, ARG_CHAR_T));
+
+/*
+ * Marks are maintained in a key sorted doubly linked list. We can't
+ * use arrays because we have no idea how big an index key could be.
+ * The underlying assumption is that users don't have more than, say,
+ * 10 marks at any one time, so this will be is fast enough.
+ *
+ * Marks are fixed, and modifications to the line don't update the mark's
+ * position in the line. This can be hard. If you add text to the line,
+ * place a mark in that text, undo the addition and use ` to move to the
+ * mark, the location will have disappeared. It's tempting to try to adjust
+ * the mark with the changes in the line, but this is hard to do, especially
+ * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
+ * would move to the first non-blank on the line when the mark location was
+ * past the end of the line. This can be complicated by deleting to a mark
+ * that has disappeared using the ` command. Historic vi treated this as
+ * a line-mode motion and deleted the line. This implementation complains to
+ * the user.
+ *
+ * In historic vi, marks returned if the operation was undone, unless the
+ * mark had been subsequently reset. Tricky. This is hard to start with,
+ * but in the presence of repeated undo it gets nasty. When a line is
+ * deleted, we delete (and log) any marks on that line. An undo will create
+ * the mark. Any mark creations are noted as to whether the user created
+ * it or if it was created by an undo. The former cannot be reset by another
+ * undo, but the latter may.
+ *
+ * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
+ * the absolute mark locations sets both, so that "m'" and "m`" work like
+ * they, ah, for lack of a better word, "should".
+ */
+
+/*
+ * mark_init --
+ * Set up the marks.
+ *
+ * PUBLIC: int mark_init __P((SCR *, EXF *));
+ */
+int
+mark_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ *
+ * Set up the marks.
+ */
+ LIST_INIT(&ep->marks);
+ return (0);
+}
+
+/*
+ * mark_end --
+ * Free up the marks.
+ *
+ * PUBLIC: int mark_end __P((SCR *, EXF *));
+ */
+int
+mark_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ LMARK *lmp;
+
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ */
+ while ((lmp = ep->marks.lh_first) != NULL) {
+ LIST_REMOVE(lmp, q);
+ free(lmp);
+ }
+ return (0);
+}
+
+/*
+ * mark_get --
+ * Get the location referenced by a mark.
+ *
+ * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
+ */
+int
+mark_get(sp, key, mp, mtype)
+ SCR *sp;
+ ARG_CHAR_T key;
+ MARK *mp;
+ mtype_t mtype;
+{
+ LMARK *lmp;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ lmp = mark_find(sp, key);
+ if (lmp == NULL || lmp->name != key) {
+ msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
+ return (1);
+ }
+ if (F_ISSET(lmp, MARK_DELETED)) {
+ msgq(sp, mtype,
+ "018|Mark %s: the line was deleted", KEY_NAME(sp, key));
+ return (1);
+ }
+
+ /*
+ * !!!
+ * The absolute mark is initialized to lno 1/cno 0, and historically
+ * you could use it in an empty file. Make such a mark always work.
+ */
+ if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
+ msgq(sp, mtype,
+ "019|Mark %s: cursor position no longer exists",
+ KEY_NAME(sp, key));
+ return (1);
+ }
+ mp->lno = lmp->lno;
+ mp->cno = lmp->cno;
+ return (0);
+}
+
+/*
+ * mark_set --
+ * Set the location referenced by a mark.
+ *
+ * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
+ */
+int
+mark_set(sp, key, value, userset)
+ SCR *sp;
+ ARG_CHAR_T key;
+ MARK *value;
+ int userset;
+{
+ LMARK *lmp, *lmt;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ /*
+ * The rules are simple. If the user is setting a mark (if it's a
+ * new mark this is always true), it always happens. If not, it's
+ * an undo, and we set it if it's not already set or if it was set
+ * by a previous undo.
+ */
+ lmp = mark_find(sp, key);
+ if (lmp == NULL || lmp->name != key) {
+ MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
+ if (lmp == NULL) {
+ LIST_INSERT_HEAD(&sp->ep->marks, lmt, q);
+ } else
+ LIST_INSERT_AFTER(lmp, lmt, q);
+ lmp = lmt;
+ } else if (!userset &&
+ !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
+ return (0);
+
+ lmp->lno = value->lno;
+ lmp->cno = value->cno;
+ lmp->name = key;
+ lmp->flags = userset ? MARK_USERSET : 0;
+ return (0);
+}
+
+/*
+ * mark_find --
+ * Find the requested mark, or, the slot immediately before
+ * where it would go.
+ */
+static LMARK *
+mark_find(sp, key)
+ SCR *sp;
+ ARG_CHAR_T key;
+{
+ LMARK *lmp, *lastlmp;
+
+ /*
+ * Return the requested mark or the slot immediately before
+ * where it should go.
+ */
+ for (lastlmp = NULL, lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lastlmp = lmp, lmp = lmp->q.le_next)
+ if (lmp->name >= key)
+ return (lmp->name == key ? lmp : lastlmp);
+ return (lastlmp);
+}
+
+/*
+ * mark_insdel --
+ * Update the marks based on an insertion or deletion.
+ *
+ * PUBLIC: int mark_insdel __P((SCR *, lnop_t, recno_t));
+ */
+int
+mark_insdel(sp, op, lno)
+ SCR *sp;
+ lnop_t op;
+ recno_t lno;
+{
+ LMARK *lmp;
+ recno_t lline;
+
+ switch (op) {
+ case LINE_APPEND:
+ /* All insert/append operations are done as inserts. */
+ abort();
+ case LINE_DELETE:
+ for (lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->lno >= lno)
+ if (lmp->lno == lno) {
+ F_SET(lmp, MARK_DELETED);
+ (void)log_mark(sp, lmp);
+ } else
+ --lmp->lno;
+ break;
+ case LINE_INSERT:
+ /*
+ * XXX
+ * Very nasty special case. If the file was empty, then we're
+ * adding the first line, which is a replacement. So, we don't
+ * modify the marks. This is a hack to make:
+ *
+ * mz:r!echo foo<carriage-return>'z
+ *
+ * work, i.e. historically you could mark the "line" in an empty
+ * file and replace it, and continue to use the mark. Insane,
+ * well, yes, I know, but someone complained.
+ *
+ * Check for line #2 before going to the end of the file.
+ */
+ if (!db_exist(sp, 2)) {
+ if (db_last(sp, &lline))
+ return (1);
+ if (lline == 1)
+ return (0);
+ }
+
+ for (lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->lno >= lno)
+ ++lmp->lno;
+ break;
+ case LINE_RESET:
+ break;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/common/mark.h b/contrib/nvi/common/mark.h
new file mode 100644
index 000000000000..9c63e183e83f
--- /dev/null
+++ b/contrib/nvi/common/mark.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)mark.h 10.3 (Berkeley) 3/6/96
+ */
+
+/*
+ * The MARK and LMARK structures define positions in the file. There are
+ * two structures because the mark subroutines are the only places where
+ * anything cares about something other than line and column.
+ *
+ * Because of the different interfaces used by the db(3) package, curses,
+ * and users, the line number is 1 based and the column number is 0 based.
+ * Additionally, it is known that the out-of-band line number is less than
+ * any legal line number. The line number is of type recno_t, as that's
+ * the underlying type of the database. The column number is of type size_t,
+ * guaranteeing that we can malloc a line.
+ */
+struct _mark {
+#define OOBLNO 0 /* Out-of-band line number. */
+ recno_t lno; /* Line number. */
+ size_t cno; /* Column number. */
+};
+
+struct _lmark {
+ LIST_ENTRY(_lmark) q; /* Linked list of marks. */
+ 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_int8_t flags;
+};
+
+#define ABSMARK1 '\'' /* Absolute mark name. */
+#define ABSMARK2 '`' /* Absolute mark name. */
diff --git a/contrib/nvi/common/mem.h b/contrib/nvi/common/mem.h
new file mode 100644
index 000000000000..af42e6bcd1de
--- /dev/null
+++ b/contrib/nvi/common/mem.h
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)mem.h 10.7 (Berkeley) 3/30/96
+ */
+
+/* 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) { \
+ void *L__bincp; \
+ if ((nlen) > llen) { \
+ if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
+ goto alloc_err; \
+ /* \
+ * !!! \
+ * Possible pointer conversion. \
+ */ \
+ lp = L__bincp; \
+ } \
+}
+#define BINC_RET(sp, lp, llen, nlen) { \
+ void *L__bincp; \
+ if ((nlen) > llen) { \
+ if ((L__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
+ return (1); \
+ /* \
+ * !!! \
+ * Possible pointer conversion. \
+ */ \
+ lp = L__bincp; \
+ } \
+}
+
+/*
+ * 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 *L__gp = (sp) == NULL ? NULL : (sp)->gp; \
+ if (L__gp == NULL || F_ISSET(L__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_GOTO(sp, bp, blen, nlen); \
+ } else { \
+ BINC_GOTO(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen); \
+ bp = L__gp->tmp_bp; \
+ blen = L__gp->tmp_blen; \
+ F_SET(L__gp, G_TMP_INUSE); \
+ } \
+}
+#define GET_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *L__gp = (sp) == NULL ? NULL : (sp)->gp; \
+ if (L__gp == NULL || F_ISSET(L__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_RET(sp, bp, blen, nlen); \
+ } else { \
+ BINC_RET(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen); \
+ bp = L__gp->tmp_bp; \
+ blen = L__gp->tmp_blen; \
+ F_SET(L__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 *L__gp = (sp) == NULL ? NULL : (sp)->gp; \
+ if (L__gp == NULL || bp == L__gp->tmp_bp) { \
+ F_CLR(L__gp, G_TMP_INUSE); \
+ BINC_GOTO(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen); \
+ bp = L__gp->tmp_bp; \
+ blen = L__gp->tmp_blen; \
+ F_SET(L__gp, G_TMP_INUSE); \
+ } else \
+ BINC_GOTO(sp, bp, blen, nlen); \
+}
+#define ADD_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *L__gp = (sp) == NULL ? NULL : (sp)->gp; \
+ if (L__gp == NULL || bp == L__gp->tmp_bp) { \
+ F_CLR(L__gp, G_TMP_INUSE); \
+ BINC_RET(sp, L__gp->tmp_bp, L__gp->tmp_blen, nlen); \
+ bp = L__gp->tmp_bp; \
+ blen = L__gp->tmp_blen; \
+ F_SET(L__gp, G_TMP_INUSE); \
+ } else \
+ BINC_RET(sp, bp, blen, nlen); \
+}
+
+/* Free a GET_SPACE returned buffer. */
+#define FREE_SPACE(sp, bp, blen) { \
+ GS *L__gp = (sp) == NULL ? NULL : (sp)->gp; \
+ if (L__gp != NULL && bp == L__gp->tmp_bp) \
+ F_CLR(L__gp, G_TMP_INUSE); \
+ else \
+ free(bp); \
+}
+
+/*
+ * Malloc a buffer, casting the return pointer. Various versions.
+ *
+ * !!!
+ * The cast should be unnecessary, malloc(3) and friends return void *'s,
+ * which is all we need. However, some systems that nvi needs to run on
+ * don't do it right yet, resulting in the compiler printing out roughly
+ * a million warnings. After awhile, it seemed easier to put the casts
+ * in instead of explaining it all the time.
+ */
+#define CALLOC(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define CALLOC_GOTO(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) \
+ goto alloc_err; \
+}
+#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \
+ p = (cast)calloc(nmemb, size); \
+}
+#define CALLOC_RET(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+
+#define MALLOC(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define MALLOC_GOTO(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) \
+ goto alloc_err; \
+}
+#define MALLOC_NOMSG(sp, p, cast, size) { \
+ p = (cast)malloc(size); \
+}
+#define MALLOC_RET(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+/*
+ * XXX
+ * Don't depend on realloc(NULL, size) working.
+ */
+#define REALLOC(sp, p, cast, size) { \
+ if ((p = (cast)(p == NULL ? \
+ malloc(size) : realloc(p, size))) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+
+/*
+ * Versions of memmove(3) and memset(3) that use the size of the
+ * initial pointer to figure out how much memory to manipulate.
+ */
+#define MEMMOVE(p, t, len) memmove(p, t, (len) * sizeof(*(p)))
+#define MEMSET(p, value, len) memset(p, value, (len) * sizeof(*(p)))
diff --git a/contrib/nvi/common/msg.c b/contrib/nvi/common/msg.c
new file mode 100644
index 000000000000..2b18082c7ab8
--- /dev/null
+++ b/contrib/nvi/common/msg.c
@@ -0,0 +1,895 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)msg.c 10.48 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "common.h"
+#include "../vi/vi.h"
+
+/*
+ * msgq --
+ * Display a message.
+ *
+ * PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...));
+ */
+void
+#ifdef __STDC__
+msgq(SCR *sp, mtype_t mt, const char *fmt, ...)
+#else
+msgq(sp, mt, fmt, va_alist)
+ SCR *sp;
+ mtype_t mt;
+ const char *fmt;
+ va_dcl
+#endif
+{
+#ifndef NL_ARGMAX
+#define __NL_ARGMAX 20 /* Set to 9 by System V. */
+ struct {
+ const char *str; /* String pointer. */
+ size_t arg; /* Argument number. */
+ size_t prefix; /* Prefix string length. */
+ size_t skip; /* Skipped string length. */
+ size_t suffix; /* Suffix string length. */
+ } str[__NL_ARGMAX];
+#endif
+ static int reenter; /* STATIC: Re-entrancy check. */
+ CHAR_T ch;
+ GS *gp;
+ size_t blen, cnt1, cnt2, len, mlen, nlen, soff;
+ const char *p, *t, *u;
+ char *bp, *mp, *rbp, *s_rbp;
+ va_list ap;
+
+ /*
+ * !!!
+ * It's possible to enter msg when there's no screen to hold the
+ * message. If sp is NULL, ignore the special cases and put the
+ * message out to stderr.
+ */
+ if (sp == NULL) {
+ gp = NULL;
+ if (mt == M_BERR)
+ mt = M_ERR;
+ else if (mt == M_VINFO)
+ mt = M_INFO;
+ } else {
+ gp = sp->gp;
+ switch (mt) {
+ case M_BERR:
+ if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) {
+ F_SET(gp, G_BELLSCHED);
+ return;
+ }
+ mt = M_ERR;
+ break;
+ case M_VINFO:
+ if (!O_ISSET(sp, O_VERBOSE))
+ return;
+ mt = M_INFO;
+ /* FALLTHROUGH */
+ case M_INFO:
+ if (F_ISSET(sp, SC_EX_SILENT))
+ return;
+ break;
+ case M_ERR:
+ case M_SYSERR:
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * It's possible to reenter msg when it allocates space. We're
+ * probably dead anyway, but there's no reason to drop core.
+ *
+ * XXX
+ * Yes, there's a race, but it should only be two instructions.
+ */
+ if (reenter++)
+ return;
+
+ /* Get space for the message. */
+ nlen = 1024;
+ if (0) {
+retry: FREE_SPACE(sp, bp, blen);
+ nlen *= 2;
+ }
+ bp = NULL;
+ blen = 0;
+ GET_SPACE_GOTO(sp, bp, blen, nlen);
+
+ /*
+ * Error prefix.
+ *
+ * mp: pointer to the current next character to be written
+ * mlen: length of the already written characters
+ * blen: total length of the buffer
+ */
+#define REM (blen - mlen)
+ mp = bp;
+ mlen = 0;
+ if (mt == M_SYSERR) {
+ p = msg_cat(sp, "020|Error: ", &len);
+ if (REM < len)
+ goto retry;
+ memcpy(mp, p, len);
+ mp += len;
+ mlen += len;
+ }
+
+ /*
+ * If we're running an ex command that the user didn't enter, display
+ * the file name and line number prefix.
+ */
+ if ((mt == M_ERR || mt == M_SYSERR) &&
+ sp != NULL && gp != NULL && gp->if_name != NULL) {
+ for (p = gp->if_name; *p != '\0'; ++p) {
+ len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p));
+ mp += len;
+ if ((mlen += len) > blen)
+ goto retry;
+ }
+ len = snprintf(mp, REM, ", %d: ", gp->if_lno);
+ mp += len;
+ if ((mlen += len) > blen)
+ goto retry;
+ }
+
+ /* If nothing to format, we're done. */
+ if (fmt == NULL)
+ goto nofmt;
+ fmt = msg_cat(sp, fmt, NULL);
+
+#ifndef NL_ARGMAX
+ /*
+ * Nvi should run on machines that don't support the numbered argument
+ * specifications (%[digit]*$). We do this by reformatting the string
+ * so that we can hand it to vsprintf(3) and it will use the arguments
+ * in the right order. When vsprintf returns, we put the string back
+ * into the right order. It's undefined, according to SVID III, to mix
+ * numbered argument specifications with the standard style arguments,
+ * so this should be safe.
+ *
+ * In addition, we also need a character that is known to not occur in
+ * any vi message, for separating the parts of the string. As callers
+ * of msgq are responsible for making sure that all the non-printable
+ * characters are formatted for printing before calling msgq, we use a
+ * random non-printable character selected at terminal initialization
+ * time. This code isn't fast by any means, but as messages should be
+ * relatively short and normally have only a few arguments, it won't be
+ * too bad. Regardless, nobody has come up with any other solution.
+ *
+ * The result of this loop is an array of pointers into the message
+ * string, with associated lengths and argument numbers. The array
+ * is in the "correct" order, and the arg field contains the argument
+ * order.
+ */
+ for (p = fmt, soff = 0; soff < __NL_ARGMAX;) {
+ for (t = p; *p != '\0' && *p != '%'; ++p);
+ if (*p == '\0')
+ break;
+ ++p;
+ if (!isdigit(*p)) {
+ if (*p == '%')
+ ++p;
+ continue;
+ }
+ for (u = p; *++p != '\0' && isdigit(*p););
+ if (*p != '$')
+ continue;
+
+ /* Up to, and including the % character. */
+ str[soff].str = t;
+ str[soff].prefix = u - t;
+
+ /* Up to, and including the $ character. */
+ str[soff].arg = atoi(u);
+ str[soff].skip = (p - u) + 1;
+ if (str[soff].arg >= __NL_ARGMAX)
+ goto ret;
+
+ /* Up to, and including the conversion character. */
+ for (u = p; (ch = *++p) != '\0';)
+ if (isalpha(ch) &&
+ strchr("diouxXfeEgGcspn", ch) != NULL)
+ break;
+ str[soff].suffix = p - u;
+ if (ch != '\0')
+ ++p;
+ ++soff;
+ }
+
+ /* If no magic strings, we're done. */
+ if (soff == 0)
+ goto format;
+
+ /* Get space for the reordered strings. */
+ if ((rbp = malloc(nlen)) == NULL)
+ goto ret;
+ s_rbp = rbp;
+
+ /*
+ * Reorder the strings into the message string based on argument
+ * order.
+ *
+ * !!!
+ * We ignore arguments that are out of order, i.e. if we don't find
+ * an argument, we continue. Assume (almost certainly incorrectly)
+ * that whoever created the string knew what they were doing.
+ *
+ * !!!
+ * Brute force "sort", but since we don't expect more than one or two
+ * arguments in a string, the setup cost of a fast sort will be more
+ * expensive than the loop.
+ */
+ for (cnt1 = 1; cnt1 <= soff; ++cnt1)
+ for (cnt2 = 0; cnt2 < soff; ++cnt2)
+ if (cnt1 == str[cnt2].arg) {
+ memmove(s_rbp, str[cnt2].str, str[cnt2].prefix);
+ memmove(s_rbp + str[cnt2].prefix,
+ str[cnt2].str + str[cnt2].prefix +
+ str[cnt2].skip, str[cnt2].suffix);
+ s_rbp += str[cnt2].prefix + str[cnt2].suffix;
+ *s_rbp++ =
+ gp == NULL ? DEFAULT_NOPRINT : gp->noprint;
+ break;
+ }
+ *s_rbp = '\0';
+ fmt = rbp;
+#endif
+
+format: /* Format the arguments into the string. */
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ len = vsnprintf(mp, REM, fmt, ap);
+ va_end(ap);
+ if (len >= nlen)
+ goto retry;
+
+#ifndef NL_ARGMAX
+ if (soff == 0)
+ goto nofmt;
+
+ /*
+ * Go through the resulting string, and, for each separator character
+ * separated string, enter its new starting position and length in the
+ * array.
+ */
+ for (p = t = mp, cnt1 = 1,
+ ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p)
+ if (*p == ch) {
+ for (cnt2 = 0; cnt2 < soff; ++cnt2)
+ if (str[cnt2].arg == cnt1)
+ break;
+ str[cnt2].str = t;
+ str[cnt2].prefix = p - t;
+ t = p + 1;
+ ++cnt1;
+ }
+
+ /*
+ * Reorder the strings once again, putting them back into the
+ * message buffer.
+ *
+ * !!!
+ * Note, the length of the message gets decremented once for
+ * each substring, when we discard the separator character.
+ */
+ for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) {
+ memmove(rbp, str[cnt1].str, str[cnt1].prefix);
+ rbp += str[cnt1].prefix;
+ --len;
+ }
+ memmove(mp, s_rbp, rbp - s_rbp);
+
+ /* Free the reordered string memory. */
+ free(s_rbp);
+#endif
+
+nofmt: mp += len;
+ if ((mlen += len) > blen)
+ goto retry;
+ if (mt == M_SYSERR) {
+ len = snprintf(mp, REM, ": %s", strerror(errno));
+ mp += len;
+ if ((mlen += len) > blen)
+ goto retry;
+ mt = M_ERR;
+ }
+
+ /* Add trailing newline. */
+ if ((mlen += 1) > blen)
+ goto retry;
+ *mp = '\n';
+
+ if (sp != NULL)
+ (void)ex_fflush(sp);
+ if (gp != NULL)
+ gp->scr_msg(sp, mt, bp, mlen);
+ else
+ (void)fprintf(stderr, "%.*s", (int)mlen, bp);
+
+ /* Cleanup. */
+ret: FREE_SPACE(sp, bp, blen);
+alloc_err:
+ reenter = 0;
+}
+
+/*
+ * msgq_str --
+ * Display a message with an embedded string.
+ *
+ * PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *));
+ */
+void
+msgq_str(sp, mtype, str, fmt)
+ SCR *sp;
+ mtype_t mtype;
+ char *str, *fmt;
+{
+ int nf, sv_errno;
+ char *p;
+
+ if (str == NULL) {
+ msgq(sp, mtype, fmt);
+ return;
+ }
+
+ sv_errno = errno;
+ p = msg_print(sp, str, &nf);
+ errno = sv_errno;
+ msgq(sp, mtype, fmt, p);
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+}
+
+/*
+ * mod_rpt --
+ * Report on the lines that changed.
+ *
+ * !!!
+ * Historic vi documentation (USD:15-8) claimed that "The editor will also
+ * always tell you when a change you make affects text which you cannot see."
+ * This wasn't true -- edit a large file and do "100d|1". We don't implement
+ * this semantic since it requires tracking each line that changes during a
+ * command instead of just keeping count.
+ *
+ * Line counts weren't right in historic vi, either. For example, given the
+ * file:
+ * abc
+ * def
+ * the command 2d}, from the 'b' would report that two lines were deleted,
+ * not one.
+ *
+ * PUBLIC: void mod_rpt __P((SCR *));
+ */
+void
+mod_rpt(sp)
+ SCR *sp;
+{
+ static char * const action[] = {
+ "293|added",
+ "294|changed",
+ "295|deleted",
+ "296|joined",
+ "297|moved",
+ "298|shifted",
+ "299|yanked",
+ };
+ static char * const lines[] = {
+ "300|line",
+ "301|lines",
+ };
+ recno_t total;
+ u_long rptval;
+ int first, cnt;
+ size_t blen, len, tlen;
+ const char *t;
+ char * const *ap;
+ char *bp, *p;
+
+ /* Change reports are turned off in batch mode. */
+ if (F_ISSET(sp, SC_EX_SILENT))
+ return;
+
+ /* Reset changing line number. */
+ sp->rptlchange = OOBLNO;
+
+ /*
+ * Don't build a message if not enough changed.
+ *
+ * !!!
+ * And now, a vi clone test. Historically, vi reported if the number
+ * of changed lines was > than the value, not >=, unless it was a yank
+ * command, which used >=. No lie. Furthermore, an action was never
+ * reported for a single line action. This is consistent for actions
+ * other than yank, but yank didn't report single line actions even if
+ * the report edit option was set to 1. In addition, setting report to
+ * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an
+ * unknown reason (this bug was fixed in System III/V at some point).
+ * I got complaints, so nvi conforms to System III/V historic practice
+ * except that we report a yank of 1 line if report is set to 1.
+ */
+#define ARSIZE(a) sizeof(a) / sizeof (*a)
+#define MAXNUM 25
+ rptval = O_VAL(sp, O_REPORT);
+ for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt)
+ total += sp->rptlines[cnt];
+ if (total == 0)
+ return;
+ if (total <= rptval && sp->rptlines[L_YANKED] < rptval) {
+ for (cnt = 0; cnt < ARSIZE(action); ++cnt)
+ sp->rptlines[cnt] = 0;
+ return;
+ }
+
+ /* Build and display the message. */
+ GET_SPACE_GOTO(sp, bp, blen, sizeof(action) * MAXNUM + 1);
+ for (p = bp, first = 1, tlen = 0,
+ ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt)
+ if (sp->rptlines[cnt] != 0) {
+ if (first)
+ first = 0;
+ else {
+ *p++ = ';';
+ *p++ = ' ';
+ tlen += 2;
+ }
+ len = snprintf(p, MAXNUM, "%lu ", sp->rptlines[cnt]);
+ p += len;
+ tlen += len;
+ t = msg_cat(sp,
+ lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len);
+ memcpy(p, t, len);
+ p += len;
+ tlen += len;
+ *p++ = ' ';
+ ++tlen;
+ t = msg_cat(sp, *ap, &len);
+ memcpy(p, t, len);
+ p += len;
+ tlen += len;
+ sp->rptlines[cnt] = 0;
+ }
+
+ /* Add trailing newline. */
+ *p = '\n';
+ ++tlen;
+
+ (void)ex_fflush(sp);
+ sp->gp->scr_msg(sp, M_INFO, bp, tlen);
+
+ FREE_SPACE(sp, bp, blen);
+alloc_err:
+ return;
+
+#undef ARSIZE
+#undef MAXNUM
+}
+
+/*
+ * msgq_status --
+ * Report on the file's status.
+ *
+ * PUBLIC: void msgq_status __P((SCR *, recno_t, u_int));
+ */
+void
+msgq_status(sp, lno, flags)
+ SCR *sp;
+ recno_t lno;
+ u_int flags;
+{
+ static int poisoned;
+ recno_t last;
+ size_t blen, len;
+ int cnt, needsep;
+ const char *t;
+ char **ap, *bp, *np, *p, *s;
+
+ /* Get sufficient memory. */
+ len = strlen(sp->frp->name);
+ GET_SPACE_GOTO(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
+ p = bp;
+
+ /* Copy in the filename. */
+ for (p = bp, t = sp->frp->name; *t != '\0'; ++t) {
+ len = KEY_LEN(sp, *t);
+ memcpy(p, KEY_NAME(sp, *t), len);
+ p += len;
+ }
+ np = p;
+ *p++ = ':';
+ *p++ = ' ';
+
+ /* Copy in the argument count. */
+ if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
+ for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
+ if (cnt > 1) {
+ (void)sprintf(p,
+ msg_cat(sp, "317|%d files to edit", NULL), cnt);
+ p += strlen(p);
+ *p++ = ':';
+ *p++ = ' ';
+ }
+ F_CLR(sp, SC_STATUS_CNT);
+ }
+
+ /*
+ * See nvi/exf.c:file_init() for a description of how and when the
+ * read-only bit is set.
+ *
+ * !!!
+ * The historic display for "name changed" was "[Not edited]".
+ */
+ needsep = 0;
+ if (F_ISSET(sp->frp, FR_NEWFILE)) {
+ F_CLR(sp->frp, FR_NEWFILE);
+ t = msg_cat(sp, "021|new file", &len);
+ memcpy(p, t, len);
+ p += len;
+ needsep = 1;
+ } else {
+ if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
+ t = msg_cat(sp, "022|name changed", &len);
+ memcpy(p, t, len);
+ p += len;
+ needsep = 1;
+ }
+ if (needsep) {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+ if (F_ISSET(sp->ep, F_MODIFIED))
+ t = msg_cat(sp, "023|modified", &len);
+ else
+ t = msg_cat(sp, "024|unmodified", &len);
+ memcpy(p, t, len);
+ p += len;
+ needsep = 1;
+ }
+ if (F_ISSET(sp->frp, FR_UNLOCKED)) {
+ if (needsep) {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+ t = msg_cat(sp, "025|UNLOCKED", &len);
+ memcpy(p, t, len);
+ p += len;
+ needsep = 1;
+ }
+ if (O_ISSET(sp, O_READONLY)) {
+ if (needsep) {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+ t = msg_cat(sp, "026|readonly", &len);
+ memcpy(p, t, len);
+ p += len;
+ needsep = 1;
+ }
+ if (needsep) {
+ *p++ = ':';
+ *p++ = ' ';
+ }
+ if (LF_ISSET(MSTAT_SHOWLAST)) {
+ if (db_last(sp, &last))
+ return;
+ if (last == 0) {
+ t = msg_cat(sp, "028|empty file", &len);
+ memcpy(p, t, len);
+ p += len;
+ } else {
+ t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
+ (void)sprintf(p, t, lno, last, (lno * 100) / last);
+ p += strlen(p);
+ }
+ } else {
+ t = msg_cat(sp, "029|line %lu", &len);
+ (void)sprintf(p, t, lno);
+ p += strlen(p);
+ }
+#ifdef DEBUG
+ (void)sprintf(p, " (pid %lu)", (u_long)getpid());
+ p += strlen(p);
+#endif
+ *p++ = '\n';
+ len = p - bp;
+
+ /*
+ * There's a nasty problem with long path names. Cscope and tags files
+ * can result in long paths and vi will request a continuation key from
+ * the user as soon as it starts the screen. Unfortunately, the user
+ * has already typed ahead, and chaos results. If we assume that the
+ * characters in the filenames and informational messages only take a
+ * single screen column each, we can trim the filename.
+ *
+ * XXX
+ * Status lines get put up at fairly awkward times. For example, when
+ * you do a filter read (e.g., :read ! echo foo) in the top screen of a
+ * split screen, we have to repaint the status lines for all the screens
+ * below the top screen. We don't want users having to enter continue
+ * characters for those screens. Make it really hard to screw this up.
+ */
+ s = bp;
+ if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
+ for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
+ if (s == np) {
+ s = p - (sp->cols - 5);
+ *--s = ' ';
+ }
+ *--s = '.';
+ *--s = '.';
+ *--s = '.';
+ len = p - s;
+ }
+
+ /* Flush any waiting ex messages. */
+ (void)ex_fflush(sp);
+
+ sp->gp->scr_msg(sp, M_INFO, s, len);
+
+ FREE_SPACE(sp, bp, blen);
+alloc_err:
+ return;
+}
+
+/*
+ * msg_open --
+ * Open the message catalogs.
+ *
+ * PUBLIC: int msg_open __P((SCR *, char *));
+ */
+int
+msg_open(sp, file)
+ SCR *sp;
+ char *file;
+{
+ /*
+ * !!!
+ * Assume that the first file opened is the system default, and that
+ * all subsequent ones user defined. Only display error messages
+ * if we can't open the user defined ones -- it's useful to know if
+ * the system one wasn't there, but if nvi is being shipped with an
+ * installed system, the file will be there, if it's not, then the
+ * message will be repeated every time nvi is started up.
+ */
+ static int first = 1;
+ DB *db;
+ DBT data, key;
+ recno_t msgno;
+ char *p, *t, buf[MAXPATHLEN];
+
+ if ((p = strrchr(file, '/')) != NULL && p[1] == '\0' &&
+ ((t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0' ||
+ (t = getenv("LANG")) != NULL && t[0] != '\0')) {
+ (void)snprintf(buf, sizeof(buf), "%s%s", file, t);
+ p = buf;
+ } else
+ p = file;
+ if ((db = dbopen(p,
+ O_NONBLOCK | O_RDONLY, 0, DB_RECNO, NULL)) == NULL) {
+ if (first) {
+ first = 0;
+ return (1);
+ }
+ msgq_str(sp, M_SYSERR, p, "%s");
+ return (1);
+ }
+
+ /*
+ * Test record 1 for the magic string. The msgq call is here so
+ * the message catalog build finds it.
+ */
+#define VMC "VI_MESSAGE_CATALOG"
+ key.data = &msgno;
+ key.size = sizeof(recno_t);
+ msgno = 1;
+ if (db->get(db, &key, &data, 0) != 0 ||
+ data.size != sizeof(VMC) - 1 ||
+ memcmp(data.data, VMC, sizeof(VMC) - 1)) {
+ (void)db->close(db);
+ if (first) {
+ first = 0;
+ return (1);
+ }
+ msgq_str(sp, M_ERR, p,
+ "030|The file %s is not a message catalog");
+ return (1);
+ }
+ first = 0;
+
+ if (sp->gp->msg != NULL)
+ (void)sp->gp->msg->close(sp->gp->msg);
+ sp->gp->msg = db;
+ return (0);
+}
+
+/*
+ * msg_close --
+ * Close the message catalogs.
+ *
+ * PUBLIC: void msg_close __P((GS *));
+ */
+void
+msg_close(gp)
+ GS *gp;
+{
+ if (gp->msg != NULL)
+ (void)gp->msg->close(gp->msg);
+}
+
+/*
+ * msg_cont --
+ * Return common continuation messages.
+ *
+ * PUBLIC: const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
+ */
+const char *
+msg_cmsg(sp, which, lenp)
+ SCR *sp;
+ cmsg_t which;
+ size_t *lenp;
+{
+ switch (which) {
+ case CMSG_CONF:
+ return (msg_cat(sp, "268|confirm? [ynq]", lenp));
+ case CMSG_CONT:
+ return (msg_cat(sp, "269|Press any key to continue: ", lenp));
+ case CMSG_CONT_EX:
+ return (msg_cat(sp,
+ "270|Press any key to continue [: to enter more ex commands]: ",
+ lenp));
+ case CMSG_CONT_R:
+ return (msg_cat(sp, "161|Press Enter to continue: ", lenp));
+ case CMSG_CONT_S:
+ return (msg_cat(sp, "275| cont?", lenp));
+ case CMSG_CONT_Q:
+ return (msg_cat(sp,
+ "271|Press any key to continue [q to quit]: ", lenp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * msg_cat --
+ * Return a single message from the catalog, plus its length.
+ *
+ * !!!
+ * Only a single catalog message can be accessed at a time, if multiple
+ * ones are needed, they must be copied into local memory.
+ *
+ * PUBLIC: const char *msg_cat __P((SCR *, const char *, size_t *));
+ */
+const char *
+msg_cat(sp, str, lenp)
+ SCR *sp;
+ const char *str;
+ size_t *lenp;
+{
+ GS *gp;
+ DBT data, key;
+ recno_t msgno;
+
+ /*
+ * If it's not a catalog message, i.e. has doesn't have a leading
+ * number and '|' symbol, we're done.
+ */
+ if (isdigit(str[0]) &&
+ isdigit(str[1]) && isdigit(str[2]) && str[3] == '|') {
+ key.data = &msgno;
+ key.size = sizeof(recno_t);
+ msgno = atoi(str);
+
+ /*
+ * XXX
+ * Really sleazy hack -- we put an extra character on the
+ * end of the format string, and then we change it to be
+ * the nul termination of the string. There ought to be
+ * a better way. Once we can allocate multiple temporary
+ * memory buffers, maybe we can use one of them instead.
+ */
+ gp = sp == NULL ? NULL : sp->gp;
+ if (gp != NULL && gp->msg != NULL &&
+ gp->msg->get(gp->msg, &key, &data, 0) == 0 &&
+ data.size != 0) {
+ if (lenp != NULL)
+ *lenp = data.size - 1;
+ ((char *)data.data)[data.size - 1] = '\0';
+ return (data.data);
+ }
+ str = &str[4];
+ }
+ if (lenp != NULL)
+ *lenp = strlen(str);
+ return (str);
+}
+
+/*
+ * msg_print --
+ * Return a printable version of a string, in allocated memory.
+ *
+ * PUBLIC: char *msg_print __P((SCR *, const char *, int *));
+ */
+char *
+msg_print(sp, s, needfree)
+ SCR *sp;
+ const char *s;
+ int *needfree;
+{
+ size_t blen, nlen;
+ const char *cp;
+ char *bp, *ep, *p, *t;
+
+ *needfree = 0;
+
+ for (cp = s; *cp != '\0'; ++cp)
+ if (!isprint(*cp))
+ break;
+ if (*cp == '\0')
+ return ((char *)s); /* SAFE: needfree set to 0. */
+
+ nlen = 0;
+ if (0) {
+retry: if (sp == NULL)
+ free(bp);
+ else
+ FREE_SPACE(sp, bp, blen);
+ needfree = 0;
+ }
+ nlen += 256;
+ if (sp == NULL) {
+ if ((bp = malloc(nlen)) == NULL)
+ goto alloc_err;
+ } else
+ GET_SPACE_GOTO(sp, bp, blen, nlen);
+ if (0) {
+alloc_err: return ("");
+ }
+ *needfree = 1;
+
+ for (p = bp, ep = (bp + blen) - 1, cp = s; *cp != '\0' && p < ep; ++cp)
+ for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++);
+ if (p == ep)
+ goto retry;
+ *p = '\0';
+ return (bp);
+}
diff --git a/contrib/nvi/common/msg.h b/contrib/nvi/common/msg.h
new file mode 100644
index 000000000000..b10f4ccae5c6
--- /dev/null
+++ b/contrib/nvi/common/msg.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)msg.h 10.10 (Berkeley) 5/10/96
+ */
+
+/*
+ * Common messages (continuation or confirmation).
+ */
+typedef enum {
+ CMSG_CONF, CMSG_CONT, CMSG_CONT_EX,
+ CMSG_CONT_R, CMSG_CONT_S, CMSG_CONT_Q } cmsg_t;
+
+/*
+ * Message types.
+ *
+ * !!!
+ * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the error
+ * messages shorter. In this implementation, O_TERSE has no effect and
+ * O_VERBOSE results in informational displays about common errors, for
+ * naive users.
+ *
+ * M_NONE Display to the user, no reformatting, no nothing.
+ *
+ * M_BERR Error: M_ERR if O_VERBOSE, else bell.
+ * M_ERR Error: Display in inverse video.
+ * M_INFO Info: Display in normal video.
+ * M_SYSERR Error: M_ERR, using strerror(3) message.
+ * M_VINFO Info: M_INFO if O_VERBOSE, else ignore.
+ *
+ * The underlying message display routines only need to know about M_NONE,
+ * M_ERR and M_INFO -- all the other message types are converted into one
+ * of them by the message routines.
+ */
+typedef enum {
+ M_NONE = 1, M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO } mtype_t;
+
+/*
+ * There are major problems with error messages being generated by routines
+ * preparing the screen to display error messages. It's possible for the
+ * editor to generate messages before we have a screen in which to display
+ * them, or during the transition between ex (and vi startup) and a true vi.
+ * There's a queue in the global area to hold them.
+ *
+ * If SC_EX/SC_VI is set, that's the mode that the editor is in. If the flag
+ * S_SCREEN_READY is set, that means that the screen is prepared to display
+ * messages.
+ */
+typedef struct _msgh MSGH; /* MSGS list head structure. */
+LIST_HEAD(_msgh, _msg);
+struct _msg {
+ LIST_ENTRY(_msg) q; /* Linked list of messages. */
+ mtype_t mtype; /* Message type: M_NONE, M_ERR, M_INFO. */
+ char *buf; /* Message buffer. */
+ size_t len; /* Message length. */
+};
+
+/* Flags to msgq_status(). */
+#define MSTAT_SHOWLAST 0x01 /* Show the line number of the last line. */
+#define MSTAT_TRUNCATE 0x02 /* Truncate the file name if it's too long. */
diff --git a/contrib/nvi/common/options.awk b/contrib/nvi/common/options.awk
new file mode 100644
index 000000000000..0c91f0718f07
--- /dev/null
+++ b/contrib/nvi/common/options.awk
@@ -0,0 +1,9 @@
+# @(#)options.awk 10.1 (Berkeley) 6/8/95
+
+/^\/\* O_[0-9A-Z_]*/ {
+ printf("#define %s %d\n", $2, cnt++);
+ next;
+}
+END {
+ printf("#define O_OPTIONCOUNT %d\n", cnt);
+}
diff --git a/contrib/nvi/common/options.c b/contrib/nvi/common/options.c
new file mode 100644
index 000000000000..973778c78bbd
--- /dev/null
+++ b/contrib/nvi/common/options.c
@@ -0,0 +1,1141 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)options.c 10.51 (Berkeley) 10/14/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../vi/vi.h"
+#include "pathnames.h"
+
+static int opts_abbcmp __P((const void *, const void *));
+static int opts_cmp __P((const void *, const void *));
+static int opts_print __P((SCR *, OPTLIST const *));
+
+/*
+ * 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.
+ */
+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_BACKUP 4.4BSD */
+ {"backup", NULL, OPT_STR, 0},
+/* O_BEAUTIFY 4BSD */
+ {"beautify", NULL, OPT_0BOOL, 0},
+/* O_CDPATH 4.4BSD */
+ {"cdpath", NULL, OPT_STR, 0},
+/* O_CEDIT 4.4BSD */
+ {"cedit", NULL, OPT_STR, 0},
+/* O_COLUMNS 4.4BSD */
+ {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
+/* O_COMMENT 4.4BSD */
+ {"comment", NULL, OPT_0BOOL, 0},
+/* O_DIRECTORY 4BSD */
+ {"directory", NULL, OPT_STR, 0},
+/* O_EDCOMPATIBLE 4BSD */
+ {"edcompatible",NULL, OPT_0BOOL, 0},
+/* O_ESCAPETIME 4.4BSD */
+ {"escapetime", NULL, OPT_NUM, 0},
+/* O_ERRORBELLS 4BSD */
+ {"errorbells", NULL, OPT_0BOOL, 0},
+/* O_EXRC System V (undocumented) */
+ {"exrc", NULL, OPT_0BOOL, 0},
+/* O_EXTENDED 4.4BSD */
+ {"extended", f_recompile, OPT_0BOOL, 0},
+/* O_FILEC 4.4BSD */
+ {"filec", NULL, OPT_STR, 0},
+/* O_FLASH HPUX */
+ {"flash", NULL, OPT_1BOOL, 0},
+/* O_HARDTABS 4BSD */
+ {"hardtabs", NULL, OPT_NUM, 0},
+/* O_ICLOWER 4.4BSD */
+ {"iclower", f_recompile, OPT_0BOOL, 0},
+/* O_IGNORECASE 4BSD */
+ {"ignorecase", f_recompile, OPT_0BOOL, 0},
+/* O_KEYTIME 4.4BSD */
+ {"keytime", NULL, OPT_NUM, 0},
+/* O_LEFTRIGHT 4.4BSD */
+ {"leftright", f_reformat, OPT_0BOOL, 0},
+/* O_LINES 4.4BSD */
+ {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
+/* O_LISP 4BSD
+ * XXX
+ * When the lisp option is implemented, delete the OPT_NOSAVE flag,
+ * so that :mkexrc dumps it.
+ */
+ {"lisp", f_lisp, OPT_0BOOL, OPT_NOSAVE},
+/* O_LIST 4BSD */
+ {"list", f_reformat, OPT_0BOOL, 0},
+/* O_LOCKFILES 4.4BSD
+ * XXX
+ * Locking isn't reliable enough over NFS to require it, in addition,
+ * it's a serious startup performance problem over some remote links.
+ */
+ {"lock", NULL, OPT_1BOOL, 0},
+/* O_MAGIC 4BSD */
+ {"magic", NULL, OPT_1BOOL, 0},
+/* O_MATCHTIME 4.4BSD */
+ {"matchtime", NULL, OPT_NUM, 0},
+/* O_MESG 4BSD */
+ {"mesg", NULL, OPT_1BOOL, 0},
+/* O_MODELINE 4BSD
+ * !!!
+ * 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.
+ */
+ {"modeline", NULL, OPT_0BOOL, OPT_NOSET},
+/* O_MSGCAT 4.4BSD */
+ {"msgcat", f_msgcat, OPT_STR, 0},
+/* O_NOPRINT 4.4BSD */
+ {"noprint", f_print, OPT_STR, 0},
+/* O_NUMBER 4BSD */
+ {"number", f_reformat, OPT_0BOOL, 0},
+/* O_OCTAL 4.4BSD */
+ {"octal", f_print, OPT_0BOOL, 0},
+/* O_OPEN 4BSD */
+ {"open", NULL, OPT_1BOOL, 0},
+/* O_OPTIMIZE 4BSD */
+ {"optimize", NULL, OPT_1BOOL, 0},
+/* O_PARAGRAPHS 4BSD */
+ {"paragraphs", f_paragraph, OPT_STR, 0},
+/* O_PATH 4.4BSD */
+ {"path", NULL, OPT_STR, 0},
+/* O_PRINT 4.4BSD */
+ {"print", f_print, OPT_STR, 0},
+/* O_PROMPT 4BSD */
+ {"prompt", NULL, OPT_1BOOL, 0},
+/* O_READONLY 4BSD (undocumented) */
+ {"readonly", f_readonly, OPT_0BOOL, OPT_ALWAYS},
+/* 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, 0},
+/* O_RULER 4.4BSD */
+ {"ruler", NULL, OPT_0BOOL, 0},
+/* O_SCROLL 4BSD */
+ {"scroll", NULL, OPT_NUM, 0},
+/* O_SEARCHINCR 4.4BSD */
+ {"searchincr", NULL, OPT_0BOOL, 0},
+/* O_SECTIONS 4BSD */
+ {"sections", f_section, OPT_STR, 0},
+/* O_SECURE 4.4BSD */
+ {"secure", NULL, OPT_0BOOL, OPT_NOUNSET},
+/* O_SHELL 4BSD */
+ {"shell", NULL, OPT_STR, 0},
+/* O_SHELLMETA 4.4BSD */
+ {"shellmeta", NULL, OPT_STR, 0},
+/* O_SHIFTWIDTH 4BSD */
+ {"shiftwidth", NULL, OPT_NUM, OPT_NOZERO},
+/* O_SHOWMATCH 4BSD */
+ {"showmatch", NULL, OPT_0BOOL, 0},
+/* O_SHOWMODE 4.4BSD */
+ {"showmode", NULL, OPT_0BOOL, 0},
+/* O_SIDESCROLL 4.4BSD */
+ {"sidescroll", NULL, OPT_NUM, OPT_NOZERO},
+/* O_SLOWOPEN 4BSD */
+ {"slowopen", NULL, OPT_0BOOL, 0},
+/* O_SOURCEANY 4BSD (undocumented)
+ * !!!
+ * 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.
+ */
+ {"sourceany", NULL, OPT_0BOOL, OPT_NOSET},
+/* O_TABSTOP 4BSD */
+ {"tabstop", f_reformat, OPT_NUM, OPT_NOZERO},
+/* O_TAGLENGTH 4BSD */
+ {"taglength", NULL, OPT_NUM, 0},
+/* O_TAGS 4BSD */
+ {"tags", NULL, OPT_STR, 0},
+/* O_TERM 4BSD
+ * !!!
+ * By default, the historic vi always displayed information about two
+ * options, redraw and term. Term seems sufficient.
+ */
+ {"term", NULL, OPT_STR, OPT_ADISP|OPT_NOSAVE},
+/* O_TERSE 4BSD */
+ {"terse", NULL, OPT_0BOOL, 0},
+/* O_TILDEOP 4.4BSD */
+ {"tildeop", 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_NDISP|OPT_NOSAVE},
+/* O_W300 4BSD */
+ {"w300", f_w300, OPT_NUM, OPT_NDISP|OPT_NOSAVE},
+/* O_W9600 4BSD */
+ {"w9600", f_w9600, OPT_NUM, OPT_NDISP|OPT_NOSAVE},
+/* O_WARN 4BSD */
+ {"warn", NULL, OPT_1BOOL, 0},
+/* O_WINDOW 4BSD */
+ {"window", f_window, OPT_NUM, 0},
+/* O_WINDOWNAME 4BSD */
+ {"windowname", NULL, OPT_0BOOL, 0},
+/* O_WRAPLEN 4.4BSD */
+ {"wraplen", NULL, OPT_NUM, 0},
+/* O_WRAPMARGIN 4BSD */
+ {"wrapmargin", NULL, OPT_NUM, 0},
+/* 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 */
+ {"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 */
+ {"smd", O_SHOWMODE}, /* 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) */
+ {"wl", O_WRAPLEN}, /* 4.4BSD */
+ {"wm", O_WRAPMARGIN}, /* 4BSD */
+ {"ws", O_WRAPSCAN}, /* 4BSD */
+ {NULL},
+};
+
+/*
+ * opts_init --
+ * Initialize some of the options.
+ *
+ * PUBLIC: int opts_init __P((SCR *, int *));
+ */
+int
+opts_init(sp, oargs)
+ SCR *sp;
+ int *oargs;
+{
+ ARGS *argv[2], a, b;
+ OPTLIST const *op;
+ u_long v;
+ int cnt, optindx;
+ char *s, b1[1024];
+
+ a.bp = b1;
+ b.bp = NULL;
+ a.len = b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+ /* Set numeric and string default values. */
+#define OI(indx, str) { \
+ if (str != b1) /* GCC puts strings in text-space. */ \
+ (void)strcpy(b1, str); \
+ a.len = strlen(b1); \
+ if (opts_set(sp, argv, NULL)) { \
+ optindx = indx; \
+ goto err; \
+ } \
+}
+ /*
+ * Indirect global options to global space. Specifically, set up
+ * terminal, lines, columns first, they're used by other options.
+ * Note, don't set the flags until we've set up the indirection.
+ */
+ if (o_set(sp, O_TERM, 0, NULL, GO_TERM))
+ goto err;
+ F_SET(&sp->opts[O_TERM], OPT_GLOBAL);
+ if (o_set(sp, O_LINES, 0, NULL, GO_LINES))
+ goto err;
+ F_SET(&sp->opts[O_LINES], OPT_GLOBAL);
+ if (o_set(sp, O_COLUMNS, 0, NULL, GO_COLUMNS))
+ goto err;
+ F_SET(&sp->opts[O_COLUMNS], OPT_GLOBAL);
+ if (o_set(sp, O_SECURE, 0, NULL, GO_SECURE))
+ goto err;
+ F_SET(&sp->opts[O_SECURE], OPT_GLOBAL);
+
+ /* Initialize string values. */
+ (void)snprintf(b1, sizeof(b1),
+ "cdpath=%s", (s = getenv("CDPATH")) == NULL ? ":" : s);
+ OI(O_CDPATH, b1);
+
+ /*
+ * !!!
+ * 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);
+ OI(O_DIRECTORY, b1);
+ OI(O_ESCAPETIME, "escapetime=1");
+ OI(O_KEYTIME, "keytime=6");
+ OI(O_MATCHTIME, "matchtime=7");
+ (void)snprintf(b1, sizeof(b1), "msgcat=%s", _PATH_MSGCAT);
+ OI(O_MSGCAT, b1);
+ OI(O_REPORT, "report=5");
+ OI(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
+ (void)snprintf(b1, sizeof(b1), "path=%s", "");
+ OI(O_PATH, b1);
+ (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
+ OI(O_RECDIR, b1);
+ OI(O_SECTIONS, "sections=NHSHH HUnhsh");
+ (void)snprintf(b1, sizeof(b1),
+ "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
+ OI(O_SHELL, b1);
+ OI(O_SHELLMETA, "shellmeta=~{[*?$`'\"\\");
+ OI(O_SHIFTWIDTH, "shiftwidth=8");
+ OI(O_SIDESCROLL, "sidescroll=16");
+ OI(O_TABSTOP, "tabstop=8");
+ (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
+ OI(O_TAGS, b1);
+
+ /*
+ * XXX
+ * Initialize O_SCROLL here, after term; initializing term should
+ * have created a LINES/COLUMNS value.
+ */
+ if ((v = (O_VAL(sp, O_LINES) - 1) / 2) == 0)
+ v = 1;
+ (void)snprintf(b1, sizeof(b1), "scroll=%ld", v);
+ OI(O_SCROLL, b1);
+
+ /*
+ * The default window option values are:
+ * 8 if baud rate <= 600
+ * 16 if baud rate <= 1200
+ * LINES - 1 if baud rate > 1200
+ *
+ * Note, the windows option code will correct any too-large value
+ * or when the O_LINES value is 1.
+ */
+ if (sp->gp->scr_baud(sp, &v))
+ return (1);
+ 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);
+ OI(O_WINDOW, b1);
+
+ /*
+ * Set boolean default values, and copy all settings into the default
+ * information. OS_NOFREE is set, we're copying, not replacing.
+ */
+ for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
+ switch (op->type) {
+ case OPT_0BOOL:
+ break;
+ case OPT_1BOOL:
+ O_SET(sp, cnt);
+ O_D_SET(sp, cnt);
+ break;
+ case OPT_NUM:
+ o_set(sp, cnt, OS_DEF, NULL, O_VAL(sp, cnt));
+ break;
+ case OPT_STR:
+ if (O_STR(sp, cnt) != NULL && o_set(sp, cnt,
+ OS_DEF | OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * !!!
+ * Some options can be initialized by the command name or the
+ * command-line arguments. They don't set the default values,
+ * it's historic practice.
+ */
+ for (; *oargs != -1; ++oargs)
+ OI(*oargs, optlist[*oargs].name);
+ return (0);
+#undef OI
+
+err: msgq(sp, M_ERR,
+ "031|Unable to set default %s option", optlist[optindx].name);
+ return (1);
+}
+
+/*
+ * opts_set --
+ * Change the values of one or more options.
+ *
+ * PUBLIC: int opts_set __P((SCR *, ARGS *[], char *));
+ */
+int
+opts_set(sp, argv, usage)
+ SCR *sp;
+ ARGS *argv[];
+ char *usage;
+{
+ enum optdisp disp;
+ enum nresult nret;
+ OPTLIST const *op;
+ OPTION *spo;
+ u_long value, turnoff;
+ int ch, equals, nf, nf2, offset, qmark, rval;
+ char *endp, *name, *p, *sep, *t;
+
+ disp = NO_DISPLAY;
+ for (rval = 0; argv[0]->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 question mark. */
+ for (sep = NULL, equals = qmark = 0,
+ p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
+ if (ch == '=' || ch == '?') {
+ if (p == name) {
+ if (usage != NULL)
+ msgq(sp, M_ERR,
+ "032|Usage: %s", usage);
+ return (1);
+ }
+ sep = p;
+ if (ch == '=')
+ equals = 1;
+ else
+ qmark = 1;
+ break;
+ }
+
+ turnoff = 0;
+ op = NULL;
+ if (sep != NULL)
+ *sep++ = '\0';
+
+ /* Search for the name, then name without any leading "no". */
+ if ((op = opts_search(name)) == NULL &&
+ name[0] == 'n' && name[1] == 'o') {
+ turnoff = 1;
+ name += 2;
+ op = opts_search(name);
+ }
+ if (op == NULL) {
+ opts_nomatch(sp, name);
+ rval = 1;
+ continue;
+ }
+
+ /* Find current option values. */
+ offset = op - optlist;
+ spo = sp->opts + offset;
+
+ /*
+ * !!!
+ * Historically, the question mark could be a separate
+ * argument.
+ */
+ if (!equals && !qmark &&
+ argv[1]->len == 1 && argv[1]->bp[0] == '?') {
+ ++argv;
+ qmark = 1;
+ }
+
+ /* Set name, value. */
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ /* Some options may not be reset. */
+ if (F_ISSET(op, OPT_NOUNSET) && turnoff) {
+ msgq_str(sp, M_ERR, name,
+ "291|set: the %s option may not be turned off");
+ rval = 1;
+ break;
+ }
+
+ /* Some options may not be set. */
+ if (F_ISSET(op, OPT_NOSET) && !turnoff) {
+ msgq_str(sp, M_ERR, name,
+ "313|set: the %s option may never be turned on");
+ rval = 1;
+ break;
+ }
+
+ if (equals) {
+ msgq_str(sp, M_ERR, name,
+ "034|set: [no]%s option doesn't take a value");
+ rval = 1;
+ break;
+ }
+ if (qmark) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+
+ /*
+ * Do nothing if the value is unchanged, the underlying
+ * functions can be expensive.
+ */
+ if (!F_ISSET(op, OPT_ALWAYS))
+ if (turnoff) {
+ if (!O_ISSET(sp, offset))
+ break;
+ } else {
+ if (O_ISSET(sp, offset))
+ break;
+ }
+
+ /* Report to subsystems. */
+ if (op->func != NULL &&
+ op->func(sp, spo, NULL, &turnoff) ||
+ ex_optchange(sp, offset, NULL, &turnoff) ||
+ v_optchange(sp, offset, NULL, &turnoff) ||
+ sp->gp->scr_optchange(sp, offset, NULL, &turnoff)) {
+ rval = 1;
+ break;
+ }
+
+ /* Set the value. */
+ if (turnoff)
+ O_CLR(sp, offset);
+ else
+ O_SET(sp, offset);
+ break;
+ case OPT_NUM:
+ if (turnoff) {
+ msgq_str(sp, M_ERR, name,
+ "035|set: %s option isn't a boolean");
+ rval = 1;
+ break;
+ }
+ if (qmark || !equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+
+ if (!isdigit(sep[0]))
+ goto badnum;
+ if ((nret =
+ nget_uslong(&value, sep, &endp, 10)) != NUM_OK) {
+ p = msg_print(sp, name, &nf);
+ t = msg_print(sp, sep, &nf2);
+ switch (nret) {
+ case NUM_ERR:
+ msgq(sp, M_SYSERR,
+ "036|set: %s option: %s", p, t);
+ break;
+ case NUM_OVER:
+ msgq(sp, M_ERR,
+ "037|set: %s option: %s: value overflow", p, t);
+ break;
+ case NUM_OK:
+ case NUM_UNDER:
+ abort();
+ }
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ if (nf2)
+ FREE_SPACE(sp, t, 0);
+ rval = 1;
+ break;
+ }
+ if (*endp && !isblank(*endp)) {
+badnum: p = msg_print(sp, name, &nf);
+ t = msg_print(sp, sep, &nf2);
+ msgq(sp, M_ERR,
+ "038|set: %s option: %s is an illegal number", p, t);
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ if (nf2)
+ FREE_SPACE(sp, t, 0);
+ rval = 1;
+ break;
+ }
+
+ /* Some options may never be set to zero. */
+ if (F_ISSET(op, OPT_NOZERO) && value == 0) {
+ msgq_str(sp, M_ERR, name,
+ "314|set: the %s option may never be set to 0");
+ rval = 1;
+ break;
+ }
+
+ /*
+ * Do nothing if the value is unchanged, the underlying
+ * functions can be expensive.
+ */
+ if (!F_ISSET(op, OPT_ALWAYS) &&
+ O_VAL(sp, offset) == value)
+ break;
+
+ /* Report to subsystems. */
+ if (op->func != NULL &&
+ op->func(sp, spo, sep, &value) ||
+ ex_optchange(sp, offset, sep, &value) ||
+ v_optchange(sp, offset, sep, &value) ||
+ sp->gp->scr_optchange(sp, offset, sep, &value)) {
+ rval = 1;
+ break;
+ }
+
+ /* Set the value. */
+ if (o_set(sp, offset, 0, NULL, value))
+ rval = 1;
+ break;
+ case OPT_STR:
+ if (turnoff) {
+ msgq_str(sp, M_ERR, name,
+ "039|set: %s option isn't a boolean");
+ rval = 1;
+ break;
+ }
+ if (qmark || !equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+
+ /*
+ * Do nothing if the value is unchanged, the underlying
+ * functions can be expensive.
+ */
+ if (!F_ISSET(op, OPT_ALWAYS) &&
+ O_STR(sp, offset) != NULL &&
+ !strcmp(O_STR(sp, offset), sep))
+ break;
+
+ /* Report to subsystems. */
+ if (op->func != NULL &&
+ op->func(sp, spo, sep, NULL) ||
+ ex_optchange(sp, offset, sep, NULL) ||
+ v_optchange(sp, offset, sep, NULL) ||
+ sp->gp->scr_optchange(sp, offset, sep, NULL)) {
+ rval = 1;
+ break;
+ }
+
+ /* Set the value. */
+ if (o_set(sp, offset, OS_STRDUP, sep, 0))
+ rval = 1;
+ break;
+ default:
+ abort();
+ }
+ }
+ if (disp != NO_DISPLAY)
+ opts_dump(sp, disp);
+ return (rval);
+}
+
+/*
+ * o_set --
+ * Set an option's value.
+ *
+ * PUBLIC: int o_set __P((SCR *, int, u_int, char *, u_long));
+ */
+int
+o_set(sp, opt, flags, str, val)
+ SCR *sp;
+ int opt;
+ u_int flags;
+ char *str;
+ u_long val;
+{
+ OPTION *op;
+
+ /* Set a pointer to the options area. */
+ op = F_ISSET(&sp->opts[opt], OPT_GLOBAL) ?
+ &sp->gp->opts[sp->opts[opt].o_cur.val] : &sp->opts[opt];
+
+ /* Copy the string, if requested. */
+ if (LF_ISSET(OS_STRDUP) && (str = strdup(str)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* Free the previous string, if requested, and set the value. */
+ if LF_ISSET(OS_DEF)
+ if (LF_ISSET(OS_STR | OS_STRDUP)) {
+ if (!LF_ISSET(OS_NOFREE) && op->o_def.str != NULL)
+ free(op->o_def.str);
+ op->o_def.str = str;
+ } else
+ op->o_def.val = val;
+ else
+ if (LF_ISSET(OS_STR | OS_STRDUP)) {
+ if (!LF_ISSET(OS_NOFREE) && op->o_cur.str != NULL)
+ free(op->o_cur.str);
+ op->o_cur.str = str;
+ } else
+ op->o_cur.val = val;
+ return (0);
+}
+
+/*
+ * opts_empty --
+ * Return 1 if the string option is invalid, 0 if it's OK.
+ *
+ * PUBLIC: int opts_empty __P((SCR *, int, int));
+ */
+int
+opts_empty(sp, off, silent)
+ SCR *sp;
+ int off, silent;
+{
+ char *p;
+
+ if ((p = O_STR(sp, off)) == NULL || p[0] == '\0') {
+ if (!silent)
+ msgq_str(sp, M_ERR, optlist[off].name,
+ "305|No %s edit option specified");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * opts_dump --
+ * List the current values of selected options.
+ *
+ * PUBLIC: void opts_dump __P((SCR *, enum optdisp));
+ */
+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.
+ *
+ * Find a column width we can live with, testing from 10 columns to 1.
+ */
+ for (numcols = 10; numcols > 1; --numcols) {
+ colwidth = sp->cols / numcols & ~(STANDARD_TAB - 1);
+ if (colwidth >= 10) {
+ colwidth =
+ (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
+ numcols = sp->cols / colwidth;
+ break;
+ }
+ colwidth = 0;
+ }
+
+ /*
+ * Get the set of options to list, entering them into
+ * the column list or the overflow list.
+ */
+ for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
+ cnt = op - optlist;
+
+ /* If OPT_NDISP set, it's never displayed. */
+ if (F_ISSET(op, OPT_NDISP))
+ continue;
+
+ switch (type) {
+ case ALL_DISPLAY: /* Display all. */
+ break;
+ case CHANGED_DISPLAY: /* Display changed. */
+ /* If OPT_ADISP set, it's always "changed". */
+ if (F_ISSET(op, OPT_ADISP))
+ break;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ case OPT_NUM:
+ if (O_VAL(sp, cnt) == O_D_VAL(sp, cnt))
+ continue;
+ break;
+ case OPT_STR:
+ if (O_STR(sp, cnt) == O_D_STR(sp, cnt) ||
+ O_D_STR(sp, cnt) != NULL &&
+ !strcmp(O_STR(sp, cnt), O_D_STR(sp, cnt)))
+ continue;
+ break;
+ }
+ break;
+ case SELECT_DISPLAY: /* Display selected. */
+ if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
+ continue;
+ break;
+ default:
+ case NO_DISPLAY:
+ abort();
+ }
+ 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:
+ if (O_STR(sp, cnt) != NULL)
+ curlen += strlen(O_STR(sp, cnt));
+ curlen += 3;
+ break;
+ }
+ /* Offset by 2 so there's a gap. */
+ if (curlen <= colwidth - 2)
+ s_op[s_num++] = cnt;
+ else
+ b_op[b_num++] = cnt;
+ }
+
+ if (s_num > 0) {
+ /* Figure out the number of rows. */
+ if (s_num > numcols) {
+ numrows = s_num / numcols;
+ if (s_num % numcols)
+ ++numrows;
+ } else
+ numrows = 1;
+
+ /* Display the options in sorted order. */
+ for (row = 0; row < numrows;) {
+ for (base = row, col = 0; col < numcols; ++col) {
+ cnt = opts_print(sp, &optlist[s_op[base]]);
+ if ((base += numrows) >= s_num)
+ break;
+ (void)ex_printf(sp, "%*s",
+ (int)(colwidth - cnt), "");
+ }
+ if (++row < numrows || b_num)
+ (void)ex_puts(sp, "\n");
+ }
+ }
+
+ for (row = 0; row < b_num;) {
+ (void)opts_print(sp, &optlist[b_op[row]]);
+ if (++row < b_num)
+ (void)ex_puts(sp, "\n");
+ }
+ (void)ex_puts(sp, "\n");
+}
+
+/*
+ * opts_print --
+ * Print out an option.
+ */
+static int
+opts_print(sp, op)
+ SCR *sp;
+ OPTLIST const *op;
+{
+ int curlen, offset;
+
+ curlen = 0;
+ offset = op - optlist;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ curlen += ex_printf(sp,
+ "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
+ break;
+ case OPT_NUM:
+ curlen += ex_printf(sp, "%s=%ld", op->name, O_VAL(sp, offset));
+ break;
+ case OPT_STR:
+ curlen += ex_printf(sp, "%s=\"%s\"", op->name,
+ O_STR(sp, offset) == NULL ? "" : O_STR(sp, offset));
+ break;
+ }
+ return (curlen);
+}
+
+/*
+ * opts_save --
+ * Write the current configuration to a file.
+ *
+ * PUBLIC: int opts_save __P((SCR *, FILE *));
+ */
+int
+opts_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ OPTLIST const *op;
+ int ch, cnt;
+ char *p;
+
+ for (op = optlist; op->name != NULL; ++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=%-3ld\n", op->name, O_VAL(sp, cnt));
+ break;
+ case OPT_STR:
+ if (O_STR(sp, cnt) == NULL)
+ break;
+ (void)fprintf(fp, "set ");
+ for (p = op->name; (ch = *p) != '\0'; ++p) {
+ if (isblank(ch) || ch == '\\')
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('=', fp);
+ for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
+ if (isblank(ch) || ch == '\\')
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ break;
+ }
+ if (ferror(fp)) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * opts_search --
+ * Search for an option.
+ *
+ * PUBLIC: OPTLIST const *opts_search __P((char *));
+ */
+OPTLIST const *
+opts_search(name)
+ char *name;
+{
+ OPTLIST const *op, *found;
+ OABBREV atmp, *ap;
+ OPTLIST otmp;
+ size_t len;
+
+ /* Check list of abbreviations. */
+ atmp.name = name;
+ if ((ap = bsearch(&atmp, abbrev, sizeof(abbrev) / sizeof(OABBREV) - 1,
+ sizeof(OABBREV), opts_abbcmp)) != NULL)
+ return (optlist + ap->offset);
+
+ /* Check list of options. */
+ otmp.name = name;
+ if ((op = bsearch(&otmp, optlist, sizeof(optlist) / sizeof(OPTLIST) - 1,
+ sizeof(OPTLIST), opts_cmp)) != NULL)
+ return (op);
+
+ /*
+ * Check to see if the name is the prefix of one (and only one)
+ * option. If so, return the option.
+ */
+ len = strlen(name);
+ for (found = NULL, 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 (found != NULL)
+ return (NULL);
+ found = op;
+ }
+ }
+ return (found);
+}
+
+/*
+ * opts_nomatch --
+ * Standard nomatch error message for options.
+ *
+ * PUBLIC: void opts_nomatch __P((SCR *, char *));
+ */
+void
+opts_nomatch(sp, name)
+ SCR *sp;
+ char *name;
+{
+ msgq_str(sp, M_ERR, name,
+ "033|set: no %s option: 'set all' gives all option values");
+}
+
+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_copy --
+ * Copy a screen's OPTION array.
+ *
+ * PUBLIC: int opts_copy __P((SCR *, SCR *));
+ */
+int
+opts_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ int cnt, rval;
+
+ /* Copy most everything without change. */
+ memcpy(sp->opts, orig->opts, sizeof(orig->opts));
+
+ /* Copy the string edit options. */
+ for (cnt = rval = 0; cnt < O_OPTIONCOUNT; ++cnt) {
+ if (optlist[cnt].type != OPT_STR ||
+ F_ISSET(&optlist[cnt], OPT_GLOBAL))
+ continue;
+ /*
+ * If never set, or already failed, NULL out the entries --
+ * have to continue after failure, otherwise would have two
+ * screens referencing the same memory.
+ */
+ if (rval || O_STR(sp, cnt) == NULL) {
+ o_set(sp, cnt, OS_NOFREE | OS_STR, NULL, 0);
+ o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
+ continue;
+ }
+
+ /* Copy the current string. */
+ if (o_set(sp, cnt, OS_NOFREE | OS_STRDUP, O_STR(sp, cnt), 0)) {
+ o_set(sp, cnt, OS_DEF | OS_NOFREE | OS_STR, NULL, 0);
+ goto nomem;
+ }
+
+ /* Copy the default string. */
+ if (O_D_STR(sp, cnt) != NULL && o_set(sp, cnt,
+ OS_DEF | OS_NOFREE | OS_STRDUP, O_D_STR(sp, cnt), 0)) {
+nomem: msgq(orig, M_SYSERR, NULL);
+ rval = 1;
+ }
+ }
+ return (rval);
+}
+
+/*
+ * opts_free --
+ * Free all option strings
+ *
+ * PUBLIC: void opts_free __P((SCR *));
+ */
+void
+opts_free(sp)
+ SCR *sp;
+{
+ int cnt;
+
+ for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) {
+ if (optlist[cnt].type != OPT_STR ||
+ F_ISSET(&optlist[cnt], OPT_GLOBAL))
+ continue;
+ if (O_STR(sp, cnt) != NULL)
+ free(O_STR(sp, cnt));
+ if (O_D_STR(sp, cnt) != NULL)
+ free(O_D_STR(sp, cnt));
+ }
+}
diff --git a/contrib/nvi/common/options.h b/contrib/nvi/common/options.h
new file mode 100644
index 000000000000..2646dc301b5a
--- /dev/null
+++ b/contrib/nvi/common/options.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)options.h 10.19 (Berkeley) 10/10/96
+ */
+
+/*
+ * Edit option information. Historically, if you set a boolean or numeric
+ * edit option value to its "default" value, it didn't show up in the :set
+ * display, i.e. it wasn't considered "changed". String edit options would
+ * show up as changed, regardless. We maintain a parallel set of values
+ * which are the default values and never consider an edit option changed
+ * if it was reset to the default value.
+ *
+ * Macros to retrieve boolean, integral and string option values, and to
+ * set, clear and test boolean option values. Some options (secure, lines,
+ * columns, terminal type) are global in scope, and are therefore stored
+ * in the global area. The offset in the global options array is stored
+ * in the screen's value field. This is set up when the options are first
+ * initialized.
+ */
+#define O_V(sp, o, fld) \
+ (F_ISSET(&(sp)->opts[(o)], OPT_GLOBAL) ? \
+ (sp)->gp->opts[(sp)->opts[(o)].o_cur.val].fld : \
+ (sp)->opts[(o)].fld)
+
+/* Global option macros. */
+#define OG_CLR(gp, o) ((gp)->opts[(o)].o_cur.val) = 0
+#define OG_SET(gp, o) ((gp)->opts[(o)].o_cur.val) = 1
+#define OG_STR(gp, o) ((gp)->opts[(o)].o_cur.str)
+#define OG_VAL(gp, o) ((gp)->opts[(o)].o_cur.val)
+#define OG_ISSET(gp, o) OG_VAL(gp, o)
+
+#define OG_D_STR(gp, o) ((gp)->opts[(o)].o_def.str)
+#define OG_D_VAL(gp, o) ((gp)->opts[(o)].o_def.val)
+
+/*
+ * Flags to o_set(); need explicit OS_STR as can be setting the value to
+ * NULL.
+ */
+#define OS_DEF 0x01 /* Set the default value. */
+#define OS_NOFREE 0x02 /* Don't free the old string. */
+#define OS_STR 0x04 /* Set to string argument. */
+#define OS_STRDUP 0x08 /* Copy then set to string argument. */
+
+struct _option {
+ union {
+ u_long val; /* Value or boolean. */
+ char *str; /* String. */
+ } o_cur;
+#define O_CLR(sp, o) o_set(sp, o, 0, NULL, 0)
+#define O_SET(sp, o) o_set(sp, o, 0, NULL, 1)
+#define O_STR(sp, o) O_V(sp, o, o_cur.str)
+#define O_VAL(sp, o) O_V(sp, o, o_cur.val)
+#define O_ISSET(sp, o) O_VAL(sp, o)
+
+ union {
+ u_long val; /* Value or boolean. */
+ char *str; /* String. */
+ } o_def;
+#define O_D_CLR(sp, o) o_set(sp, o, OS_DEF, NULL, 0)
+#define O_D_SET(sp, o) o_set(sp, o, OS_DEF, NULL, 1)
+#define O_D_STR(sp, o) O_V(sp, o, o_def.str)
+#define O_D_VAL(sp, o) O_V(sp, o, o_def.val)
+#define O_D_ISSET(sp, o) O_D_VAL(sp, o)
+
+#define OPT_GLOBAL 0x01 /* Option is global. */
+#define OPT_SELECTED 0x02 /* Selected for display. */
+ u_int8_t flags;
+};
+
+/* List of option names, associated update functions and information. */
+struct _optlist {
+ 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_ADISP 0x001 /* Always display the option. */
+#define OPT_ALWAYS 0x002 /* Always call the support function. */
+#define OPT_NDISP 0x004 /* Never display the option. */
+#define OPT_NOSAVE 0x008 /* Mkexrc command doesn't save. */
+#define OPT_NOSET 0x010 /* Option may not be set. */
+#define OPT_NOUNSET 0x020 /* Option may not be unset. */
+#define OPT_NOZERO 0x040 /* Option may not be set to 0. */
+ u_int8_t flags;
+};
+
+/* Option argument to opts_dump(). */
+enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
+
+/* Options array. */
+extern OPTLIST const optlist[];
+
+#include "options_def.h"
diff --git a/contrib/nvi/common/options_f.c b/contrib/nvi/common/options_f.c
new file mode 100644
index 000000000000..ea3c61160cf5
--- /dev/null
+++ b/contrib/nvi/common/options_f.c
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)options_f.c 10.25 (Berkeley) 7/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+/*
+ * PUBLIC: int f_altwerase __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_altwerase(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (!*valp)
+ O_CLR(sp, O_TTYWERASE);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_columns __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_columns(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ /* Validate the number. */
+ if (*valp < MINIMUM_SCREEN_COLS) {
+ msgq(sp, M_ERR, "040|Screen columns too small, less than %d",
+ MINIMUM_SCREEN_COLS);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * It's not uncommon for allocation of huge chunks of memory to cause
+ * core dumps on various systems. So, we prune out numbers that are
+ * "obviously" wrong. Vi will not work correctly if it has the wrong
+ * number of lines/columns for the screen, but at least we don't drop
+ * core.
+ */
+#define MAXIMUM_SCREEN_COLS 500
+ if (*valp > MAXIMUM_SCREEN_COLS) {
+ msgq(sp, M_ERR, "041|Screen columns too large, greater than %d",
+ MAXIMUM_SCREEN_COLS);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_lines __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_lines(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ /* Validate the number. */
+ if (*valp < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "042|Screen lines too small, less than %d",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * It's not uncommon for allocation of huge chunks of memory to cause
+ * core dumps on various systems. So, we prune out numbers that are
+ * "obviously" wrong. Vi will not work correctly if it has the wrong
+ * number of lines/columns for the screen, but at least we don't drop
+ * core.
+ */
+#define MAXIMUM_SCREEN_ROWS 500
+ if (*valp > MAXIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "043|Screen lines too large, greater than %d",
+ MAXIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /*
+ * Set the value, and the related scroll value. If no window
+ * value set, set a new default window.
+ */
+ o_set(sp, O_LINES, 0, NULL, *valp);
+ if (*valp == 1) {
+ sp->defscroll = 1;
+
+ if (O_VAL(sp, O_WINDOW) == O_D_VAL(sp, O_WINDOW) ||
+ O_VAL(sp, O_WINDOW) > *valp) {
+ o_set(sp, O_WINDOW, 0, NULL, 1);
+ o_set(sp, O_WINDOW, OS_DEF, NULL, 1);
+ }
+ } else {
+ sp->defscroll = (*valp - 1) / 2;
+
+ if (O_VAL(sp, O_WINDOW) == O_D_VAL(sp, O_WINDOW) ||
+ O_VAL(sp, O_WINDOW) > *valp) {
+ o_set(sp, O_WINDOW, 0, NULL, *valp - 1);
+ o_set(sp, O_WINDOW, OS_DEF, NULL, *valp - 1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_lisp __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_lisp(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ msgq(sp, M_ERR, "044|The lisp option is not implemented");
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_msgcat __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_msgcat(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ (void)msg_open(sp, str);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_paragraph __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_paragraph(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "048|The paragraph option must be in two character groups");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_print __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_print(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ /* Reinitialize the key fast lookup table. */
+ v_key_ilookup(sp);
+
+ /* Reformat the screen. */
+ F_SET(sp, SC_SCR_REFORMAT);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_readonly __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_readonly(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ /*
+ * !!!
+ * See the comment in exf.c.
+ */
+ if (*valp)
+ F_CLR(sp, SC_READONLY);
+ else
+ F_SET(sp, SC_READONLY);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_recompile __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_recompile(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (F_ISSET(sp, SC_RE_SEARCH)) {
+ regfree(&sp->re_c);
+ F_CLR(sp, SC_RE_SEARCH);
+ }
+ if (F_ISSET(sp, SC_RE_SUBST)) {
+ regfree(&sp->subre_c);
+ F_CLR(sp, SC_RE_SUBST);
+ }
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_reformat __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_reformat(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ F_SET(sp, SC_SCR_REFORMAT);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_section __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_section(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "049|The section option must be in two character groups");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_ttywerase __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_ttywerase(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (!*valp)
+ O_CLR(sp, O_ALTWERASE);
+ return (0);
+}
+
+/*
+ * PUBLIC: int f_w300 __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_w300(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ u_long v;
+
+ /* Historical behavior for w300 was < 1200. */
+ if (sp->gp->scr_baud(sp, &v))
+ return (1);
+ if (v >= 1200)
+ return (0);
+
+ return (f_window(sp, op, str, valp));
+}
+
+/*
+ * PUBLIC: int f_w1200 __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_w1200(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ u_long v;
+
+ /* Historical behavior for w1200 was == 1200. */
+ if (sp->gp->scr_baud(sp, &v))
+ return (1);
+ if (v < 1200 || v > 4800)
+ return (0);
+
+ return (f_window(sp, op, str, valp));
+}
+
+/*
+ * PUBLIC: int f_w9600 __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_w9600(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ u_long v;
+
+ /* Historical behavior for w9600 was > 1200. */
+ if (sp->gp->scr_baud(sp, &v))
+ return (1);
+ if (v <= 4800)
+ return (0);
+
+ return (f_window(sp, op, str, valp));
+}
+
+/*
+ * PUBLIC: int f_window __P((SCR *, OPTION *, char *, u_long *));
+ */
+int
+f_window(sp, op, str, valp)
+ SCR *sp;
+ OPTION *op;
+ char *str;
+ u_long *valp;
+{
+ if (*valp >= O_VAL(sp, O_LINES) - 1 &&
+ (*valp = O_VAL(sp, O_LINES) - 1) == 0)
+ *valp = 1;
+ return (0);
+}
diff --git a/contrib/nvi/common/put.c b/contrib/nvi/common/put.c
new file mode 100644
index 000000000000..8c0ca4b7c14f
--- /dev/null
+++ b/contrib/nvi/common/put.c
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)put.c 10.11 (Berkeley) 9/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * put --
+ * Put text buffer contents into the file.
+ *
+ * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
+ */
+int
+put(sp, cbp, namep, cp, rp, append)
+ SCR *sp;
+ CB *cbp;
+ CHAR_T *namep;
+ MARK *cp, *rp;
+ int append;
+{
+ CHAR_T name;
+ TEXT *ltp, *tp;
+ recno_t lno;
+ size_t blen, clen, len;
+ int rval;
+ char *bp, *p, *t;
+
+ if (cbp == NULL)
+ if (namep == NULL) {
+ cbp = sp->gp->dcbp;
+ if (cbp == NULL) {
+ msgq(sp, M_ERR,
+ "053|The default buffer is empty");
+ return (1);
+ }
+ } else {
+ name = *namep;
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ msgq(sp, M_ERR, "054|Buffer %s is empty",
+ KEY_NAME(sp, name));
+ return (1);
+ }
+ }
+ tp = cbp->textq.cqh_first;
+
+ /*
+ * It's possible to do a put into an empty file, meaning that the cut
+ * buffer simply becomes the file. It's a special case so that we can
+ * ignore it in general.
+ *
+ * !!!
+ * Historically, pasting into a file with no lines in vi would preserve
+ * the single blank line. This is surely a result of the fact that the
+ * historic vi couldn't deal with a file that had no lines in it. This
+ * implementation treats that as a bug, and does not retain the blank
+ * line.
+ *
+ * Historical practice is that the cursor ends at the first character
+ * in the file.
+ */
+ if (cp->lno == 1) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno == 0) {
+ for (; tp != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (db_append(sp, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+ }
+ }
+
+ /* If a line mode buffer, append each new line into the file. */
+ if (F_ISSET(cbp, CB_LMODE)) {
+ lno = append ? cp->lno : cp->lno - 1;
+ rp->lno = lno + 1;
+ for (; tp != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (db_append(sp, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->cno = 0;
+ (void)nonblank(sp, rp->lno, &rp->cno);
+ return (0);
+ }
+
+ /*
+ * If buffer was cut in character mode, replace the current line with
+ * one built from the portion of the first line to the left of the
+ * split plus the first line in the CB. Append each intermediate line
+ * in the CB. Append a line built from the portion of the first line
+ * to the right of the split plus the last line in the CB.
+ *
+ * Get the first line.
+ */
+ lno = cp->lno;
+ if (db_get(sp, lno, DBG_FATAL, &p, &len))
+ return (1);
+
+ GET_SPACE_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) {
+ memcpy(bp, p, clen);
+ p += clen;
+ t += clen;
+ }
+
+ /* First line from the CB. */
+ if (tp->len != 0) {
+ memcpy(t, tp->lb, tp->len);
+ t += tp->len;
+ }
+
+ /* Calculate length left in the original line. */
+ clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));
+
+ /*
+ * !!!
+ * In the historical 4BSD version of vi, character mode puts within
+ * a single line have two cursor behaviors: if the put is from the
+ * unnamed buffer, the cursor moves to the character inserted which
+ * appears last in the file. If the put is from a named buffer,
+ * the cursor moves to the character inserted which appears first
+ * in the file. In System III/V, it was changed at some point and
+ * the cursor always moves to the first character. In both versions
+ * of vi, character mode puts that cross line boundaries leave the
+ * cursor on the first character. Nvi implements the System III/V
+ * behavior, and expect POSIX.2 to do so as well.
+ */
+ rp->lno = lno;
+ rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);
+
+ /*
+ * If no more lines in the CB, append the rest of the original
+ * line and quit. Otherwise, build the last line before doing
+ * the intermediate lines, because the line changes will lose
+ * the cached line.
+ */
+ if (tp->q.cqe_next == (void *)&cbp->textq) {
+ if (clen > 0) {
+ memcpy(t, p, clen);
+ t += clen;
+ }
+ if (db_set(sp, lno, bp, t - bp))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ } else {
+ /*
+ * Have to build both the first and last lines of the
+ * put before doing any sets or we'll lose the cached
+ * line. Build both the first and last lines in the
+ * same buffer, so we don't have to have another buffer
+ * floating around.
+ *
+ * Last part of original line; check for space, reset
+ * the pointer into the buffer.
+ */
+ ltp = cbp->textq.cqh_last;
+ len = t - bp;
+ ADD_SPACE_RET(sp, bp, blen, ltp->len + clen);
+ t = bp + len;
+
+ /* Add in last part of the CB. */
+ memcpy(t, ltp->lb, ltp->len);
+ if (clen)
+ memcpy(t + ltp->len, p, clen);
+ clen += ltp->len;
+
+ /*
+ * Now: bp points to the first character of the first
+ * line, t points to the last character of the last
+ * line, t - bp is the length of the first line, and
+ * clen is the length of the last. Just figured you'd
+ * want to know.
+ *
+ * Output the line replacing the original line.
+ */
+ if (db_set(sp, lno, bp, t - bp))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+
+ /* Output any intermediate lines in the CB. */
+ for (tp = tp->q.cqe_next;
+ tp->q.cqe_next != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (db_append(sp, 1, lno, tp->lb, tp->len))
+ goto err;
+
+ if (db_append(sp, 1, lno, t, clen))
+ goto err;
+ ++sp->rptlines[L_ADDED];
+ }
+ rval = 0;
+
+ if (0)
+err: rval = 1;
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/contrib/nvi/common/recover.c b/contrib/nvi/common/recover.c
new file mode 100644
index 000000000000..f3abaab5a536
--- /dev/null
+++ b/contrib/nvi/common/recover.c
@@ -0,0 +1,878 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)recover.c 10.21 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+/*
+ * We include <sys/file.h>, because the open #defines were found there
+ * on historical systems. We also include <fcntl.h> because the open(2)
+ * #defines are found there on newer systems.
+ */
+#include <sys/file.h>
+
+#include <bitstring.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "pathnames.h"
+
+/*
+ * Recovery code.
+ *
+ * The basic scheme is as follows. In the EXF structure, we maintain full
+ * paths of a b+tree file and a mail recovery file. The former is the file
+ * used as backing store by the DB package. The latter is the file that
+ * contains an email message to be sent to the user if we crash. The two
+ * simple states of recovery are:
+ *
+ * + first starting the edit session:
+ * the b+tree file exists and is mode 700, the mail recovery
+ * file doesn't exist.
+ * + after the file has been modified:
+ * the b+tree file exists and is mode 600, the mail recovery
+ * file exists, and is exclusively locked.
+ *
+ * In the EXF structure we maintain a file descriptor that is the locked
+ * file descriptor for the mail recovery file. NOTE: we sometimes have to
+ * do locking with fcntl(2). This is a problem because if you close(2) any
+ * file descriptor associated with the file, ALL of the locks go away. Be
+ * sure to remember that if you have to modify the recovery code. (It has
+ * been rhetorically asked of what the designers could have been thinking
+ * when they did that interface. The answer is simple: they weren't.)
+ *
+ * To find out if a recovery file/backing file pair are in use, try to get
+ * a lock on the recovery file.
+ *
+ * To find out if a backing file can be deleted at boot time, check for an
+ * owner execute bit. (Yes, I know it's ugly, but it's either that or put
+ * special stuff into the backing file itself, or correlate the files at
+ * boot time, neither of which looks like fun.) Note also that there's a
+ * window between when the file is created and the X bit is set. It's small,
+ * but it's there. To fix the window, check for 0 length files as well.
+ *
+ * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
+ * this DOES NOT mean that any initialization has been done, only that we
+ * haven't yet failed at setting up or doing recovery.
+ *
+ * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
+ * If that bit is not set when ending a file session:
+ * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
+ * they are unlink(2)'d, and free(3)'d.
+ * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
+ *
+ * The backing b+tree file is set up when a file is first edited, so that
+ * the DB package can use it for on-disk caching and/or to snapshot the
+ * file. When the file is first modified, the mail recovery file is created,
+ * the backing file permissions are updated, the file is sync(2)'d to disk,
+ * and the timer is started. Then, at RCV_PERIOD second intervals, the
+ * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
+ * means that the data structures (SCR, EXF, the underlying tree structures)
+ * must be consistent when the signal arrives.
+ *
+ * The recovery mail file contains normal mail headers, with two additions,
+ * which occur in THIS order, as the FIRST TWO headers:
+ *
+ * X-vi-recover-file: file_name
+ * X-vi-recover-path: recover_path
+ *
+ * Since newlines delimit the headers, this means that file names cannot have
+ * newlines in them, but that's probably okay. As these files aren't intended
+ * to be long-lived, changing their format won't be too painful.
+ *
+ * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
+ */
+
+#define VI_FHEADER "X-vi-recover-file: "
+#define VI_PHEADER "X-vi-recover-path: "
+
+static int rcv_copy __P((SCR *, int, char *));
+static void rcv_email __P((SCR *, char *));
+static char *rcv_gets __P((char *, size_t, int));
+static int rcv_mailfile __P((SCR *, int, char *));
+static int rcv_mktemp __P((SCR *, char *, char *, int));
+
+/*
+ * rcv_tmp --
+ * Build a file name that will be used as the recovery file.
+ *
+ * PUBLIC: int rcv_tmp __P((SCR *, EXF *, char *));
+ */
+int
+rcv_tmp(sp, ep, name)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+{
+ struct stat sb;
+ int fd;
+ char *dp, *p, path[MAXPATHLEN];
+
+ /*
+ * !!!
+ * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
+ *
+ *
+ * If the recovery directory doesn't exist, try and create it. As
+ * the recovery files are themselves protected from reading/writing
+ * by other than the owner, the worst that can happen is that a user
+ * would have permission to remove other user's recovery files. If
+ * the sticky bit has the BSD semantics, that too will be impossible.
+ */
+ if (opts_empty(sp, O_RECDIR, 0))
+ goto err;
+ dp = O_STR(sp, O_RECDIR);
+ if (stat(dp, &sb)) {
+ if (errno != ENOENT || mkdir(dp, 0)) {
+ msgq(sp, M_SYSERR, "%s", dp);
+ goto err;
+ }
+ (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
+ }
+
+ /* Newlines delimit the mail messages. */
+ for (p = name; *p; ++p)
+ if (*p == '\n') {
+ msgq(sp, M_ERR,
+ "055|Files with newlines in the name are unrecoverable");
+ goto err;
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, path, dp, S_IRWXU)) == -1)
+ goto err;
+ (void)close(fd);
+
+ if ((ep->rcv_path = strdup(path)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(path);
+err: msgq(sp, M_ERR,
+ "056|Modifications not recoverable if the session fails");
+ return (1);
+ }
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_init --
+ * Force the file to be snapshotted for recovery.
+ *
+ * PUBLIC: int rcv_init __P((SCR *));
+ */
+int
+rcv_init(sp)
+ SCR *sp;
+{
+ EXF *ep;
+ recno_t lno;
+
+ ep = sp->ep;
+
+ /* Only do this once. */
+ F_CLR(ep, F_FIRSTMODIFY);
+
+ /* If we already know the file isn't recoverable, we're done. */
+ if (!F_ISSET(ep, F_RCV_ON))
+ return (0);
+
+ /* Turn off recoverability until we figure out if this will work. */
+ F_CLR(ep, F_RCV_ON);
+
+ /* Test if we're recovering a file, not editing one. */
+ if (ep->rcv_mpath == NULL) {
+ /* Build a file to mail to the user. */
+ if (rcv_mailfile(sp, 0, NULL))
+ goto err;
+
+ /* Force a read of the entire file. */
+ if (db_last(sp, &lno))
+ goto err;
+
+ /* Turn on a busy message, and sync it to backing store. */
+ sp->gp->scr_busy(sp,
+ "057|Copying file for recovery...", BUSY_ON);
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ msgq_str(sp, M_SYSERR, ep->rcv_path,
+ "058|Preservation failed: %s");
+ sp->gp->scr_busy(sp, NULL, BUSY_OFF);
+ goto err;
+ }
+ sp->gp->scr_busy(sp, NULL, BUSY_OFF);
+ }
+
+ /* Turn off the owner execute bit. */
+ (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+
+err: msgq(sp, M_ERR,
+ "059|Modifications not recoverable if the session fails");
+ return (1);
+}
+
+/*
+ * rcv_sync --
+ * Sync the file, optionally:
+ * flagging the backup file to be preserved
+ * snapshotting the backup file and send email to the user
+ * sending email to the user if the file was modified
+ * ending the file session
+ *
+ * PUBLIC: int rcv_sync __P((SCR *, u_int));
+ */
+int
+rcv_sync(sp, flags)
+ SCR *sp;
+ u_int flags;
+{
+ EXF *ep;
+ int fd, rval;
+ char *dp, buf[1024];
+
+ /* Make sure that there's something to recover/sync. */
+ ep = sp->ep;
+ if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
+ return (0);
+
+ /* Sync the file if it's been modified. */
+ if (F_ISSET(ep, F_MODIFIED)) {
+ SIGBLOCK;
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ F_CLR(ep, F_RCV_ON | F_RCV_NORM);
+ msgq_str(sp, M_SYSERR,
+ ep->rcv_path, "060|File backup failed: %s");
+ SIGUNBLOCK;
+ return (1);
+ }
+ SIGUNBLOCK;
+
+ /* REQUEST: don't remove backing file on exit. */
+ if (LF_ISSET(RCV_PRESERVE))
+ F_SET(ep, F_RCV_NORM);
+
+ /* REQUEST: send email. */
+ if (LF_ISSET(RCV_EMAIL))
+ rcv_email(sp, ep->rcv_mpath);
+ }
+
+ /*
+ * !!!
+ * Each time the user exec's :preserve, we have to snapshot all of
+ * the recovery information, i.e. it's like the user re-edited the
+ * file. We copy the DB(3) backing file, and then create a new mail
+ * recovery file, it's simpler than exiting and reopening all of the
+ * underlying files.
+ *
+ * REQUEST: snapshot the file.
+ */
+ rval = 0;
+ if (LF_ISSET(RCV_SNAPSHOT)) {
+ if (opts_empty(sp, O_RECDIR, 0))
+ goto err;
+ dp = O_STR(sp, O_RECDIR);
+ (void)snprintf(buf, sizeof(buf), "%s/vi.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, buf, dp, S_IRUSR | S_IWUSR)) == -1)
+ goto err;
+ sp->gp->scr_busy(sp,
+ "061|Copying file for recovery...", BUSY_ON);
+ if (rcv_copy(sp, fd, ep->rcv_path) ||
+ close(fd) || rcv_mailfile(sp, 1, buf)) {
+ (void)unlink(buf);
+ (void)close(fd);
+ rval = 1;
+ }
+ sp->gp->scr_busy(sp, NULL, BUSY_OFF);
+ }
+ if (0) {
+err: rval = 1;
+ }
+
+ /* REQUEST: end the file session. */
+ if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
+ rval = 1;
+
+ return (rval);
+}
+
+/*
+ * rcv_mailfile --
+ * Build the file to mail to the user.
+ */
+static int
+rcv_mailfile(sp, issync, cp_path)
+ SCR *sp;
+ int issync;
+ char *cp_path;
+{
+ EXF *ep;
+ GS *gp;
+ struct passwd *pw;
+ size_t len;
+ time_t now;
+ uid_t uid;
+ int fd;
+ char *dp, *p, *t, buf[4096], mpath[MAXPATHLEN];
+ char *t1, *t2, *t3;
+
+ /*
+ * XXX
+ * MAXHOSTNAMELEN is in various places on various systems, including
+ * <netdb.h> and <sys/socket.h>. If not found, use a large default.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 1024
+#endif
+ char host[MAXHOSTNAMELEN];
+
+ gp = sp->gp;
+ if ((pw = getpwuid(uid = getuid())) == NULL) {
+ msgq(sp, M_ERR,
+ "062|Information on user id %u not found", uid);
+ return (1);
+ }
+
+ if (opts_empty(sp, O_RECDIR, 0))
+ return (1);
+ dp = O_STR(sp, O_RECDIR);
+ (void)snprintf(mpath, sizeof(mpath), "%s/recover.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, mpath, dp, S_IRUSR | S_IWUSR)) == -1)
+ return (1);
+
+ /*
+ * XXX
+ * We keep an open lock on the file so that the recover option can
+ * distinguish between files that are live and those that need to
+ * be recovered. There's an obvious window between the mkstemp call
+ * and the lock, but it's pretty small.
+ */
+ ep = sp->ep;
+ if (file_lock(sp, NULL, NULL, fd, 1) != LOCK_SUCCESS)
+ msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
+ if (!issync) {
+ /* Save the recover file descriptor, and mail path. */
+ ep->rcv_fd = fd;
+ if ((ep->rcv_mpath = strdup(mpath)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ }
+ cp_path = ep->rcv_path;
+ }
+
+ /*
+ * XXX
+ * We can't use stdio(3) here. The problem is that we may be using
+ * fcntl(2), so if ANY file descriptor into the file is closed, the
+ * lock is lost. So, we could never close the FILE *, even if we
+ * dup'd the fd first.
+ */
+ t = sp->frp->name;
+ if ((p = strrchr(t, '/')) == NULL)
+ p = t;
+ else
+ ++p;
+ (void)time(&now);
+ (void)gethostname(host, sizeof(host));
+ len = snprintf(buf, sizeof(buf),
+ "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
+ VI_FHEADER, t, /* Non-standard. */
+ VI_PHEADER, cp_path, /* Non-standard. */
+ "Reply-To: root",
+ "From: root (Nvi recovery program)",
+ "To: ", pw->pw_name,
+ "Subject: Nvi saved the file ", p,
+ "Precedence: bulk"); /* For vacation(1). */
+ if (len > sizeof(buf) - 1)
+ goto lerr;
+ if (write(fd, buf, len) != len)
+ goto werr;
+
+ len = snprintf(buf, sizeof(buf),
+ "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
+ "On ", ctime(&now), ", the user ", pw->pw_name,
+ " was editing a file named ", t, " on the machine ",
+ host, ", when it was saved for recovery. ",
+ "You can recover most, if not all, of the changes ",
+ "to this file using the -r option to ", gp->progname, ":\n\n\t",
+ gp->progname, " -r ", t);
+ if (len > sizeof(buf) - 1) {
+lerr: msgq(sp, M_ERR, "064|Recovery file buffer overrun");
+ goto err;
+ }
+
+ /*
+ * Format the message. (Yes, I know it's silly.)
+ * Requires that the message end in a <newline>.
+ */
+#define FMTCOLS 60
+ for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
+ /* Check for a short length. */
+ if (len <= FMTCOLS) {
+ t2 = t1 + (len - 1);
+ goto wout;
+ }
+
+ /* Check for a required <newline>. */
+ t2 = strchr(t1, '\n');
+ if (t2 - t1 <= FMTCOLS)
+ goto wout;
+
+ /* Find the closest space, if any. */
+ for (t3 = t2; t2 > t1; --t2)
+ if (*t2 == ' ') {
+ if (t2 - t1 <= FMTCOLS)
+ goto wout;
+ t3 = t2;
+ }
+ t2 = t3;
+
+ /* t2 points to the last character to display. */
+wout: *t2++ = '\n';
+
+ /* t2 points one after the last character to display. */
+ if (write(fd, t1, t2 - t1) != t2 - t1)
+ goto werr;
+ }
+
+ if (issync) {
+ rcv_email(sp, mpath);
+ if (close(fd)) {
+werr: msgq(sp, M_SYSERR, "065|Recovery file");
+ goto err;
+ }
+ }
+ return (0);
+
+err: if (!issync)
+ ep->rcv_fd = -1;
+ if (fd != -1)
+ (void)close(fd);
+ return (1);
+}
+
+/*
+ * people making love
+ * never exactly the same
+ * just like a snowflake
+ *
+ * rcv_list --
+ * List the files that can be recovered by this user.
+ *
+ * PUBLIC: int rcv_list __P((SCR *));
+ */
+int
+rcv_list(sp)
+ SCR *sp;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ FILE *fp;
+ int found;
+ char *p, *t, file[MAXPATHLEN], path[MAXPATHLEN];
+
+ /* Open the recovery directory for reading. */
+ if (opts_empty(sp, O_RECDIR, 0))
+ return (1);
+ p = O_STR(sp, O_RECDIR);
+ if (chdir(p) || (dirp = opendir(".")) == NULL) {
+ msgq_str(sp, M_SYSERR, p, "recdir: %s");
+ return (1);
+ }
+
+ /* Read the directory. */
+ for (found = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+
+ /*
+ * If it's readable, it's recoverable.
+ *
+ * XXX
+ * Should be "r", we don't want to write the file. However,
+ * if we're using fcntl(2), there's no way to lock a file
+ * descriptor that's not open for writing.
+ */
+ if ((fp = fopen(dp->d_name, "r+")) == NULL)
+ continue;
+
+ switch (file_lock(sp, NULL, NULL, fileno(fp), 1)) {
+ case LOCK_FAILED:
+ /*
+ * XXX
+ * Assume that a lock can't be acquired, but that we
+ * should permit recovery anyway. If this is wrong,
+ * and someone else is using the file, we're going to
+ * die horribly.
+ */
+ break;
+ case LOCK_SUCCESS:
+ break;
+ case LOCK_UNAVAIL:
+ /* If it's locked, it's live. */
+ (void)fclose(fp);
+ continue;
+ }
+
+ /* Check the headers. */
+ if (fgets(file, sizeof(file), fp) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL ||
+ fgets(path, sizeof(path), fp) == NULL ||
+ strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+ (t = strchr(path, '\n')) == NULL) {
+ msgq_str(sp, M_ERR, dp->d_name,
+ "066|%s: malformed recovery file");
+ goto next;
+ }
+ *p = *t = '\0';
+
+ /*
+ * If the file doesn't exist, it's an orphaned recovery file,
+ * toss it.
+ *
+ * XXX
+ * This can occur if the backup file was deleted and we crashed
+ * before deleting the email file.
+ */
+ errno = 0;
+ if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
+ errno == ENOENT) {
+ (void)unlink(dp->d_name);
+ goto next;
+ }
+
+ /* Get the last modification time and display. */
+ (void)fstat(fileno(fp), &sb);
+ (void)printf("%.24s: %s\n",
+ ctime(&sb.st_mtime), file + sizeof(VI_FHEADER) - 1);
+ found = 1;
+
+ /* Close, discarding lock. */
+next: (void)fclose(fp);
+ }
+ if (found == 0)
+ (void)printf("vi: no files to recover.\n");
+ (void)closedir(dirp);
+ return (0);
+}
+
+/*
+ * rcv_read --
+ * Start a recovered file as the file to edit.
+ *
+ * PUBLIC: int rcv_read __P((SCR *, FREF *));
+ */
+int
+rcv_read(sp, frp)
+ SCR *sp;
+ FREF *frp;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ EXF *ep;
+ time_t rec_mtime;
+ int fd, found, locked, requested, sv_fd;
+ char *name, *p, *t, *rp, *recp, *pathp;
+ char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
+
+ if (opts_empty(sp, O_RECDIR, 0))
+ return (1);
+ rp = O_STR(sp, O_RECDIR);
+ if ((dirp = opendir(rp)) == NULL) {
+ msgq_str(sp, M_ERR, rp, "%s");
+ return (1);
+ }
+
+ name = frp->name;
+ sv_fd = -1;
+ rec_mtime = 0;
+ recp = pathp = NULL;
+ for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+ (void)snprintf(recpath,
+ sizeof(recpath), "%s/%s", rp, dp->d_name);
+
+ /*
+ * If it's readable, it's recoverable. It would be very
+ * nice to use stdio(3), but, we can't because that would
+ * require closing and then reopening the file so that we
+ * could have a lock and still close the FP. Another tip
+ * of the hat to fcntl(2).
+ *
+ * XXX
+ * Should be O_RDONLY, we don't want to write it. However,
+ * if we're using fcntl(2), there's no way to lock a file
+ * descriptor that's not open for writing.
+ */
+ if ((fd = open(recpath, O_RDWR, 0)) == -1)
+ continue;
+
+ switch (file_lock(sp, NULL, NULL, fd, 1)) {
+ case LOCK_FAILED:
+ /*
+ * XXX
+ * Assume that a lock can't be acquired, but that we
+ * should permit recovery anyway. If this is wrong,
+ * and someone else is using the file, we're going to
+ * die horribly.
+ */
+ locked = 0;
+ break;
+ case LOCK_SUCCESS:
+ locked = 1;
+ break;
+ case LOCK_UNAVAIL:
+ /* If it's locked, it's live. */
+ (void)close(fd);
+ continue;
+ }
+
+ /* Check the headers. */
+ if (rcv_gets(file, sizeof(file), fd) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL ||
+ rcv_gets(path, sizeof(path), fd) == NULL ||
+ strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+ (t = strchr(path, '\n')) == NULL) {
+ msgq_str(sp, M_ERR, recpath,
+ "067|%s: malformed recovery file");
+ goto next;
+ }
+ *p = *t = '\0';
+ ++found;
+
+ /*
+ * If the file doesn't exist, it's an orphaned recovery file,
+ * toss it.
+ *
+ * XXX
+ * This can occur if the backup file was deleted and we crashed
+ * before deleting the email file.
+ */
+ errno = 0;
+ if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
+ errno == ENOENT) {
+ (void)unlink(dp->d_name);
+ goto next;
+ }
+
+ /* Check the file name. */
+ if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
+ goto next;
+
+ ++requested;
+
+ /*
+ * If we've found more than one, take the most recent.
+ *
+ * XXX
+ * Since we're using st_mtime, for portability reasons,
+ * we only get a single second granularity, instead of
+ * getting it right.
+ */
+ (void)fstat(fd, &sb);
+ if (recp == NULL || rec_mtime < sb.st_mtime) {
+ p = recp;
+ t = pathp;
+ if ((recp = strdup(recpath)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ recp = p;
+ goto next;
+ }
+ if ((pathp = strdup(path)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ free(recp);
+ recp = p;
+ pathp = t;
+ goto next;
+ }
+ if (p != NULL) {
+ free(p);
+ free(t);
+ }
+ rec_mtime = sb.st_mtime;
+ if (sv_fd != -1)
+ (void)close(sv_fd);
+ sv_fd = fd;
+ } else
+next: (void)close(fd);
+ }
+ (void)closedir(dirp);
+
+ if (recp == NULL) {
+ msgq_str(sp, M_INFO, name,
+ "068|No files named %s, readable by you, to recover");
+ return (1);
+ }
+ if (found) {
+ if (requested > 1)
+ msgq(sp, M_INFO,
+ "069|There are older versions of this file for you to recover");
+ if (found > requested)
+ msgq(sp, M_INFO,
+ "070|There are other files for you to recover");
+ }
+
+ /*
+ * Create the FREF structure, start the btree file.
+ *
+ * XXX
+ * file_init() is going to set ep->rcv_path.
+ */
+ if (file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
+ free(recp);
+ free(pathp);
+ (void)close(sv_fd);
+ return (1);
+ }
+
+ /*
+ * We keep an open lock on the file so that the recover option can
+ * distinguish between files that are live and those that need to
+ * be recovered. The lock is already acquired, just copy it.
+ */
+ ep = sp->ep;
+ ep->rcv_mpath = recp;
+ ep->rcv_fd = sv_fd;
+ if (!locked)
+ F_SET(frp, FR_UNLOCKED);
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_copy --
+ * Copy a recovery file.
+ */
+static int
+rcv_copy(sp, wfd, fname)
+ SCR *sp;
+ int wfd;
+ char *fname;
+{
+ int nr, nw, off, rfd;
+ char buf[8 * 1024];
+
+ if ((rfd = open(fname, O_RDONLY, 0)) == -1)
+ goto err;
+ while ((nr = read(rfd, buf, sizeof(buf))) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, nr)) < 0)
+ goto err;
+ if (nr == 0)
+ return (0);
+
+err: msgq_str(sp, M_SYSERR, fname, "%s");
+ return (1);
+}
+
+/*
+ * rcv_gets --
+ * Fgets(3) for a file descriptor.
+ */
+static char *
+rcv_gets(buf, len, fd)
+ char *buf;
+ size_t len;
+ int fd;
+{
+ int nr;
+ char *p;
+
+ if ((nr = read(fd, buf, len - 1)) == -1)
+ return (NULL);
+ if ((p = strchr(buf, '\n')) == NULL)
+ return (NULL);
+ (void)lseek(fd, (off_t)((p - buf) + 1), SEEK_SET);
+ return (buf);
+}
+
+/*
+ * rcv_mktemp --
+ * Paranoid make temporary file routine.
+ */
+static int
+rcv_mktemp(sp, path, dname, perms)
+ SCR *sp;
+ char *path, *dname;
+ int perms;
+{
+ int fd;
+
+ /*
+ * !!!
+ * We expect mkstemp(3) to set the permissions correctly. On
+ * historic System V systems, mkstemp didn't. Do it here, on
+ * GP's.
+ *
+ * XXX
+ * The variable perms should really be a mode_t, and it would
+ * be nice to use fchmod(2) instead of chmod(2), here.
+ */
+ if ((fd = mkstemp(path)) == -1)
+ msgq_str(sp, M_SYSERR, dname, "%s");
+ else
+ (void)chmod(path, perms);
+ return (fd);
+}
+
+/*
+ * rcv_email --
+ * Send email.
+ */
+static void
+rcv_email(sp, fname)
+ SCR *sp;
+ char *fname;
+{
+ struct stat sb;
+ char buf[MAXPATHLEN * 2 + 20];
+
+ if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb))
+ msgq_str(sp, M_SYSERR,
+ _PATH_SENDMAIL, "071|not sending email: %s");
+ else {
+ /*
+ * !!!
+ * If you need to port this to a system that doesn't have
+ * sendmail, the -t flag causes sendmail to read the message
+ * for the recipients instead of specifying them some other
+ * way.
+ */
+ (void)snprintf(buf, sizeof(buf),
+ "%s -t < %s", _PATH_SENDMAIL, fname);
+ (void)system(buf);
+ }
+}
diff --git a/contrib/nvi/common/screen.c b/contrib/nvi/common/screen.c
new file mode 100644
index 000000000000..ba9e287b648b
--- /dev/null
+++ b/contrib/nvi/common/screen.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)screen.c 10.15 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../vi/vi.h"
+
+/*
+ * screen_init --
+ * Do the default initialization of an SCR structure.
+ *
+ * PUBLIC: int screen_init __P((GS *, SCR *, SCR **));
+ */
+int
+screen_init(gp, orig, spp)
+ GS *gp;
+ SCR *orig, **spp;
+{
+ SCR *sp;
+ size_t len;
+
+ *spp = NULL;
+ CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
+ *spp = sp;
+
+/* INITIALIZED AT SCREEN CREATE. */
+ sp->id = ++gp->id;
+ sp->refcnt = 1;
+
+ sp->gp = gp; /* All ref the GS structure. */
+
+ sp->ccnt = 2; /* Anything > 1 */
+
+ /*
+ * XXX
+ * sp->defscroll is initialized by the opts_init() code because
+ * we don't have the option information yet.
+ */
+
+ CIRCLEQ_INIT(&sp->tiq);
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ sp->searchdir = NOTSET;
+ } else {
+ /* Alternate file name. */
+ if (orig->alt_name != NULL &&
+ (sp->alt_name = strdup(orig->alt_name)) == NULL)
+ goto mem;
+
+ /* Last executed at buffer. */
+ if (F_ISSET(orig, SC_AT_SET)) {
+ F_SET(sp, SC_AT_SET);
+ sp->at_lbuf = orig->at_lbuf;
+ }
+
+ /* Retain searching/substitution information. */
+ sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
+ if (orig->re != NULL && (sp->re =
+ v_strdup(sp, orig->re, orig->re_len)) == NULL)
+ goto mem;
+ sp->re_len = orig->re_len;
+ if (orig->subre != NULL && (sp->subre =
+ v_strdup(sp, orig->subre, orig->subre_len)) == NULL)
+ goto mem;
+ sp->subre_len = orig->subre_len;
+ if (orig->repl != NULL && (sp->repl =
+ v_strdup(sp, orig->repl, orig->repl_len)) == NULL)
+ goto mem;
+ sp->repl_len = orig->repl_len;
+ if (orig->newl_len) {
+ len = orig->newl_len * sizeof(size_t);
+ MALLOC(sp, sp->newl, size_t *, len);
+ if (sp->newl == NULL) {
+mem: msgq(orig, M_SYSERR, NULL);
+ goto err;
+ }
+ sp->newl_len = orig->newl_len;
+ sp->newl_cnt = orig->newl_cnt;
+ memcpy(sp->newl, orig->newl, len);
+ }
+
+ if (opts_copy(orig, sp))
+ goto err;
+
+ F_SET(sp, F_ISSET(orig, SC_EX | SC_VI));
+ }
+
+ if (ex_screen_copy(orig, sp)) /* Ex. */
+ goto err;
+ if (v_screen_copy(orig, sp)) /* Vi. */
+ goto err;
+
+ *spp = sp;
+ return (0);
+
+err: screen_end(sp);
+ return (1);
+}
+
+/*
+ * screen_end --
+ * Release a screen, no matter what had (and had not) been
+ * initialized.
+ *
+ * PUBLIC: int screen_end __P((SCR *));
+ */
+int
+screen_end(sp)
+ SCR *sp;
+{
+ int rval;
+
+ /* If multiply referenced, just decrement the count and return. */
+ if (--sp->refcnt != 0)
+ return (0);
+
+ /*
+ * Remove the screen from the displayed queue.
+ *
+ * If a created screen failed during initialization, it may not
+ * be linked into the chain.
+ */
+ if (sp->q.cqe_next != NULL)
+ CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
+
+ /* The screen is no longer real. */
+ F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
+
+ rval = 0;
+#ifdef HAVE_PERL_INTERP
+ if (perl_screen_end(sp)) /* End perl. */
+ rval = 1;
+#endif
+ if (v_screen_end(sp)) /* End vi. */
+ rval = 1;
+ if (ex_screen_end(sp)) /* End ex. */
+ rval = 1;
+
+ /* Free file names. */
+ { char **ap;
+ if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) {
+ for (ap = sp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(sp->argv);
+ }
+ }
+
+ /* Free any text input. */
+ if (sp->tiq.cqh_first != NULL)
+ text_lfree(&sp->tiq);
+
+ /* Free alternate file name. */
+ if (sp->alt_name != NULL)
+ free(sp->alt_name);
+
+ /* Free up search information. */
+ if (sp->re != NULL)
+ free(sp->re);
+ if (F_ISSET(sp, SC_RE_SEARCH))
+ regfree(&sp->re_c);
+ if (sp->subre != NULL)
+ free(sp->subre);
+ if (F_ISSET(sp, SC_RE_SUBST))
+ regfree(&sp->subre_c);
+ if (sp->repl != NULL)
+ free(sp->repl);
+ if (sp->newl != NULL)
+ free(sp->newl);
+
+ /* Free all the options */
+ opts_free(sp);
+
+ /* Free the screen itself. */
+ free(sp);
+
+ return (rval);
+}
+
+/*
+ * screen_next --
+ * Return the next screen in the queue.
+ *
+ * PUBLIC: SCR *screen_next __P((SCR *));
+ */
+SCR *
+screen_next(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SCR *next;
+
+ /* Try the display queue, without returning the current screen. */
+ gp = sp->gp;
+ for (next = gp->dq.cqh_first;
+ next != (void *)&gp->dq; next = next->q.cqe_next)
+ if (next != sp)
+ break;
+ if (next != (void *)&gp->dq)
+ return (next);
+
+ /* Try the hidden queue; if found, move screen to the display queue. */
+ if (gp->hq.cqh_first != (void *)&gp->hq) {
+ next = gp->hq.cqh_first;
+ CIRCLEQ_REMOVE(&gp->hq, next, q);
+ CIRCLEQ_INSERT_HEAD(&gp->dq, next, q);
+ return (next);
+ }
+ return (NULL);
+}
diff --git a/contrib/nvi/common/screen.h b/contrib/nvi/common/screen.h
new file mode 100644
index 000000000000..bb7254f62a21
--- /dev/null
+++ b/contrib/nvi/common/screen.h
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)screen.h 10.24 (Berkeley) 7/19/96
+ */
+
+/*
+ * There are minimum values that vi has to have to display a screen. The row
+ * minimum is fixed at 1 (the svi code can share a line between the text line
+ * and the colon command/message line). Column calculation is a lot trickier.
+ * For example, you have to have enough columns to display the line number,
+ * not to mention guaranteeing that tabstop and shiftwidth values are smaller
+ * than the current column value. It's simpler to have a fixed value and not
+ * worry about it.
+ *
+ * XXX
+ * MINIMUM_SCREEN_COLS is almost certainly wrong.
+ */
+#define MINIMUM_SCREEN_ROWS 1
+#define MINIMUM_SCREEN_COLS 20
+
+/*
+ * 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. */
+
+ int id; /* Screen id #. */
+ int refcnt; /* Reference count. */
+
+ GS *gp; /* Pointer to global area. */
+ SCR *nextdisp; /* Next display screen. */
+ SCR *ccl_parent; /* Colon command-line parent screen. */
+ EXF *ep; /* Screen's current EXF structure. */
+
+ FREF *frp; /* FREF being edited. */
+ char **argv; /* NULL terminated file name array. */
+ char **cargv; /* Current file name. */
+
+ u_long ccnt; /* Command count. */
+ u_long q_ccnt; /* Quit or ZZ command count. */
+
+ /* Screen's: */
+ size_t rows; /* 1-N: number of rows. */
+ size_t cols; /* 1-N: number of columns. */
+ size_t t_rows; /* 1-N: cur number of text rows. */
+ size_t t_maxrows; /* 1-N: max number of text rows. */
+ size_t t_minrows; /* 1-N: min number of text rows. */
+ size_t woff; /* 0-N: screen offset in frame. */
+
+ /* Cursor's: */
+ recno_t lno; /* 1-N: file line. */
+ size_t cno; /* 0-N: file character in line. */
+
+ size_t rcm; /* Vi: 0-N: Most attractive column. */
+
+#define L_ADDED 0 /* Added lines. */
+#define L_CHANGED 1 /* Changed lines. */
+#define L_DELETED 2 /* Deleted lines. */
+#define L_JOINED 3 /* Joined lines. */
+#define L_MOVED 4 /* Moved lines. */
+#define L_SHIFT 5 /* Shift lines. */
+#define L_YANKED 6 /* Yanked lines. */
+ recno_t rptlchange; /* Ex/vi: last L_CHANGED lno. */
+ recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
+
+ TEXTH tiq; /* Ex/vi: text input queue. */
+
+ SCRIPT *script; /* Vi: script mode information .*/
+
+ recno_t defscroll; /* Vi: ^D, ^U scroll information. */
+
+ /* Display character. */
+ CHAR_T cname[MAX_CHARACTER_COLUMNS + 1];
+ size_t clen; /* Length of display character. */
+
+ enum { /* Vi editor mode. */
+ SM_APPEND = 0, SM_CHANGE, SM_COMMAND, SM_INSERT,
+ SM_REPLACE } showmode;
+
+ void *ex_private; /* Ex private area. */
+ void *vi_private; /* Vi private area. */
+ void *perl_private; /* Perl private area. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ char *alt_name; /* Ex/vi: alternate file name. */
+
+ CHAR_T at_lbuf; /* Ex/vi: Last executed at buffer. */
+
+ /* Ex/vi: re_compile flags. */
+#define RE_C_CSCOPE 0x0001 /* Compile cscope pattern. */
+#define RE_C_SEARCH 0x0002 /* Compile search replacement. */
+#define RE_C_SILENT 0x0004 /* No error messages. */
+#define RE_C_SUBST 0x0008 /* Compile substitute replacement. */
+#define RE_C_TAG 0x0010 /* Compile ctag pattern. */
+
+#define RE_WSTART "[[:<:]]" /* Ex/vi: not-in-word search pattern. */
+#define RE_WSTOP "[[:>:]]"
+ /* Ex/vi: flags to search routines. */
+#define SEARCH_CSCOPE 0x0001 /* Search for a cscope pattern. */
+#define SEARCH_EOL 0x0002 /* Offset past EOL is okay. */
+#define SEARCH_FILE 0x0004 /* Search the entire file. */
+#define SEARCH_INCR 0x0008 /* Search incrementally. */
+#define SEARCH_MSG 0x0010 /* Display search messages. */
+#define SEARCH_PARSE 0x0020 /* Parse the search pattern. */
+#define SEARCH_SET 0x0040 /* Set search direction. */
+#define SEARCH_TAG 0x0080 /* Search for a tag pattern. */
+#define SEARCH_WMSG 0x0100 /* Display search-wrapped messages. */
+
+ /* Ex/vi: RE information. */
+ dir_t searchdir; /* Last file search direction. */
+ regex_t re_c; /* Search RE: compiled form. */
+ char *re; /* Search RE: uncompiled form. */
+ size_t re_len; /* Search RE: uncompiled length. */
+ regex_t subre_c; /* Substitute RE: compiled form. */
+ char *subre; /* Substitute RE: uncompiled form. */
+ size_t subre_len; /* Substitute RE: uncompiled length). */
+ 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_int8_t c_suffix; /* Edcompatible 'c' suffix value. */
+ u_int8_t g_suffix; /* Edcompatible 'g' suffix value. */
+
+ OPTION opts[O_OPTIONCOUNT]; /* Ex/vi: Options. */
+
+/*
+ * Screen flags.
+ *
+ * Editor screens.
+ */
+#define SC_EX 0x00000001 /* Ex editor. */
+#define SC_VI 0x00000002 /* Vi editor. */
+
+/*
+ * Screen formatting flags, first major, then minor.
+ *
+ * SC_SCR_EX
+ * Ex screen, i.e. cooked mode.
+ * SC_SCR_VI
+ * Vi screen, i.e. raw mode.
+ * SC_SCR_EXWROTE
+ * The editor had to write on the screen behind curses' back, and we can't
+ * let curses change anything until the user agrees, e.g. entering the
+ * commands :!utility followed by :set. We have to switch back into the
+ * vi "editor" to read the user's command input, but we can't touch the
+ * rest of the screen because it's known to be wrong.
+ * SC_SCR_REFORMAT
+ * The expected presentation of the lines on the screen have changed,
+ * requiring that the intended screen lines be recalculated. Implies
+ * SC_SCR_REDRAW.
+ * SC_SCR_REDRAW
+ * The screen doesn't correctly represent the file; repaint it. Note,
+ * setting SC_SCR_REDRAW in the current window causes *all* windows to
+ * be repainted.
+ * SC_SCR_CENTER
+ * If the current line isn't already on the screen, center it.
+ * SC_SCR_TOP
+ * If the current line isn't already on the screen, put it at the to@.
+ */
+#define SC_SCR_EX 0x00000004 /* Screen is in ex mode. */
+#define SC_SCR_VI 0x00000008 /* Screen is in vi mode. */
+#define SC_SCR_EXWROTE 0x00000010 /* Ex overwrite: see comment above. */
+#define SC_SCR_REFORMAT 0x00000020 /* Reformat (refresh). */
+#define SC_SCR_REDRAW 0x00000040 /* Refresh. */
+
+#define SC_SCR_CENTER 0x00000080 /* Center the line if not visible. */
+#define SC_SCR_TOP 0x00000100 /* Top the line if not visible. */
+
+/* Screen/file changes. */
+#define SC_EXIT 0x00000200 /* Exiting (not forced). */
+#define SC_EXIT_FORCE 0x00000400 /* Exiting (forced). */
+#define SC_FSWITCH 0x00000800 /* Switch underlying files. */
+#define SC_SSWITCH 0x00001000 /* Switch screens. */
+
+#define SC_ARGNOFREE 0x00002000 /* Argument list wasn't allocated. */
+#define SC_ARGRECOVER 0x00004000 /* Argument list is recovery files. */
+#define SC_AT_SET 0x00008000 /* Last at buffer set. */
+#define SC_COMEDIT 0x00010000 /* Colon command-line edit window. */
+#define SC_EX_GLOBAL 0x00020000 /* Ex: executing a global command. */
+#define SC_EX_SILENT 0x00040000 /* Ex: batch script. */
+#define SC_EX_WAIT_NO 0x00080000 /* Ex: don't wait for the user. */
+#define SC_EX_WAIT_YES 0x00100000 /* Ex: do wait for the user. */
+#define SC_READONLY 0x00200000 /* Persistent readonly state. */
+#define SC_RE_SEARCH 0x00400000 /* Search RE has been compiled. */
+#define SC_RE_SUBST 0x00800000 /* Substitute RE has been compiled. */
+#define SC_SCRIPT 0x01000000 /* Shell script window. */
+#define SC_STATUS 0x02000000 /* Welcome message. */
+#define SC_STATUS_CNT 0x04000000 /* Welcome message plus file count. */
+#define SC_TINPUT 0x08000000 /* Doing text input. */
+#define SC_TINPUT_INFO 0x10000000 /* Doing text input on info line. */
+ u_int32_t flags;
+};
diff --git a/contrib/nvi/common/search.c b/contrib/nvi/common/search.c
new file mode 100644
index 000000000000..3fd2719778fa
--- /dev/null
+++ b/contrib/nvi/common/search.c
@@ -0,0 +1,492 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)search.c 10.25 (Berkeley) 6/30/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+typedef enum { S_EMPTY, S_EOF, S_NOPREV, S_NOTFOUND, S_SOF, S_WRAP } smsg_t;
+
+static void search_msg __P((SCR *, smsg_t));
+static int search_init __P((SCR *, dir_t, char *, size_t, char **, u_int));
+
+/*
+ * search_init --
+ * Set up a search.
+ */
+static int
+search_init(sp, dir, ptrn, plen, epp, flags)
+ SCR *sp;
+ dir_t dir;
+ char *ptrn, **epp;
+ size_t plen;
+ u_int flags;
+{
+ recno_t lno;
+ int delim;
+ char *p, *t;
+
+ /* If the file is empty, it's a fast search. */
+ if (sp->lno <= 1) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_EMPTY);
+ return (1);
+ }
+ }
+
+ if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
+ /*
+ * Use the saved pattern if no pattern specified, or if only
+ * one or two delimiter characters specified.
+ *
+ * !!!
+ * Historically, only the pattern itself was saved, vi didn't
+ * preserve addressing or delta information.
+ */
+ if (ptrn == NULL)
+ goto prev;
+ if (plen == 1) {
+ if (epp != NULL)
+ *epp = ptrn + 1;
+ goto prev;
+ }
+ if (ptrn[0] == ptrn[1]) {
+ if (epp != NULL)
+ *epp = ptrn + 2;
+
+ /* Complain if we don't have a previous pattern. */
+prev: if (sp->re == NULL) {
+ search_msg(sp, S_NOPREV);
+ return (1);
+ }
+ /* Re-compile the search pattern if necessary. */
+ if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
+ sp->re, sp->re_len, NULL, NULL, &sp->re_c,
+ RE_C_SEARCH |
+ (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT)))
+ return (1);
+
+ /* Set the search direction. */
+ if (LF_ISSET(SEARCH_SET))
+ sp->searchdir = dir;
+ return (0);
+ }
+
+ /*
+ * Set the delimiter, and move forward to the terminating
+ * delimiter, handling escaped delimiters.
+ *
+ * QUOTING NOTE:
+ * Only discard an escape character if it escapes a delimiter.
+ */
+ for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
+ if (--plen == 0 || p[0] == delim) {
+ if (plen != 0)
+ ++p;
+ break;
+ }
+ if (plen > 1 && p[0] == '\\' && p[1] == delim) {
+ ++p;
+ --plen;
+ }
+ }
+ if (epp != NULL)
+ *epp = p;
+
+ plen = t - ptrn;
+ }
+
+ /* Compile the RE. */
+ if (re_compile(sp, ptrn, plen, &sp->re, &sp->re_len, &sp->re_c,
+ RE_C_SEARCH |
+ (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT) |
+ (LF_ISSET(SEARCH_TAG) ? RE_C_TAG : 0) |
+ (LF_ISSET(SEARCH_CSCOPE) ? RE_C_CSCOPE : 0)))
+ return (1);
+
+ /* Set the search direction. */
+ if (LF_ISSET(SEARCH_SET))
+ sp->searchdir = dir;
+
+ return (0);
+}
+
+/*
+ * f_search --
+ * Do a forward search.
+ *
+ * PUBLIC: int f_search __P((SCR *,
+ * PUBLIC: MARK *, MARK *, char *, size_t, char **, u_int));
+ */
+int
+f_search(sp, fm, rm, ptrn, plen, eptrn, flags)
+ SCR *sp;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ size_t plen;
+ u_int flags;
+{
+ busy_t btype;
+ recno_t lno;
+ regmatch_t match[1];
+ size_t coff, len;
+ int cnt, eval, rval, wrapped;
+ char *l;
+
+ if (search_init(sp, FORWARD, ptrn, plen, eptrn, flags))
+ return (1);
+
+ if (LF_ISSET(SEARCH_FILE)) {
+ lno = 1;
+ coff = 0;
+ } else {
+ if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
+ return (1);
+ lno = fm->lno;
+
+ /*
+ * If doing incremental search, start searching at the previous
+ * column, so that we search a minimal distance and still match
+ * special patterns, e.g., \< for beginning of a word.
+ *
+ * Otherwise, start searching immediately after the cursor. If
+ * at the end of the line, start searching on the next line.
+ * This is incompatible (read bug fix) with the historic vi --
+ * searches for the '$' pattern never moved forward, and the
+ * "-t foo" didn't work if the 'f' was the first character in
+ * the file.
+ */
+ if (LF_ISSET(SEARCH_INCR)) {
+ if ((coff = fm->cno) != 0)
+ --coff;
+ } else if (fm->cno + 1 >= len) {
+ coff = 0;
+ lno = fm->lno + 1;
+ if (db_get(sp, lno, 0, &l, &len)) {
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_EOF);
+ return (1);
+ }
+ lno = 1;
+ }
+ } else
+ coff = fm->cno + 1;
+ }
+
+ btype = BUSY_ON;
+ for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; ++lno, coff = 0) {
+ if (cnt-- == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (LF_ISSET(SEARCH_MSG)) {
+ search_busy(sp, btype);
+ btype = BUSY_UPDATE;
+ }
+ cnt = INTERRUPT_CHECK;
+ }
+ if (wrapped && lno > fm->lno || db_get(sp, lno, 0, &l, &len)) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_EOF);
+ break;
+ }
+ lno = 0;
+ wrapped = 1;
+ continue;
+ }
+
+ /* If already at EOL, just keep going. */
+ if (len != 0 && coff == len)
+ continue;
+
+ /* Set the termination. */
+ match[0].rm_so = coff;
+ match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "F search: %lu from %u to %u\n",
+ lno, coff, len != 0 ? len - 1 : len);
+#endif
+ /* Search the line. */
+ eval = regexec(&sp->re_c, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
+ break;
+ }
+
+ /* Warn if the search wrapped. */
+ if (wrapped && LF_ISSET(SEARCH_WMSG))
+ search_msg(sp, S_WRAP);
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "F search: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ rm->lno = lno;
+ rm->cno = match[0].rm_so;
+
+ /*
+ * If a change command, it's possible to move beyond the end
+ * of a line. Historic vi generally got this wrong (e.g. try
+ * "c?$<cr>"). Not all that sure this gets it right, there
+ * are lots of strange cases.
+ */
+ if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
+ rm->cno = len != 0 ? len - 1 : 0;
+
+ rval = 0;
+ break;
+ }
+
+ if (LF_ISSET(SEARCH_MSG))
+ search_busy(sp, BUSY_OFF);
+ return (rval);
+}
+
+/*
+ * b_search --
+ * Do a backward search.
+ *
+ * PUBLIC: int b_search __P((SCR *,
+ * PUBLIC: MARK *, MARK *, char *, size_t, char **, u_int));
+ */
+int
+b_search(sp, fm, rm, ptrn, plen, eptrn, flags)
+ SCR *sp;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ size_t plen;
+ u_int flags;
+{
+ busy_t btype;
+ recno_t lno;
+ regmatch_t match[1];
+ size_t coff, last, len;
+ int cnt, eval, rval, wrapped;
+ char *l;
+
+ if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
+ return (1);
+
+ /*
+ * If doing incremental search, set the "starting" position past the
+ * current column, so that we search a minimal distance and still
+ * match special patterns, e.g., \> for the end of a word. This is
+ * safe when the cursor is at the end of a line because we only use
+ * it for comparison with the location of the match.
+ *
+ * Otherwise, start searching immediately before the cursor. If in
+ * the first column, start search on the previous line.
+ */
+ if (LF_ISSET(SEARCH_INCR)) {
+ lno = fm->lno;
+ coff = fm->cno + 1;
+ } else {
+ if (fm->cno == 0) {
+ if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_SOF);
+ return (1);
+ }
+ lno = fm->lno - 1;
+ } else
+ lno = fm->lno;
+ coff = fm->cno;
+ }
+
+ btype = BUSY_ON;
+ for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
+ if (cnt-- == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (LF_ISSET(SEARCH_MSG)) {
+ search_busy(sp, btype);
+ btype = BUSY_UPDATE;
+ }
+ cnt = INTERRUPT_CHECK;
+ }
+ if (wrapped && lno < fm->lno || lno == 0) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_SOF);
+ break;
+ }
+ if (db_last(sp, &lno))
+ break;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ search_msg(sp, S_EMPTY);
+ break;
+ }
+ ++lno;
+ wrapped = 1;
+ continue;
+ }
+
+ if (db_get(sp, lno, 0, &l, &len))
+ break;
+
+ /* Set the termination. */
+ match[0].rm_so = 0;
+ match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
+#endif
+ /* Search the line. */
+ eval = regexec(&sp->re_c, l, 1, match,
+ (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
+ break;
+ }
+
+ /* Check for a match starting past the cursor. */
+ if (coff != 0 && match[0].rm_so >= coff)
+ continue;
+
+ /* Warn if the search wrapped. */
+ if (wrapped && LF_ISSET(SEARCH_WMSG))
+ search_msg(sp, S_WRAP);
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "B found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ /*
+ * We now have the first match on the line. Step through the
+ * line character by character until find the last acceptable
+ * match. This is painful, we need a better interface to regex
+ * to make this work.
+ */
+ for (;;) {
+ last = match[0].rm_so++;
+ if (match[0].rm_so >= len)
+ break;
+ match[0].rm_eo = len;
+ eval = regexec(&sp->re_c, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
+ REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ break;
+ if (eval != 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ re_error(sp, eval, &sp->re_c);
+ else
+ (void)sp->gp->scr_bell(sp);
+ goto err;
+ }
+ if (coff && match[0].rm_so >= coff)
+ break;
+ }
+ rm->lno = lno;
+
+ /* See comment in f_search(). */
+ if (!LF_ISSET(SEARCH_EOL) && last >= len)
+ rm->cno = len != 0 ? len - 1 : 0;
+ else
+ rm->cno = last;
+ rval = 0;
+ break;
+ }
+
+err: if (LF_ISSET(SEARCH_MSG))
+ search_busy(sp, BUSY_OFF);
+ return (rval);
+}
+
+/*
+ * search_msg --
+ * Display one of the search messages.
+ */
+static void
+search_msg(sp, msg)
+ SCR *sp;
+ smsg_t msg;
+{
+ switch (msg) {
+ case S_EMPTY:
+ msgq(sp, M_ERR, "072|File empty; nothing to search");
+ break;
+ case S_EOF:
+ msgq(sp, M_ERR,
+ "073|Reached end-of-file without finding the pattern");
+ break;
+ case S_NOPREV:
+ msgq(sp, M_ERR, "074|No previous search pattern");
+ break;
+ case S_NOTFOUND:
+ msgq(sp, M_ERR, "075|Pattern not found");
+ break;
+ case S_SOF:
+ msgq(sp, M_ERR,
+ "076|Reached top-of-file without finding the pattern");
+ break;
+ case S_WRAP:
+ msgq(sp, M_ERR, "077|Search wrapped");
+ break;
+ default:
+ abort();
+ }
+}
+
+/*
+ * search_busy --
+ * Put up the busy searching message.
+ *
+ * PUBLIC: void search_busy __P((SCR *, busy_t));
+ */
+void
+search_busy(sp, btype)
+ SCR *sp;
+ busy_t btype;
+{
+ sp->gp->scr_busy(sp, "078|Searching...", btype);
+}
diff --git a/contrib/nvi/common/seq.c b/contrib/nvi/common/seq.c
new file mode 100644
index 000000000000..e2be879ab686
--- /dev/null
+++ b/contrib/nvi/common/seq.c
@@ -0,0 +1,395 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)seq.c 10.10 (Berkeley) 3/30/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * seq_set --
+ * Internal version to enter a sequence.
+ *
+ * PUBLIC: int seq_set __P((SCR *, CHAR_T *,
+ * PUBLIC: size_t, CHAR_T *, size_t, CHAR_T *, size_t, seq_t, int));
+ */
+int
+seq_set(sp, name, nlen, input, ilen, output, olen, stype, flags)
+ SCR *sp;
+ CHAR_T *name, *input, *output;
+ size_t nlen, ilen, olen;
+ seq_t stype;
+ int flags;
+{
+ CHAR_T *p;
+ SEQ *lastqp, *qp;
+ int sv_errno;
+
+ /*
+ * An input string must always be present. The output string
+ * can be NULL, when set internally, that's how we throw away
+ * input.
+ *
+ * Just replace the output field if the string already set.
+ */
+ if ((qp =
+ seq_find(sp, &lastqp, NULL, input, ilen, stype, NULL)) != NULL) {
+ if (LF_ISSET(SEQ_NOOVERWRITE))
+ return (0);
+ if (output == NULL || olen == 0) {
+ p = NULL;
+ olen = 0;
+ } else if ((p = v_strdup(sp, output, olen)) == NULL) {
+ sv_errno = errno;
+ goto mem1;
+ }
+ if (qp->output != NULL)
+ free(qp->output);
+ qp->olen = olen;
+ qp->output = p;
+ return (0);
+ }
+
+ /* Allocate and initialize SEQ structure. */
+ CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ));
+ if (qp == NULL) {
+ sv_errno = errno;
+ goto mem1;
+ }
+
+ /* Name. */
+ if (name == NULL || nlen == 0)
+ qp->name = NULL;
+ else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) {
+ sv_errno = errno;
+ goto mem2;
+ }
+ qp->nlen = nlen;
+
+ /* Input. */
+ if ((qp->input = v_strdup(sp, input, ilen)) == NULL) {
+ sv_errno = errno;
+ goto mem3;
+ }
+ qp->ilen = ilen;
+
+ /* Output. */
+ if (output == NULL) {
+ qp->output = NULL;
+ olen = 0;
+ } else if ((qp->output = v_strdup(sp, output, olen)) == NULL) {
+ sv_errno = errno;
+ free(qp->input);
+mem3: if (qp->name != NULL)
+ free(qp->name);
+mem2: free(qp);
+mem1: errno = sv_errno;
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ qp->olen = olen;
+
+ /* Type, flags. */
+ qp->stype = stype;
+ qp->flags = flags;
+
+ /* Link into the chain. */
+ if (lastqp == NULL) {
+ LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
+ } else {
+ LIST_INSERT_AFTER(lastqp, qp, q);
+ }
+
+ /* Set the fast lookup bit. */
+ if (qp->input[0] < MAX_BIT_SEQ)
+ bit_set(sp->gp->seqb, qp->input[0]);
+
+ return (0);
+}
+
+/*
+ * seq_delete --
+ * Delete a sequence.
+ *
+ * PUBLIC: int seq_delete __P((SCR *, CHAR_T *, size_t, seq_t));
+ */
+int
+seq_delete(sp, input, ilen, stype)
+ SCR *sp;
+ CHAR_T *input;
+ size_t ilen;
+ seq_t stype;
+{
+ SEQ *qp;
+
+ if ((qp = seq_find(sp, NULL, NULL, input, ilen, stype, NULL)) == NULL)
+ return (1);
+ return (seq_mdel(qp));
+}
+
+/*
+ * seq_mdel --
+ * Delete a map entry, without lookup.
+ *
+ * PUBLIC: int seq_mdel __P((SEQ *));
+ */
+int
+seq_mdel(qp)
+ SEQ *qp;
+{
+ LIST_REMOVE(qp, q);
+ if (qp->name != NULL)
+ free(qp->name);
+ free(qp->input);
+ if (qp->output != NULL)
+ free(qp->output);
+ free(qp);
+ return (0);
+}
+
+/*
+ * seq_find --
+ * Search the sequence list for a match to a buffer, if ispartial
+ * isn't NULL, partial matches count.
+ *
+ * PUBLIC: SEQ *seq_find
+ * PUBLIC: __P((SCR *, SEQ **, EVENT *, CHAR_T *, size_t, seq_t, int *));
+ */
+SEQ *
+seq_find(sp, lastqp, e_input, c_input, ilen, stype, ispartialp)
+ SCR *sp;
+ SEQ **lastqp;
+ EVENT *e_input;
+ CHAR_T *c_input;
+ size_t ilen;
+ seq_t stype;
+ int *ispartialp;
+{
+ SEQ *lqp, *qp;
+ int diff;
+
+ /*
+ * Ispartialp is a location where we return if there was a
+ * partial match, i.e. if the string were extended it might
+ * match something.
+ *
+ * XXX
+ * Overload the meaning of ispartialp; only the terminal key
+ * search doesn't want the search limited to complete matches,
+ * i.e. ilen may be longer than the match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 0;
+ for (lqp = NULL, qp = sp->gp->seqq.lh_first;
+ qp != NULL; lqp = qp, qp = qp->q.le_next) {
+ /*
+ * Fast checks on the first character and type, and then
+ * a real comparison.
+ */
+ if (e_input == NULL) {
+ if (qp->input[0] > c_input[0])
+ break;
+ if (qp->input[0] < c_input[0] ||
+ qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ diff = memcmp(qp->input, c_input, MIN(qp->ilen, ilen));
+ } else {
+ if (qp->input[0] > e_input->e_c)
+ break;
+ if (qp->input[0] < e_input->e_c ||
+ qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ diff =
+ e_memcmp(qp->input, e_input, MIN(qp->ilen, ilen));
+ }
+ if (diff > 0)
+ break;
+ if (diff < 0)
+ continue;
+ /*
+ * If the entry is the same length as the string, return a
+ * match. If the entry is shorter than the string, return a
+ * match if called from the terminal key routine. Otherwise,
+ * keep searching for a complete match.
+ */
+ if (qp->ilen <= ilen) {
+ if (qp->ilen == ilen || ispartialp != NULL) {
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (qp);
+ }
+ continue;
+ }
+ /*
+ * If the entry longer than the string, return partial match
+ * if called from the terminal key routine. Otherwise, no
+ * match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 1;
+ break;
+ }
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (NULL);
+}
+
+/*
+ * seq_close --
+ * Discard all sequences.
+ *
+ * PUBLIC: void seq_close __P((GS *));
+ */
+void
+seq_close(gp)
+ GS *gp;
+{
+ SEQ *qp;
+
+ while ((qp = gp->seqq.lh_first) != NULL) {
+ if (qp->name != NULL)
+ free(qp->name);
+ if (qp->input != NULL)
+ free(qp->input);
+ if (qp->output != NULL)
+ free(qp->output);
+ LIST_REMOVE(qp, q);
+ free(qp);
+ }
+}
+
+/*
+ * seq_dump --
+ * Display the sequence entries of a specified type.
+ *
+ * PUBLIC: int seq_dump __P((SCR *, seq_t, int));
+ */
+int
+seq_dump(sp, stype, isname)
+ SCR *sp;
+ seq_t stype;
+ int isname;
+{
+ CHAR_T *p;
+ GS *gp;
+ SEQ *qp;
+ int cnt, len, olen;
+
+ cnt = 0;
+ gp = sp->gp;
+ for (qp = gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (stype != qp->stype || F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ ++cnt;
+ for (p = qp->input,
+ olen = qp->ilen, len = 0; olen > 0; --olen, ++p)
+ len += ex_puts(sp, KEY_NAME(sp, *p));
+ for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
+ len -= ex_puts(sp, " ");
+
+ if (qp->output != NULL)
+ for (p = qp->output,
+ olen = qp->olen, len = 0; olen > 0; --olen, ++p)
+ len += ex_puts(sp, KEY_NAME(sp, *p));
+ else
+ len = 0;
+
+ if (isname && qp->name != NULL) {
+ for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
+ len -= ex_puts(sp, " ");
+ for (p = qp->name,
+ olen = qp->nlen; olen > 0; --olen, ++p)
+ (void)ex_puts(sp, KEY_NAME(sp, *p));
+ }
+ (void)ex_puts(sp, "\n");
+ }
+ return (cnt);
+}
+
+/*
+ * seq_save --
+ * Save the sequence entries to a file.
+ *
+ * PUBLIC: int seq_save __P((SCR *, FILE *, char *, seq_t));
+ */
+int
+seq_save(sp, fp, prefix, stype)
+ SCR *sp;
+ FILE *fp;
+ char *prefix;
+ seq_t stype;
+{
+ CHAR_T *p;
+ SEQ *qp;
+ size_t olen;
+ int ch;
+
+ /* Write a sequence command for all keys the user defined. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (stype != qp->stype || !F_ISSET(qp, SEQ_USERDEF))
+ continue;
+ if (prefix)
+ (void)fprintf(fp, "%s", prefix);
+ for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == CH_LITERAL || ch == '|' ||
+ isblank(ch) || KEY_VAL(sp, ch) == K_NL)
+ (void)putc(CH_LITERAL, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc(' ', fp);
+ if (qp->output != NULL)
+ for (p = qp->output,
+ olen = qp->olen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == CH_LITERAL || ch == '|' ||
+ KEY_VAL(sp, ch) == K_NL)
+ (void)putc(CH_LITERAL, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ }
+ return (0);
+}
+
+/*
+ * e_memcmp --
+ * Compare a string of EVENT's to a string of CHAR_T's.
+ *
+ * PUBLIC: int e_memcmp __P((CHAR_T *, EVENT *, size_t));
+ */
+int
+e_memcmp(p1, ep, n)
+ CHAR_T *p1;
+ EVENT *ep;
+ size_t n;
+{
+ if (n != 0) {
+ do {
+ if (*p1++ != ep->e_c)
+ return (*--p1 - ep->e_c);
+ ++ep;
+ } while (--n != 0);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/common/seq.h b/contrib/nvi/common/seq.h
new file mode 100644
index 000000000000..984bb6c0bd18
--- /dev/null
+++ b/contrib/nvi/common/seq.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)seq.h 10.3 (Berkeley) 3/6/96
+ */
+
+/*
+ * Map and abbreviation structures.
+ *
+ * The map structure is doubly linked list, sorted by input string and by
+ * input length within the string. (The latter is necessary so that short
+ * matches will happen before long matches when the list is searched.)
+ * Additionally, there is a bitmap which has bits set if there are entries
+ * starting with the corresponding character. This keeps us from walking
+ * the list unless it's necessary.
+ *
+ * The name and the output fields of a SEQ can be empty, i.e. NULL.
+ * Only the input field is required.
+ *
+ * XXX
+ * The fast-lookup bits are never turned off -- users don't usually unmap
+ * things, though, so it's probably not a big deal.
+ */
+struct _seq {
+ LIST_ENTRY(_seq) q; /* Linked list of all sequences. */
+ seq_t stype; /* Sequence type. */
+ CHAR_T *name; /* Sequence name (if any). */
+ size_t nlen; /* Name length. */
+ CHAR_T *input; /* Sequence input keys. */
+ size_t ilen; /* Input keys length. */
+ CHAR_T *output; /* Sequence output keys. */
+ size_t olen; /* Output keys length. */
+
+#define SEQ_FUNCMAP 0x01 /* If unresolved function key.*/
+#define SEQ_NOOVERWRITE 0x02 /* Don't replace existing entry. */
+#define SEQ_SCREEN 0x04 /* If screen specific. */
+#define SEQ_USERDEF 0x08 /* If user defined. */
+ u_int8_t flags;
+};
diff --git a/contrib/nvi/common/util.c b/contrib/nvi/common/util.c
new file mode 100644
index 000000000000..5a4422a2c422
--- /dev/null
+++ b/contrib/nvi/common/util.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)util.c 10.11 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+/*
+ * binc --
+ * Increase the size of a buffer.
+ *
+ * PUBLIC: void *binc __P((SCR *, void *, size_t *, size_t));
+ */
+void *
+binc(sp, bp, bsizep, min)
+ SCR *sp; /* sp MAY BE NULL!!! */
+ void *bp;
+ size_t *bsizep, min;
+{
+ size_t csize;
+
+ /* If already larger than the minimum, just return. */
+ if (min && *bsizep >= min)
+ return (bp);
+
+ csize = *bsizep + MAX(min, 256);
+ REALLOC(sp, bp, void *, csize);
+
+ if (bp == NULL) {
+ /*
+ * Theoretically, realloc is supposed to leave any already
+ * held memory alone if it can't get more. Don't trust it.
+ */
+ *bsizep = 0;
+ return (NULL);
+ }
+ /*
+ * Memory is guaranteed to be zero-filled, various parts of
+ * nvi depend on this.
+ */
+ memset((char *)bp + *bsizep, 0, csize - *bsizep);
+ *bsizep = csize;
+ return (bp);
+}
+
+/*
+ * nonblank --
+ * Set the column number of the first non-blank character
+ * including or after the starting column. On error, set
+ * the column to 0, it's safest.
+ *
+ * PUBLIC: int nonblank __P((SCR *, recno_t, size_t *));
+ */
+int
+nonblank(sp, lno, cnop)
+ SCR *sp;
+ recno_t lno;
+ size_t *cnop;
+{
+ char *p;
+ size_t cnt, len, off;
+ int isempty;
+
+ /* Default. */
+ off = *cnop;
+ *cnop = 0;
+
+ /* Get the line, succeeding in an empty file. */
+ if (db_eget(sp, lno, &p, &len, &isempty))
+ return (!isempty);
+
+ /* Set the offset. */
+ if (len == 0 || off >= len)
+ return (0);
+
+ for (cnt = off, p = &p[off],
+ len -= off; len && isblank(*p); ++cnt, ++p, --len);
+
+ /* Set the return. */
+ *cnop = len ? cnt : cnt - 1;
+ return (0);
+}
+
+/*
+ * tail --
+ * Return tail of a path.
+ *
+ * PUBLIC: char *tail __P((char *));
+ */
+char *
+tail(path)
+ char *path;
+{
+ char *p;
+
+ if ((p = strrchr(path, '/')) == NULL)
+ return (path);
+ return (p + 1);
+}
+
+/*
+ * v_strdup --
+ * Strdup for wide character strings with an associated length.
+ *
+ * PUBLIC: CHAR_T *v_strdup __P((SCR *, const CHAR_T *, size_t));
+ */
+CHAR_T *
+v_strdup(sp, str, len)
+ SCR *sp;
+ const CHAR_T *str;
+ size_t len;
+{
+ CHAR_T *copy;
+
+ MALLOC(sp, copy, CHAR_T *, len + 1);
+ if (copy == NULL)
+ return (NULL);
+ memcpy(copy, str, len * sizeof(CHAR_T));
+ copy[len] = '\0';
+ return (copy);
+}
+
+/*
+ * nget_uslong --
+ * Get an unsigned long, checking for overflow.
+ *
+ * PUBLIC: enum nresult nget_uslong __P((u_long *, const char *, char **, int));
+ */
+enum nresult
+nget_uslong(valp, p, endp, base)
+ u_long *valp;
+ const char *p;
+ char **endp;
+ int base;
+{
+ errno = 0;
+ *valp = strtoul(p, endp, base);
+ if (errno == 0)
+ return (NUM_OK);
+ if (errno == ERANGE && *valp == ULONG_MAX)
+ return (NUM_OVER);
+ return (NUM_ERR);
+}
+
+/*
+ * nget_slong --
+ * Convert a signed long, checking for overflow and underflow.
+ *
+ * PUBLIC: enum nresult nget_slong __P((long *, const char *, char **, int));
+ */
+enum nresult
+nget_slong(valp, p, endp, base)
+ long *valp;
+ const char *p;
+ char **endp;
+ int base;
+{
+ errno = 0;
+ *valp = strtol(p, endp, base);
+ if (errno == 0)
+ return (NUM_OK);
+ if (errno == ERANGE) {
+ if (*valp == LONG_MAX)
+ return (NUM_OVER);
+ if (*valp == LONG_MIN)
+ return (NUM_UNDER);
+ }
+ return (NUM_ERR);
+}
+
+#ifdef DEBUG
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * TRACE --
+ * debugging trace routine.
+ *
+ * PUBLIC: void TRACE __P((SCR *, const char *, ...));
+ */
+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/contrib/nvi/common/util.h b/contrib/nvi/common/util.h
new file mode 100644
index 000000000000..46edb4aae5a8
--- /dev/null
+++ b/contrib/nvi/common/util.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)util.h 10.5 (Berkeley) 3/16/96
+ */
+
+/* Macros to init/set/clear/test flags. */
+#define FL_INIT(l, f) (l) = (f) /* Specific flags location. */
+#define FL_SET(l, f) ((l) |= (f))
+#define FL_CLR(l, f) ((l) &= ~(f))
+#define FL_ISSET(l, f) ((l) & (f))
+
+#define LF_INIT(f) FL_INIT(flags, f) /* Local variable flags. */
+#define LF_SET(f) FL_SET(flags, f)
+#define LF_CLR(f) FL_CLR(flags, f)
+#define LF_ISSET(f) FL_ISSET(flags, f)
+
+#define F_INIT(p, f) FL_INIT((p)->flags, f) /* Structure element flags. */
+#define F_SET(p, f) FL_SET((p)->flags, f)
+#define F_CLR(p, f) FL_CLR((p)->flags, f)
+#define F_ISSET(p, f) FL_ISSET((p)->flags, f)
+
+/* Offset to next column of stop size, e.g. tab offsets. */
+#define COL_OFF(c, stop) ((stop) - ((c) % (stop)))
+
+/* Busy message types. */
+typedef enum { B_NONE, B_OFF, B_READ, B_RECOVER, B_SEARCH, B_WRITE } bmsg_t;
+
+/*
+ * Number handling defines and protoypes.
+ *
+ * NNFITS: test for addition of two negative numbers under a limit
+ * NPFITS: test for addition of two positive numbers under a limit
+ * NADD_SLONG: test for addition of two signed longs
+ * NADD_USLONG: test for addition of two unsigned longs
+ */
+enum nresult { NUM_ERR, NUM_OK, NUM_OVER, NUM_UNDER };
+#define NNFITS(min, cur, add) \
+ (((long)(min)) - (cur) <= (add))
+#define NPFITS(max, cur, add) \
+ (((unsigned long)(max)) - (cur) >= (add))
+#define NADD_SLONG(sp, v1, v2) \
+ ((v1) < 0 ? \
+ ((v2) < 0 && \
+ NNFITS(LONG_MIN, (v1), (v2))) ? NUM_UNDER : NUM_OK : \
+ (v1) > 0 ? \
+ (v2) > 0 && \
+ NPFITS(LONG_MAX, (v1), (v2)) ? NUM_OK : NUM_OVER : \
+ NUM_OK)
+#define NADD_USLONG(sp, v1, v2) \
+ (NPFITS(ULONG_MAX, (v1), (v2)) ? NUM_OK : NUM_OVER)
diff --git a/contrib/nvi/docs/TODO b/contrib/nvi/docs/TODO
new file mode 100644
index 000000000000..6fe88295d5ff
--- /dev/null
+++ b/contrib/nvi/docs/TODO
@@ -0,0 +1,147 @@
+CL: In single-line screens, have to press 'q' twice when quitting out
+ of a ":set all" display.
+
+COMMON: There's a serious problem with error returns -- we need to separate
+ command failure from fatal error, consistently, over the entire source
+ tree.
+
+ We need to rework all of vi to have three return values:
+
+ 0: success
+ 1: vi error, continue
+ 2: fatal error, die
+
+ Right now we don't recognize fatal errors for what they are.
+
+VI: Change the screen scrolling to not eat user characters... i.e.
+ g/pattern/foo should not eat already entered chars.
+
+COMMON: It's known that it's possible to sync the backing files in the
+ wrong manner, leaving backup files that aren't recoverable. This
+ is going to be left alone until we have a logging version of DB,
+ which will hopefully fix this (or at least make it possible to
+ easily do so).
+
+COMMON: The complete list of POSIX.1 calls that can return EINTR are:
+
+ wait, waitpid, sleep, dup2, close, read, write,
+ fcntl(SETLCKW) tcsetattr, tcdrain
+
+ The problem is that technically, any system/library call can
+ return EINTR, so, while nvi blocks (most of?) the obvious ones,
+ someone may have to do a complete pass and block signals
+ everywhere.
+
+COMMON: The vi main command loop should use the general-purpose overflow
+ and underflow routines. In addition, the vi command loop uses
+ unsigned longs -- should probably be fixed as a 32-bit unsigned
+ type, and then check to make sure it's never used as as variable
+ type again.
+
+DB: When nvi edits files that don't have trailing newlines, it appends
+ one, regardless. This is required, by default, from POSIX.2.
+
+COMMON: Open mode is not yet implemented.
+
+COMMON: ^C isn't passed to the shell in the script windows as an interrupt
+ character.
+
+COMMON: The options:
+
+ hardtabs, lisp, optimize, redraw, slowopen
+
+ are recognized, but not implemented. These options are unlikely
+ to be implemented, so if you want them you might want to say
+ something! I will implement lisp if anyone ever documents how it
+ worked.
+
+COMMON: If you run out of space in the recovery directory, the recovery
+ file is left in place.
+
+COMMON: Should "view" set a lock on the file?
+
+COMMON: Field editing shouldn't be hard to add to nvi:
+
+ Field editing file template:
+
+ version #
+ field # row/column start row/column stop
+ label field # Label string
+ re field # Matching re string.
+ field # row/column start row/column stop
+ label field # Label string
+ re field # Matching re string.
+
+ <tab> moves to the next field
+ <bs> in column 0 moves to the previous field
+
+COMMON: Let's rethink using an IPC mechanism:
+
+ Two way channel, with events passing in both directions.
+
+ Load into the same address space (else, how do file permissions) forks
+ in v_init -- screens get events from vi, vi gets events queued up from
+ screens.
+
+ Vi:
+ E_CHARACTER, /* Input character: e_c set. */
+ E_EOF, /* End of input (NOT ^D). */
+ E_ERR, /* Input error. */
+ E_INTERRUPT, /* Interrupt. */
+ E_REPAINT, /* Repaint: e_flno, e_tlno set. */
+ E_RESIZE, /* SIGWINCH: e_lno, e_cno set. */
+ E_SIGCONT, /* SIGCONT arrived. */
+ E_SIGFATAL, /* fatal signal arrived.
+ E_START, /* Start ex/vi. */
+ E_STOP, /* Stop ex/vi. */
+ E_STRING, /* Input string: e_csp, e_len set. */
+
+ Screen:
+ E_ADDSTR /* Add a string to the screen. */
+ E_ATTRIBUTE /* Screen attribute. */
+ E_BELL /* Beep/bell/flash the terminal. */
+ E_BUSY /* Display a busy message. */
+ E_CANONICAL /* Enter tty canonical mode. */
+ E_CLRTOEOL /* Clear to the end of the line. */
+ E_CURSOR /* Return the cursor location. */
+ E_DELETELN /* Delete a line. */
+ E_DISCARD /* Discard a screen. */
+ E_EXADJUST /* Ex: screen adjustment routine. */
+ E_FMAP /* Set a function key. */
+ E_GETKEY /* Get a key event. */
+ E_INSERTLN /* Insert a line. */
+ E_MOVE /* Move the cursor. */
+ E_MESSAGE /* Message or ex output. */
+ E_REFRESH /* Refresh the screen. */
+ E_RESIZE /* Resize two screens. */
+ E_SPLIT /* Split the screen. */
+ E_SUSPEND /* Suspend the editor. */
+
+EX: It would be nice to inverse video the replaced text during
+ interactive substitute.
+
+EX: The :args command should put the current file name out in reverse
+ video. This isn't going to be easy, currently only full lines can
+ be in reverse video, not just parts.
+
+TK: We currently permit the user to change the lines, columns and term
+ edit options. Shouldn't that be illegal in tknvi?
+
+VI: The strings found by searches should be highlighted until the next
+ character is entered.
+
+VI: Display a split vi screen for the :help command.
+
+VI: When getting a key for a continue screen, we should always read from
+ the terminal, not from a mapped key.
+
+VI: The sentence, paragraph and section movement commands don't match
+ historic practice in some boundary cases. This should be left
+ alone until POSIX 1003.2 makes up its mind.
+
+VI: The vs_sm_fill routine should scroll if possible, not always redraw.
+
+VI: Think about setting a dirty/inuse bits on the lines of the SMAP
+ structure. That way the message routines could steal lines and
+ refresh would continue to work, because it would know not to touch
+ the lines that were in use.
diff --git a/contrib/nvi/docs/USD.doc/edit/Makefile b/contrib/nvi/docs/USD.doc/edit/Makefile
new file mode 100644
index 000000000000..0c59f6d0816e
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/edit/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.4 (Berkeley) 8/18/96
+
+ROFF= groff
+TBL= tbl
+
+edittut.ps: edittut.ms
+ ${TBL} edittut.ms | ${ROFF} -ms > $@
+ chmod 444 $@
+
+clean:
+ rm -f edittut.ps
diff --git a/contrib/nvi/docs/USD.doc/edit/edit.vindex b/contrib/nvi/docs/USD.doc/edit/edit.vindex
new file mode 100644
index 000000000000..2098f14ea190
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/edit/edit.vindex
@@ -0,0 +1,115 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edit.vindex 8.1 (Berkeley) 6/8/93
+.\"
+.bd I
+.ND
+.TL
+Index
+.sp 3
+.2C
+.nf
+addressing, \fIsee\fR line numbers
+append mode, 4
+backslash (\\), 18
+buffer, 2
+command mode, 4
+context search, 8, 10, 13, 18
+control characters (``^'' notation), 8
+control-d, 6
+current filename, 19, 20
+current line (.), 9, 15
+diagnostic messages, 4
+disk, 2
+documentation, 21
+edit (to begin editing session), 3, 7
+editing commands:
+.in +2
+append (a), 4, 7
+change (c), 16
+copy (co), 13
+delete (d), 13-14
+edit (e), 12
+file (f), 19
+global (g), 18-19
+move (m), 12-13
+number (nu), 9
+preserve (pre), 20-21
+print (p), 8
+quit (q), 5, 11
+quit! (q!), 11
+read (r), 20
+recover (rec), 20
+substitute (s), 9-10, 17, 18
+undo (u), 14, 17
+write (w), 5-6, 11, 19-20
+z, 11
+.sp 10i
+! (shell escape), 19
+$= , 15
++, 15
+\-, 15
+//, 8, 18
+??, 18
+\&\fB.\fR, 9, 15
+\&\fB.\fR=, 9, 15
+.in -2
+erasing
+.ti +2
+characters (#), 8
+.ti +2
+lines (@), 8
+ex (text editor), 21
+\fIEx Reference Manual\fR, 21
+file, 1
+file recovery, 20
+filename, 2
+Interrupt (message), 7
+line numbers, \fIsee also\fR current line
+.ti +2
+dollar sign ($), 8, 12-13, 15
+.ti +2
+dot (.), 9, 15
+.ti +2
+relative (+ and \-), 15, 16
+logging out, 6
+login procedure, 2
+``magic'' characters, 21
+non-printing characters, 8
+``not found'' (message), 3
+program, 1
+recovery \fIsee\fR file recovery
+shell, 18
+shell escape (!), 19
+special characters (^, $, \e), 18
+text input mode, 4
+UNIX, 1
diff --git a/contrib/nvi/docs/USD.doc/edit/edittut.ms b/contrib/nvi/docs/USD.doc/edit/edittut.ms
new file mode 100644
index 000000000000..8a9d66ede2e6
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/edit/edittut.ms
@@ -0,0 +1,2280 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edittut.ms 8.3 (Berkeley) 8/18/96
+.\"
+.ll 6.5i
+.nr LL 6.5i
+.EH 'USD:11-%''Edit: A Tutorial'
+.OH 'Edit: A Tutorial''USD:11-%'
+.LP
+.ds u \s-2UNIX\s0
+.ND
+.sp 4
+.ce
+\f3\s+2Edit: A Tutorial\s0\f1
+.sp
+.ce 3
+.I
+Ricki Blau
+.sp
+James Joyce
+.R
+.sp
+.ce 3
+Computing Services
+University of California
+Berkeley, California 94720
+.sp 3
+.ce
+.I
+ABSTRACT
+.R
+.sp
+.LP
+This narrative introduction to the use of the text editor
+.I edit
+assumes no prior familiarity with computers or with text editing.
+Its aim is to lead the beginning \s-2UNIX\(dg\s+2 user through the
+.FS
+\(dgUNIX is a trademark of Bell Laboratories.
+.FE
+fundamental steps of writing and revising a file of text.
+Edit,
+a version of the text editor
+.I ex,
+was designed to provide an informative environment
+for new and casual users.
+.PP
+We welcome comments and suggestions about this tutorial
+and the \s-2UNIX\s+2 documentation in general.
+.sp .5v
+September 1981
+.bp
+.ll 6.5i
+.nr LL 6.5i
+.nr LT 6.5i
+.ds u \s-2UNIX\s0
+.ce
+\s+2\f3Contents\f1\s0
+.LP
+.nf
+Introduction\ \ \ 3
+.sp
+Session 1\ \ 4
+.in +.5i
+Making contact with \s-2UNIX\s+2\ \ \ 4
+Logging in\ \ 4
+Asking for \fIedit\fR\ \ \ 4
+The ``Command not found'' message\ \ \ 5
+A summary\ \ 5
+Entering text\ \ \ 5
+Messages from \fIedit\fR\ \ \ 5
+Text input mode\ \ \ 6
+Making corrections\ \ \ 6
+Writing text to disk\ \ \ 7
+Signing off\ \ 7
+.in -.5i
+.sp
+Session 2\ \ \ 8
+.in +.5i
+Adding more text to the file\ \ \ 8
+Interrupt\ \ \ 8
+Making corrections\ \ \ 8
+Listing what's in the buffer (p)\ \ \ 9
+Finding things in the buffer\ \ \ 9
+The current line\ \ \ 10
+Numbering lines (nu)\ \ \ 10
+Substitute command (s)\ \ \ 10
+Another way to list what's in the buffer (z)\ \ \ 11
+Saving the modified text\ \ \ 12
+.in -.5i
+.sp
+Session 3\ \ \ 13
+.in +.5i
+Bringing text into the buffer (e)\ \ \ 13
+Moving text in the buffer (m)\ \ \ 13
+Copying lines (copy)\ \ \ 14
+Deleting lines (d)\ \ \ 14
+A word or two of caution\ \ \ 15
+Undo (u) to the rescue\ \ \ 15
+More about the dot (.) and buffer end ($)\ \ \ 16
+Moving around in the buffer (+ and \-)\ \ \ 16
+Changing lines (c)\ \ \ 17
+.in -.5i
+.sp
+Session 4\ \ \ 18
+.in +.5i
+Making commands global (g)\ \ \ 18
+More about searching and substituting\ \ \ 19
+Special characters\ \ \ 19
+Issuing \s-2UNIX\s+2 commands from the editor\ \ \ 20
+Filenames and file manipulation\ \ \ 20
+The file (f) command\ \ \ 20
+Reading additional files (r)\ \ \ 21
+Writing parts of the buffer\ \ \ 21
+Recovering files\ \ \ 21
+Other recovery techniques\ \ \ 21
+Further reading and other information\ \ \ 22
+Using \fIex\fR\ \ \ 22
+.in -.5i
+.sp
+Index\ \ \ 23
+.bp
+.SH
+.ce
+\s+2Introduction\s0
+.PP
+Text editing using a terminal connected to a computer
+allows you to create, modify, and print text
+easily.
+A
+.I
+text editor
+.R
+is a program
+that assists you
+as you create and modify text.
+The text editor you will learn here is named
+.I edit.
+Creating text using edit is as easy as typing it
+on an electric typewriter.
+Modifying text involves telling the text editor
+what you want to add, change, or delete.
+You can review your text
+by typing a command
+to print the file contents
+as they are currently.
+Another program (which we do not discuss in this
+document), a text formatter,
+rearranges your text
+for you into ``finished form.''
+.PP
+These lessons assume no prior familiarity with computers
+or with text editing.
+They consist of a series of text editing sessions
+which lead you through the fundamental steps
+of creating and revising text.
+After scanning each lesson and before beginning the next,
+you should try the examples at a terminal to get a feeling
+for the actual process of text editing.
+If you set aside some time for experimentation,
+you will soon become familiar with using the
+computer to write and modify text.
+In addition to the actual use of the text editor,
+other features of \s-2UNIX\s0 will be very important to your work.
+You can begin to
+learn about these other features by
+reading one of the other tutorials
+that provide a general introduction to the system.
+You will be ready to proceed with this lesson as soon as
+you are familiar with (1) your terminal and its special keys,
+(2) how to login,
+(3) and the ways of correcting typing errors.
+Let's first define some terms:
+.sp .5
+.IP program 12
+A set of instructions, given to the computer,
+describing the sequence of steps the computer performs
+in order to accomplish a specific task.
+The task must be specific,
+such as balancing your checkbook
+or editing your text.
+A general task,
+such as working for world peace,
+is something we can all do,
+but not something we can currently write programs to do.
+.IP UNIX
+\s-2UNIX\s0 is a special type of program,
+called an operating system, that supervises the machinery
+and all other programs comprising the total
+computer system.
+.IP edit
+.I edit
+is the name of the \s-2UNIX\s0 text editor you will be learning to use,
+and is a program that aids you in writing or revising text.
+Edit was designed for beginning users,
+and is a simplified version of an editor named
+.I ex.
+.IP file
+Each \s-2UNIX\s0 account is allotted
+space for the permanent storage of information,
+such as programs, data or text.
+A file is a logical unit of data,
+for example, an essay, a program,
+or a chapter from a book,
+which is stored on a computer system.
+Once you create a file,
+it is kept until you instruct the system to remove it.
+You may create a file during one \s-2UNIX\s0 session,
+end the session,
+and return to use it at a later time.
+Files contain anything you choose to write and store in them.
+The sizes of files vary to suit your needs;
+one file might hold only a single number,
+yet another might contain
+a very long document or program.
+The only way to save
+information from one session to the next is to store it in a file,
+which you will learn in Session 1.
+.IP filename
+Filenames are used to distinguish one file from another,
+serving the same purpose as the labels of manila
+folders in a file cabinet.
+In order to write or access information in a file,
+you use the name of that file in a \s-2UNIX\s0 command,
+and the system will automatically locate the file.
+.IP disk
+Files are stored on an input/output device called a disk,
+which looks something like a stack of phonograph records.
+Each surface is coated with a material similar to that
+on magnetic recording tape,
+and information is recorded on it.
+.IP buffer
+A temporary work space, made available to the user
+for the duration of a session of text editing
+and used for creating and modifying
+the text file.
+We can think of the buffer as a blackboard that is
+erased after each class, where each session with the editor
+is a class.
+.bp
+.SH
+.ce 1
+\s+2Session 1\s0
+.sp 1
+.SH
+Making contact with \s-1UNIX\s0
+.PP
+To use the editor you must first make contact with the computer
+by logging in to \s-2UNIX\s0.
+We'll quickly review the standard \s-2UNIX\s0 login procedure
+for the two ways you can make contact:
+on a terminal that is directly linked to the computer,
+or over a telephone line where the computer answers your call.
+.SH
+Directly-linked terminals
+.PP
+Turn on your terminal and press the \s-1RETURN\s0 key.
+You are now ready to login.
+.SH
+Dial-up terminals
+.PP
+If your terminal connects with the computer over a telephone line,
+turn on the terminal, dial the system access number,
+and, when you hear a high-pitched tone, place the
+telephone handset in the acoustic coupler, if you are using one.
+You are now ready to login.
+.SH
+Logging in
+.PP
+The message inviting you to login is:
+.DS I 1i
+login:
+.DE
+.LP
+Type your login name, which identifies you to \s-2UNIX\s0,
+on the same line as the login message,
+and press \s-2RETURN\s+2.
+If the terminal you are using
+has both upper and lower case,
+.B
+be sure you enter your login name in lower case;
+.R
+otherwise \s-2UNIX\s0 assumes your terminal
+has only upper case and will not recognize lower case
+letters you may type.
+\s-2UNIX\s0 types ``login:'' and you reply
+with your login name, for example ``susan'':
+.DS I 1i
+login: \fBsusan\fR \fI(and press the \s-2RETURN\s0 key)\fR
+.DE
+(In the examples, input you would type appears in
+.B "bold face"
+to distinguish it from the responses from \s-2UNIX\s0.)
+.PP
+\s-2UNIX\s0 will next respond with a request for a password
+as an additional precaution to prevent
+unauthorized people from using your account.
+The password will not appear when you type it,
+to prevent others from seeing it.
+The message is:
+.DS I 1i
+Password: \fI(type your password and press \s-2RETURN\s+2)\fR
+.DE
+If any of the information you gave during the login
+sequence was mistyped or incorrect,
+\s-2UNIX\s0 will respond with
+.DS I 1i
+Login incorrect.
+.if t .sp .2v
+.if n .sp 1
+login:
+.DE
+in which case you should start the login process anew.
+Assuming that you have successfully
+logged in, \s-2UNIX\s0
+will print the message of the day and eventually will present
+you with a % at the beginning of a fresh line.
+The % is the \s-2UNIX\s0 prompt symbol
+which tells you that \s-2UNIX\s0 is ready to accept a command.
+.bd I 3
+.SH
+Asking for \fIedit\fP
+.fl
+.bd I
+.PP
+You are ready to tell \s-2UNIX\s0 that you
+want to work with edit, the text editor.
+Now is a convenient time to choose
+a name for the file of text you are about to create.
+To begin your editing session,
+type
+.B edit
+followed by a space and then the filename
+you have selected; for example, ``text''.
+After that,
+press the \s-2RETURN\s0 key and wait for edit's response:
+.DS I 1i
+% \fBedit text\fP \fI(followed by a \s-2RETURN\s+2)\fR
+"text" No such file or directory
+:
+.DE
+If you typed the command correctly,
+you will now be in communication with edit.
+Edit has set aside a buffer for use as
+a temporary working space during your current editing session.
+Since ``text'' is a new file we are about to create
+the editor was unable to find that file, which it
+confirms by saying:
+.DS I 1i
+"text" No such file or directory
+.DE
+On the next line appears edit's prompt ``:'',
+announcing that you are in \f2command mode\f1 and
+edit expects a command from you.
+You may now begin to create the new file.
+.SH
+The ``Command not found'' message
+.PP
+If you misspelled edit by typing, say, ``editor'',
+this might appear:
+.DS I 1i
+% \fBeditor\fP
+editor: Command not found
+%
+.DE
+Your mistake in calling edit ``editor'' was
+treated by \s-2UNIX\s0 as a request
+for a program named ``editor''.
+Since there is no program
+named ``editor'',
+\s-2UNIX\s0 reported that the program was ``not found''.
+A new % indicates that \s-2UNIX\s0 is ready for another command,
+and you may then enter the correct command.
+.SH
+A summary
+.PP
+Your exchange with \s-2UNIX\s0 as you logged in and made contact with edit
+should look something like this:
+.DS I 1i
+login: \fBsusan\fP
+Password:
+\&... A Message of General Interest ...
+% \fBedit text\fP
+"text" No such file or directory
+:
+.DE
+.SH
+Entering text
+.PP
+You may now begin entering text into the buffer.
+This is done by \fIappending\fP (or adding) text to whatever
+is currently in the buffer.
+Since there is nothing in the buffer at the moment,
+you are appending text to nothing;
+in effect,
+since you are adding text to nothing
+you are creating text.
+Most edit commands have two equivalent forms:
+a word that suggests what the command does,
+and a shorter abbreviation of that word.
+Many beginners find the full command names
+easier to remember at first,
+but once you are familiar with editing you may
+prefer to type the shorter abbreviations.
+The command to input text is ``append''.
+(It may be abbreviated ``a''.)
+Type
+.B append
+and press the \s-2RETURN\s0 key.
+.DS I 1i
+% \fBedit text
+\fR:\|\fBappend
+.R
+.DE
+.SH
+.bd I 3
+Messages from
+.I edit
+.fl
+.bd I
+.PP
+If you make a mistake in entering a command and
+type something that edit does not recognize,
+edit will respond with a message
+intended to help you diagnose your error.
+For example, if you misspell the command to input text by typing,
+perhaps, ``add'' instead of ``append'' or ``a'',
+you will receive this message:
+.DS I 1i
+:\|\fBadd\fR
+add: Not an editor command
+:
+.DE
+When you receive a diagnostic message,
+check what you typed in order to determine what
+part of your command confused edit.
+The message above means that edit
+was unable to recognize your mistyped command
+and, therefore, did not execute it.
+Instead, a new ``:''
+appeared to let you know that
+edit is again ready to execute a command.
+.SH
+Text input mode
+.PP
+By giving the command ``append'' (or using the abbreviation ``a''),
+you entered
+.I
+text input mode,
+.R
+also known as
+.I
+append mode.
+.R
+When you enter text input mode,
+edit stops sending you a prompt.
+You will not receive any prompts
+or error messages
+while in text input mode.
+You can enter
+pretty much anything you want on the lines.
+The lines are transmitted one by one to the buffer
+and held there during the editing session.
+You may append as much text as you want, and
+.I
+when you wish to stop entering text lines you should
+type a period as the only character on the line
+and press the \s-2RETURN\s0 key.
+.R
+When you type the period and press \s-2RETURN\s0,
+you signal that you want to stop appending text,
+and edit responds by allowing
+you to exit text input mode and reenter command mode.
+Edit will again
+prompt you for a command by printing ``:''.
+.PP
+Leaving append mode does not destroy the text in
+the buffer.
+You have to leave append
+mode to do any of the other kinds of editing,
+such as changing, adding, or printing text.
+If you type a period as the first character and
+type any other character on the same line,
+edit will believe you want to remain in append mode
+and will not let you out.
+As this can be very frustrating,
+be sure to type
+.B only
+the period and the \s-2RETURN\s0 key.
+.PP
+This is a good place to learn an important
+lesson about computers and text: a blank space is
+a character as far as a computer is concerned.
+If you so much as type a period followed by a blank
+(that is, type a period and then the space bar on the keyboard),
+you will remain in append mode with the last line of text
+being:
+.DS I 1i
+.B
+.ps +2
+\&.
+.ps -2
+.R
+.DE
+Let's say that you enter the lines
+(try to type
+.B exactly
+what you see, including ``thiss''):
+.DS I 1i
+.B
+This is some sample text.
+And thiss is some more text.
+Text editing is strange, but nice.
+\&.
+.R
+.DE
+The last line is the period followed by a \s-2RETURN\s0
+that gets you out of append mode.
+.SH
+Making corrections
+.PP
+If you have read a general introduction to \s-2UNIX\s0,
+you will recall that it is possible to erase individual
+letters that you have typed.
+This is done by typing the designated erase character
+as many times as there are characters
+you want to erase.
+.PP
+The usual erase character varies from place to place and
+user to user. Often it
+is the backspace (control-H),
+so you can correct typing errors
+in the line you are typing
+by holding down the \s-1CTRL\s+1 key
+and typing the ``H'' key. (Sometimes it is the DEL key.)
+If you type the erase character
+you will notice
+that the terminal backspaces in the line you are on.
+You can backspace over your error,
+and then type what you want to be the rest of the line.
+.PP
+If you make a bad start
+in a line
+and would like to begin again,
+you can either backspace to the beginning of the line
+or you can use the at-sign ``@'' to erase everything on the line:
+.DS I 1i
+.B
+Text edtiing is strange, but@
+Text editing is strange, but nice.
+.R
+.fl
+.bd S
+.DE
+When you type the at-sign (@), you erase
+the entire line typed so far
+and are given a fresh line to type on.
+You may immediately begin to retype the line.
+This, unfortunately, does not work after you type the
+line and press \s-2RETURN\s+2.
+To make corrections in lines that have been completed,
+it is necessary to use the editing commands
+covered in the next sessions.
+.SH
+Writing text to disk
+.PP
+You are now ready to edit the text. One common operation
+is to write the text to disk as a file for safekeeping
+after the session is over.
+This is the only way to save information from one session to the next,
+since the editor's buffer is temporary and will last only until the
+end of the editing session.
+Learning how to write a file to disk is second in
+importance only to entering the text.
+To write the contents of the buffer to a disk
+file, use the command ``write''
+(or its abbreviation ``w''):
+.DS I 1i
+:\|\fBwrite
+.R
+.DE
+Edit will copy the contents of the buffer to a disk file.
+If the file does not yet exist,
+a new file will be created automatically
+and the presence of a ``[New file]'' will be noted.
+The newly-created file will be given the name specified when
+you entered the editor, in this case ``text''.
+To confirm that the disk file has been successfully written,
+edit will repeat the filename and give
+the number of lines and the total
+number of characters in the file.
+The buffer remains unchanged by the ``write'' command.
+All of the lines that were written to disk will still be
+in the buffer,
+should you want to modify or add to them.
+.PP
+Edit must have a name for the file to be written.
+If you forgot to indicate the name of the file
+when you began to edit,
+edit will print in response to your write command:
+.DS I 1i
+No current filename
+.DE
+If this happens, you can specify the filename in a new write command:
+.DS I 1i
+:\|\fBwrite text
+.R
+.DE
+After the ``write'' (or ``w''), type a space and then the name of the file.
+.SH
+Signing off
+.PP
+We have done enough for this first lesson on using the
+\s-2UNIX\s0 text editor, and are ready to quit the session with edit.
+To do this we type ``quit'' (or ``q'') and press \s-2RETURN\s+2:
+.DS I 1i
+:\|\fBwrite
+.R
+"text" [New file] 3 lines, 90 characters
+:\|\fBquit\fR
+%
+.DE
+The % is from \s-2UNIX\s0 to tell you that your session with edit is
+over and you may command \s-2UNIX\s0 further.
+Since we want
+to end the entire session at the terminal, we also need to
+exit from \s-2UNIX\s0.
+In response to the \s-2UNIX\s0 prompt of ``\|%\|''
+type the command
+.DS I 1i
+%\|\fBlogout\fR
+.DE
+This will end your session with \s-2UNIX\s0, and will ready the
+terminal for the next user.
+It is always important to type \fBlogout\fR at the end of a session
+to make absolutely sure no one
+could accidentally stumble into your abandoned
+session and thus gain access to your files,
+tempting even the most honest of souls.
+.sp 1
+.PP
+This is the end of the first session on \s-2UNIX\s0 text editing.
+.bp
+.TL
+Session 2
+.sp
+.PP
+Login with \s-2UNIX\s0 as in the first session:
+.DS I 1i
+login: \fBsusan\fP \fI(carriage return)\fR
+Password: \fI(give password and carriage return)\fR
+.if t .sp .2v
+.if n .sp 1
+\&... A Message of General Interest ...
+%
+.DE
+When you indicate you want to edit,
+you can specify the name of the file you worked on last time.
+This will
+start edit working, and it will fetch the contents of the
+file into the buffer, so that you can resume editing the same file.
+When edit has copied the file into the buffer, it
+will repeat its name and tell
+you the number of lines and characters it contains.
+Thus,
+.DS I 1i
+.B
+% edit text
+.R
+"text" 3 lines, 90 characters
+:
+.DE
+means you asked edit to fetch
+the file named ``text'' for editing,
+causing it to copy the
+90 characters of text into the buffer.
+Edit awaits
+your further instructions,
+and indicates this by its prompt character, the colon (:).
+In this session, we will append more text to our file,
+print the contents of the buffer, and learn to change the text of a line.
+.SH
+Adding more text to the file
+.PP
+If you want to add more to the end of your
+text you may do so by using the append command to enter text input mode.
+When ``append'' is the first command
+of your editing session,
+the lines you enter
+are placed at the end of the buffer.
+Here we'll use the abbreviation for the append command, ``a'':
+.DS I 1i
+:\|\fBa
+This is text added in Session 2.
+It doesn't mean much here, but
+it does illustrate the editor.
+\|\fB\s+2\&.\s-2
+.R
+.DE
+You may recall that once you enter append mode
+using the ``a'' (or ``append'') command,
+you need to type a line containing only a period (.)
+to exit append mode.
+.SH
+Interrupt
+.PP
+Should you press the \s-2RUB\s+2 key (sometimes labelled \s-2DELETE\s+2)
+while working with edit,
+it will send this message to you:
+.DS I 1i
+Interrupt
+:
+.DE
+Any command that edit might be executing
+is terminated by rub or delete,
+causing edit to prompt you for a new command.
+If you are appending text at the time,
+you will exit from append mode
+and be expected to give another command.
+The line of text you were typing
+when the append command was interrupted
+will not be entered into the buffer.
+.SH
+Making corrections
+.PP
+If while typing the line you hit an incorrect key,
+recall that
+you may delete the incorrect character
+or cancel the entire line of input by erasing in the usual way.
+Refer either
+to the last few pages of Session 1
+if you need to review
+the procedures for making a correction.
+The most important idea to remember is that
+erasing a character or cancelling a line must be done
+before you press the \s-2RETURN\s+2 key.
+.SH
+Listing what's in the buffer (p)
+.PP
+Having appended text to what you wrote in Session 1,
+you might want to see all the lines in the buffer.
+To print the contents of the buffer, type the command:
+.DS I 1i
+:\|\fB1,$p
+.R
+.DE
+The ``1''\(dg
+.FS
+\(dgThe numeral ``one'' is the top left-most key,
+and should not be confused with the letter ``el''.
+.FE
+stands for line 1 of the buffer,
+the ``$'' is a special symbol designating the last line
+of the buffer,
+and ``p'' (or \fBprint\fR) is the command to print from line 1
+to the end of the buffer.
+The command ``1,$p'' gives you:
+.DS I 1i
+This is some sample text.
+And thiss is some more text.
+Text editing is strange, but nice.
+This is text added in Session 2.
+It doesn't mean much here, but
+it does illustrate the editor.
+.DE
+Occasionally, you may accidentally
+type a character that can't be printed,
+which can be done by striking a key
+while the \s-2CTRL\s0 key is pressed.
+In printing lines, edit uses a special notation to
+show the existence of non-printing characters.
+Suppose you had introduced the non-printing character ``control-A''
+into the word ``illustrate''
+by accidently pressing the \s-2CTRL\s0 key while
+typing ``a''.
+This can happen on many terminals
+because the \s-2CTRL\s+2 key and the ``A'' key
+are beside each other.
+If your finger presses between the two keys,
+control-A results.
+When asked to print the contents of the buffer,
+edit would display
+.DS I 1i
+it does illustr^Ate the editor.
+.DE
+To represent the control-A, edit shows ``^A''.
+The sequence ``^'' followed by a capital
+letter stands for the one character
+entered by holding down the \s-2CTRL\s0 key and typing the letter
+which appears after the ``^''.
+We'll soon discuss the commands that can be used
+to correct this typing error.
+.PP
+In looking over the text we see that
+``this'' is typed as ``thiss'' in the second line,
+a deliberate error so we can learn to make corrections.
+Let's correct the spelling.
+.SH
+Finding things in the buffer
+.PP
+In order to change something in the buffer we first need to
+find it.
+We can find ``thiss'' in the text we have
+entered by looking at a listing
+of the lines.
+Physically speaking, we search the lines
+of text looking for ``thiss'' and stop searching when
+we have found it.
+The way to tell edit to search for something
+is to type it inside slash marks:
+.DS I 1i
+:\|\fB/thiss/
+.R
+.DE
+By typing
+.B /thiss/
+and pressing \s-1RETURN\s0,
+you instruct edit to search for ``thiss''.
+If you ask edit to look for a pattern of characters
+which it cannot find in the buffer,
+it will respond ``Pattern not found''.
+When edit finds
+the characters ``thiss'', it will print the line of text
+for your inspection:
+.DS I 1i
+And thiss is some more text.
+.DE
+Edit is now positioned in the buffer at the
+line it just printed,
+ready to make a change in the line.
+.bp
+.SH
+The current line
+.PP
+Edit keeps track of the line in the buffer where it is located
+at all times during an editing session.
+In general, the line that has been most recently
+printed, entered, or changed
+is the current location in the buffer.
+The editor is prepared to make changes
+at the current location in the buffer,
+unless you direct it to another location.
+.PP
+In particular,
+when you bring a file into the buffer,
+you will be located at the last line in the file,
+where the editor left off copying the lines
+from the file to the buffer.
+If your first editing command is ``append'',
+the lines you enter are added
+to the end of the file,
+after the current line \(em
+the last line in the file.
+.PP
+You can refer to your current location in the buffer by the
+symbol
+period (.) usually known by the name ``dot''.
+If you type ``.'' and carriage
+return you will be instructing edit to print the current line:
+.DS I 1i
+:\|\fB\s+2\&.\s-2
+.R
+And thiss is some more text.
+.DE
+.PP
+If you want to know the number of the current line,
+you can type
+.B \&.=
+and press \s-2RETURN\s+2,
+and edit will respond with the line number:
+.DS I 1i
+:\|\fB\s+2.\s-2=
+.R
+2
+.DE
+If you type the number of any line and press \s-2RETURN\s+2,
+edit will position you at that line and
+print its contents:
+.DS I 1i
+:\|\fB2
+.R
+And thiss is some more text.
+.DE
+You should experiment with these commands
+to gain experience in using them to make changes.
+.SH
+Numbering lines (nu)
+.PP
+The
+.B
+number (nu)
+.R
+command is similar to print,
+giving both the number and the text of each printed line.
+To see the number and the text of the current line type
+.DS I 1i
+:\|\fBnu
+.R
+\0\0\0\0\02\0\0And thiss is some more text.
+.DE
+Note that the shortest abbreviation for the number command is
+``nu'' (and not ``n'', which is used for a different command).
+You may specify a range of lines
+to be listed by the number command in the same way that lines
+are specified for print.
+For example, \f31,$nu\f1 lists all lines in the buffer with their
+corresponding line numbers.
+.SH
+Substitute command (s)
+.PP
+Now that you have found the misspelled word,
+you can change it from ``thiss'' to ``this''.
+As far as edit is concerned,
+changing things is a matter of
+substituting one thing for another.
+As
+.I a
+stood for
+.I append,
+so
+.I s
+stands for
+.I substitute.
+We will use the abbreviation ``s'' to reduce the chance
+of mistyping the substitute command.
+This command will instruct edit to make the change:
+.DS I 1i
+\f32s/thiss/this/\f1
+.DE
+We first indicate the line to be changed, line 2,
+and then
+type an ``s'' to indicate we want
+edit to make a substitution.
+Inside the first set of slashes
+are the characters that we want to change,
+followed by the characters to replace them,
+and then a closing slash mark.
+To summarize:
+.DS I 1i
+2s/ \fIwhat is to be changed\fR / \fIwhat to change it to \fR/
+.DE
+If edit finds an exact match of the characters to be
+changed it will make the change
+.B only
+in the first occurrence of the characters.
+If it does not find the characters
+to be changed, it will respond:
+.DS I 1i
+Substitute pattern match failed
+.DE
+indicating that your instructions could not be carried out.
+When edit does find the characters that you want to change,
+it will make the substitution and automatically print
+the changed line, so that you can check that the correct substitution
+was made.
+In the example,
+.DS I 1i
+:\|\fB2s/thiss/this/
+.R
+And this is some more text.
+.DE
+line 2 (and line 2 only) will be searched for the characters
+``thiss'', and when the first exact match is found, ``thiss''
+will be changed to ``this''.
+Strictly speaking, it was not necessary above to
+specify the number of the line to be changed.
+In
+.DS I 1i
+:\|\fBs/thiss/this/
+.R
+.DE
+edit will assume that we mean to change
+the line where we are currently located (``.'').
+In this case,
+the command without a line number would have produced the same result
+because we were already located
+at the line we wished to change.
+.PP
+For another illustration of the substitute command,
+let us choose the line:
+.DS I 1i
+Text editing is strange, but nice.
+.DE
+You can make this line a bit more positive
+by taking out the characters ``strange, but\ '' so the line
+reads:
+.DS I 1i
+Text editing is nice.
+.DE
+A command that will first position edit at the desired line
+and then make the substitution is:
+.DS I 1i
+:\|\fB/strange/s/strange, but //
+.R
+.DE
+.LP
+What we have done here is combine our search with
+our substitution.
+Such combinations are perfectly legal,
+and speed up editing quite a bit
+once you get used to them.
+That is, you do not necessarily have to use
+line numbers to identify a line to edit.
+Instead, you may identify the line you want to change
+by asking edit to search for a specified pattern of letters
+that occurs in that line.
+The parts of the above command are:
+.in +1i
+.TS
+l l.
+\fB/strange/\fP tells edit to find the characters ``strange'' in the text
+\fBs\fP tells edit to make a substitution
+\fB/strange, but //\fP substitutes nothing at all for the characters ``strange, but ''
+.TE
+.in -1i
+.PP
+You should note the space after ``but'' in ``/strange, but /''.
+If you do not indicate that the space is to be taken out,
+your line will read:
+.DS I 1i
+.if t Text editing is nice.
+.if n Text editing is nice.
+.DE
+which looks a little funny
+because of the extra space between ``is'' and ``nice''.
+Again, we realize from this that a blank space
+is a real character to a computer, and in editing text
+we need to be aware of spaces
+within a line just as we would be aware of an ``a'' or
+a ``4''.
+.SH
+Another way to list what's in the buffer (z)
+.PP
+Although the print command is useful for looking at specific lines
+in the buffer,
+other commands may be more convenient for
+viewing large sections of text.
+You can ask to see a screen full of text at a time
+by using the command
+.B z.
+If you type
+.DS I 1i
+:\|\fB1z
+.R
+.DE
+edit will start with line 1 and continue printing lines,
+stopping either when the screen of
+your terminal is full
+or when the last line in the buffer has been printed.
+If you want to read the next segment of text, type the command
+.DS I 1i
+:\|\fBz
+.DE
+If no starting line number is given for the z command,
+printing will start at the ``current'' line, in this case the
+last line printed.
+Viewing lines in the buffer one screen full at a time
+is known as \fIpaging\fR.
+Paging can also be used to print
+a section of text on a hard-copy terminal.
+.SH
+Saving the modified text
+.PP
+This seems to be a good place to pause in our work,
+and so we should end the second session.
+If you (in haste) type ``q'' to quit the session
+your dialogue with edit will be:
+.DS I 1i
+:\|\fBq
+.R
+No write since last change (:quit! overrides)
+:
+.DE
+This is edit's warning that you have not written
+the modified contents of the buffer to disk.
+You run the risk of losing the work you did
+during the editing session since you typed the latest write
+command.
+Because in this lesson we have not written
+to disk at all, everything we have done
+would have been lost
+if edit had obeyed the \fBq\fR command.
+If you did not want to save the work done during
+this editing session, you would have to type ``q!''
+or (``quit!'')
+to confirm that you indeed wanted to end the session
+immediately,
+leaving the file as it was
+after the most recent ``write'' command.
+However,
+since you want to save what
+you have edited, you need to type:
+.DS I 1i
+:\|\fBw
+.R
+"text" 6 lines, 171 characters
+.DE
+and then follow with the commands to quit and logout:
+.DS I 1i
+:\|\fBq
+% \fBlogout\fR
+.DE
+and hang up the phone or turn off the terminal when
+\s-2UNIX\s0 asks for a name.
+Terminals connected to the port selector
+will stop after the logout command,
+and pressing keys on the keyboard will do nothing.
+.sp 1
+.PP
+This is the end of the second session on \s-2UNIX\s0 text editing.
+.bp
+.TL
+Session 3
+.SH
+Bringing text into the buffer (e)
+.PP
+Login to \s-2UNIX\s0 and make contact with edit.
+You should try to login without
+looking at the notes, but if you must
+then by all means do.
+.PP
+Did you remember to give the name of the file
+you wanted to edit?
+That is, did you type
+.DS I 1i
+% \fBedit text\fR
+.DE
+or simply
+.DS I 1i
+% \fBedit\fR
+.DE
+Both ways get you in contact with edit, but the first way
+will bring a copy of the file named ``text'' into
+the buffer.
+If you did forget to tell edit the name of your file,
+you can get it into the buffer by
+typing:
+.DS I 1i
+:\|\fBe text
+.R
+"text" 6 lines, 171 characters
+.DE
+The command
+.B edit,
+which may be abbreviated \fBe\fR,
+tells edit that you want
+to erase anything that might already be in
+the buffer and bring a copy of the file ``text'' into the buffer
+for editing.
+You may also use the edit (e) command to change files in
+the middle of an editing session,
+or to give edit the name of a new file that you want to create.
+Because the edit command clears the buffer,
+you will receive a warning if you try to edit a new file without
+having saved a copy of the old file.
+This gives you a chance to write the contents of the buffer to disk
+before editing the next file.
+.SH
+Moving text in the buffer (m)
+.PP
+Edit allows you to move lines of text
+from one location in the buffer to another
+by means of the
+.B move
+(\fBm\fR) command.
+The first two examples are for illustration only,
+though after you have read this Session
+you are welcome to return to them for practice.
+The command
+.DS I 1i
+:\|\fB2,4m$
+.R
+.DE
+directs edit to move lines 2, 3, and 4
+to the end of the buffer ($).
+The format for the move command is that you specify
+the first line to be moved, the last line to be moved,
+the move command ``m'', and the line after which
+the moved text is to be placed.
+So,
+.DS I 1i
+:\|\fB1,3m6
+.R
+.DE
+would instruct edit to move lines 1 through 3 (inclusive)
+to a location after line 6 in the buffer.
+To move only one line, say, line 4,
+to a location in the buffer after line 5,
+the command would be ``4m5''.
+.PP
+Let's move some text using the command:
+.DS I 1i
+:\|\fB5,$m1
+.R
+2 lines moved
+it does illustrate the editor.
+.DE
+After executing a command that moves more than one line of the buffer,
+edit tells how many lines were affected by the move
+and prints the last moved line for your inspection.
+If you want to see more than just the last line,
+you can then
+use the print (p), z, or number (nu) command to view more text.
+The buffer should now contain:
+.DS I 1i
+This is some sample text.
+It doesn't mean much here, but
+it does illustrate the editor.
+And this is some more text.
+Text editing is nice.
+This is text added in Session 2.
+.DE
+You can restore the original order by typing:
+.DS I 1i
+:\|\fB4,$m1
+.R
+.DE
+or, combining context searching and the move command:
+.DS I 1i
+:\|\fB/And this is some/,/This is text/m/This is some sample/
+.R
+.DE
+(Do not type both examples here!)
+The problem with combining context searching
+with the move command
+is that your chance of making a typing error
+in such a long command is greater than
+if you type line numbers.
+.SH
+Copying lines (copy)
+.PP
+The
+.B copy
+command
+is used to make a second copy of specified lines,
+leaving the original lines where they were.
+Copy
+has the same format as the move command, for example:
+.DS I 1i
+:\|\fB2,5copy $
+.R
+.DE
+makes a copy of lines 2 through 5,
+placing the added lines after the buffer's end ($).
+Experiment with the copy command
+so that you can become familiar with how it works.
+Note that the shortest abbreviation for copy is
+\f3co\f1 (and
+not the letter ``c'', which has another meaning).
+.SH
+Deleting lines (d)
+.PP
+Suppose you want to delete
+the line
+.DS I 1i
+This is text added in Session 2.
+.DE
+from the buffer.
+If you know the number of the line to be deleted,
+you can type
+that number followed by
+\fBdelete\fR or \fBd\fR.
+This example deletes line 4,
+which is ``This is text added in Session 2.''
+if you typed the commands
+suggested so far.
+.DS I 1i
+:\|\fB4d
+.R
+It doesn't mean much here, but
+.DE
+Here ``4'' is the number of the line to be deleted,
+and ``delete'' or ``d'' is the command to delete the line.
+After executing the delete command,
+edit prints the line that has become the current line (``.'').
+.PP
+If you do not happen to know the line number
+you can search for the line and then delete it using this
+sequence of commands:
+.DS I 1i
+:\|\fB/added in Session 2./
+.R
+This is text added in Session 2.
+:\|\fBd
+.R
+It doesn't mean much here, but
+.DE
+The ``/added in Session 2./''
+asks edit to locate and print
+the line containing the indicated text,
+starting its search at the current line
+and moving line by line
+until it finds the text.
+Once you are sure that you have correctly specified the line
+you want to delete,
+you can enter the delete (d) command.
+In this case it is not necessary to
+specify a line number before the ``d''.
+If no line number is given,
+edit deletes the current line (``.''),
+that is, the line found by our search.
+After the deletion, your buffer should contain:
+.DS I 1i
+This is some sample text.
+And this is some more text.
+Text editing is nice.
+It doesn't mean much here, but
+it does illustrate the editor.
+And this is some more text.
+Text editing is nice.
+This is text added in Session 2.
+It doesn't mean much here, but
+.DE
+To delete both lines 2 and 3:
+.DS I 1i
+And this is some more text.
+Text editing is nice.
+.DE
+you type
+.DS I 1i
+:\|\f32,3d\f1
+2 lines deleted
+.DE
+which specifies the range of lines from 2 to 3,
+and the operation on those lines \(em ``d'' for delete.
+If you delete more than one line
+you will receive a message
+telling you the number of lines deleted,
+as indicated in the example above.
+.PP
+The previous example assumes that you know the line numbers for
+the lines to be deleted.
+If you do not you might combine the search command
+with the delete command:
+.DS I 1i
+:\|\fB/And this is some/,/Text editing is nice./d
+.R
+.DE
+.SH
+A word or two of caution
+.PP
+In using the search function to locate lines to
+be deleted you should be
+.B
+absolutely sure
+.R
+the characters you give as the basis for the search
+will take edit to the line you want deleted.
+Edit will search for the first
+occurrence of the characters starting from where
+you last edited \-
+that is, from the line you see printed if you type dot (.).
+.PP
+A search based on too few
+characters may result in the wrong lines being deleted,
+which edit will do as easily as if you had meant it.
+For this reason, it is usually safer
+to specify the search and then delete in two separate steps,
+at least until you become familiar enough with using the editor
+that you understand how best to specify searches.
+For a beginner it is not a bad idea to double-check
+each command before pressing \s-2RETURN\s+2 to send the command on its way.
+.SH
+Undo (u) to the rescue
+.PP
+The
+.B
+undo (u)
+.R
+command has the ability to
+reverse the effects of the last command that changed the buffer.
+To undo the previous command, type
+``u'' or ``undo''.
+Undo can rescue
+the contents of the buffer from many an unfortunate mistake.
+However, its powers are not unlimited,
+so it is still wise to be reasonably
+careful about the commands you give.
+.PP
+It is possible to undo only commands which
+have the power to change the buffer \(em for example,
+delete, append, move, copy, substitute, and even undo itself.
+The commands write (w) and edit (e), which interact with disk files,
+cannot be undone, nor can commands that do not change
+the buffer, such as print.
+Most importantly,
+the
+.B only
+command that can be reversed by undo
+is the
+last ``undo-able'' command you typed.
+You can use control-H and @ to change
+commands while you are typing them,
+and undo to reverse the effect of the commands
+after you have typed them and pressed \s-2RETURN\s+2.
+.PP
+To illustrate,
+let's issue an undo command.
+Recall that the last buffer-changing command we gave deleted
+the lines formerly numbered 2 and 3.
+Typing undo at this moment will reverse the effects
+of the deletion, causing those two lines to be
+replaced in the buffer.
+.DS I 1i
+:\|\fBu
+.R
+2 more lines in file after undo
+And this is some more text.
+.DE
+Here again, edit informs you if the command affects more
+than one line,
+and prints
+the text of the line which is now ``dot'' (the current line).
+.SH
+More about the dot (.) and buffer end ($)
+.PP
+The function assumed by the symbol dot depends on its context.
+It can be used:
+.IP
+1. to exit from append mode; we type dot (and only a dot) on
+a line and press \s-2RETURN\s+2;
+.IP
+2. to refer to the line we are at in the buffer.
+.LP
+Dot can also be combined with the equal sign to get
+the number of the line currently being edited:
+.DS I 1i
+:\|\fB\&.=
+.R
+.DE
+If we type ``\fB.\fR='' we are asking for the number of the line,
+and if we type ``\fB.\fR'' we are asking for the text of the line.
+.PP
+In this editing session and the last, we used the dollar
+sign to indicate the end of the buffer
+in commands such as print, copy, and move.
+The dollar sign as a command asks edit to print the last
+line in the buffer.
+If the dollar sign is combined with the equal sign (\f3$=\f1)
+edit will print the line number corresponding to the
+last line in the buffer.
+.PP
+``\fB.\fR'' and ``$'', then, represent line numbers.
+Whenever appropriate, these symbols can be used in
+place of line numbers in commands.
+For example
+.DS I 1i
+:\|\fB\s+2.\s-2,$d
+.R
+.DE
+instructs edit to delete all lines from the current line (\fB.\fR)
+to the end of the buffer.
+.SH
+Moving around in the buffer (+ and \-)
+.PP
+When you are editing
+you often want
+to go back and re-read a previous line.
+You could specify a context search for a line you want to
+read if you remember some of its text,
+but if you simply want to see what was written a few, say 3, lines
+ago, you can type
+.DS I 1i
+\-3p
+.DE
+This tells edit to move back to a position 3 lines
+before the current line (.)
+and print that line.
+You can move forward in the buffer similarly:
+.DS I 1i
++2p
+.DE
+instructs edit to print the line that is 2
+ahead of your current position.
+.PP
+You may use ``+'' and ``\-'' in any command where edit
+accepts line numbers.
+Line numbers specified with ``+'' or ``\-''
+can be combined to print a range of lines.
+The command
+.DS I 1i
+:\|\fB\-1,+2copy$
+.R
+.DE
+makes a copy of 4 lines: the current line, the line before it,
+and the two after it.
+The copied lines will be placed after the last line
+in the buffer ($),
+and the original lines referred to by ``\-1'' and ``+2''
+remain where they are.
+.PP
+Try typing only ``\-''; you will move back one line just as
+if you had typed ``\-1p''.
+Typing the command ``+'' works similarly.
+You might also try typing a few plus or minus signs in a row
+(such as ``+++'') to see edit's response.
+Typing \s-2RETURN\s+2 alone on a line is the equivalent
+of typing ``+1p''; it will move you one line ahead in the buffer
+and print that line.
+.PP
+If you are at the last line of the buffer and try
+to move further ahead, perhaps by typing a ``+'' or
+a carriage return alone on the line,
+edit will remind you that you are at the end of the buffer:
+.sp
+.nf
+.ti 1i
+At end-of-file
+.br
+or
+.ti 1i
+Not that many lines in buffer
+.fi
+.LP
+Similarly, if you try to move to a position before the first line,
+edit will print one of these messages:
+.sp
+.nf
+.ti 1i
+Nonzero address required on this command
+.br
+or
+.ti 1i
+Negative address \- first buffer line is 1
+.fi
+.LP
+The number associated with a buffer line is the line's ``address'',
+in that it can be used to locate the line.
+.SH
+Changing lines (c)
+.PP
+You can also delete certain lines and
+insert new text in their place.
+This can be accomplished easily with the
+.B "change (c)"
+command.
+The change command instructs edit to delete specified lines
+and then switch to text input mode to
+accept the text that will replace them.
+Let's say you want to change the first two lines in the buffer:
+.DS I 1i
+This is some sample text.
+And this is some more text.
+.DE
+to read
+.DS I 1i
+This text was created with the \s-2UNIX\s0 text editor.
+.DE
+To do so, you type:
+.DS I 1i
+:\|\fB1,2c
+.R
+2 lines changed
+.B
+This text was created with the \s-2UNIX\s0 text editor.
+\s+2\&.\s-2
+.R
+:
+.DE
+In the command
+.B 1,2c
+we specify that we want to change
+the range of lines beginning with 1 and ending with 2
+by giving line numbers as with the print command.
+These lines will be deleted.
+After you type \s-2RETURN\s+2 to end the change command,
+edit notifies you if more than one line will be changed
+and places you in text input mode.
+Any text typed on the following lines will be inserted into
+the position where lines were deleted by the change command.
+.B
+You will remain in text input mode until you exit in the usual way,
+by typing a period alone on a line.
+.R
+Note that the number of lines added to the buffer need not be
+the same as the number of lines deleted.
+.sp 1
+.PP
+This is the end of the third session on text editing with \s-2UNIX\s0.
+.bp
+.SH
+.ce 1
+\s+2Session 4\s0
+.sp
+.PP
+This lesson covers several topics, starting with
+commands that apply throughout the buffer,
+characters with special meanings,
+and how to issue \s-2UNIX\s0 commands while in the editor.
+The next topics deal with files:
+more on reading and writing,
+and methods of recovering files lost in a crash.
+The final section suggests sources of further information.
+.SH
+Making commands global (g)
+.PP
+One disadvantage to the commands we have used for
+searching or substituting is that if you
+have a number of instances of a word to change
+it appears that you have to type the command
+repeatedly, once for
+each time the change needs to be made.
+Edit, however, provides a way to make commands
+apply to the entire contents of the buffer \-
+the
+.B
+global (g)
+.R
+command.
+.PP
+To print all lines
+containing a certain sequence of characters
+(say, ``text'')
+the command is:
+.DS I 1i
+:\|\fBg/text/p
+.R
+.DE
+The ``g'' instructs edit to
+make a global search for all lines
+in the buffer containing the characters ``text''.
+The ``p'' prints the lines found.
+.PP
+To issue a global command, start by typing a ``g'' and then a search
+pattern identifying
+the lines to be affected.
+Then, on the same line, type the command to be
+executed for the identified lines.
+Global substitutions are frequently useful.
+For example,
+to change all instances of the word ``text'' to the word ``material''
+the command would be a combination of the global search and the
+substitute command:
+.DS I 1i
+:\|\fBg/text/s/text/material/g
+.R
+.DE
+Note the ``g'' at the end of the global command,
+which instructs edit to change
+each and every instance of ``text'' to ``material''.
+If you do not type the ``g'' at the end of the command
+only the
+.I first
+instance of ``text'' \fIin each line\fR will be changed
+(the normal result of the substitute command).
+The ``g'' at the end of the command is independent of the ``g''
+at the beginning.
+You may give a command such as:
+.DS I 1i
+:\|\fB5s/text/material/g
+.R
+.DE
+to change every instance of ``text'' in line 5 alone.
+Further, neither command will change ``text'' to ``material''
+if ``Text'' begins with a capital rather than a lower-case
+.I t.
+.PP
+Edit does not automatically print the lines modified by a
+global command.
+If you want the lines to be printed, type a ``p''
+at the end of the global command:
+.DS I 1i
+:\|\fBg/text/s/text/material/gp
+.R
+.DE
+You should be careful
+about using the global command in combination with any other \-
+in essence, be sure of what you are telling edit to do
+to the entire buffer.
+For example,
+.DS I 1i
+:\|\fBg/ /d
+.R
+72 less lines in file after global
+.DE
+will delete every line containing a blank anywhere in it.
+This could adversely affect
+your document, since most lines have spaces between words
+and thus would be deleted.
+After executing the global command,
+edit will print a warning if the command added or deleted more than one line.
+Fortunately, the undo command can reverse
+the effects of a global command.
+You should experiment with the global command
+on a small file of text to see what it can do for you.
+.SH
+More about searching and substituting
+.PP
+In using slashes to identify a character string
+that we want to search for or change,
+we have always specified the exact characters.
+There is a less tedious way to
+repeat the same string of characters.
+To change ``text'' to ``texts'' we may type either
+.DS I 1i
+:\|\fB/text/s/text/texts/
+.R
+.DE
+as we have done in the past,
+or a somewhat abbreviated command:
+.DS I 1i
+:\|\fB/text/s//texts/
+.R
+.DE
+In this example, the characters to be changed
+are not specified \-
+there are no characters, not even a space,
+between the two slash marks
+that indicate what is to be changed.
+This lack of characters between the slashes
+is taken by the editor to mean
+``use the characters we last searched for as the characters to be changed.''
+.PP
+Similarly, the last context search may be repeated
+by typing a pair of slashes with nothing between them:
+.DS I 1i
+:\|\fB/does/
+.R
+It doesn't mean much here, but
+:\|\fB//
+.R
+it does illustrate the editor.
+.DE
+(You should note that the search command found the characters ``does''
+in the word ``doesn't'' in the first search request.)
+Because no characters are specified for the second search,
+the editor scans the buffer for the next occurrence of the
+characters ``does''.
+.PP
+Edit normally searches forward through the buffer,
+wrapping around from the end of the buffer to the beginning,
+until the specified character string is found.
+If you want to search in the reverse direction,
+use question marks (?) instead of slashes
+to surround the characters you are searching for.
+.PP
+It is also possible
+to repeat the last substitution
+without having to retype the entire command.
+An ampersand (&) used as a command
+repeats the most recent substitute command,
+using the same search and replacement patterns.
+After altering the current line by typing
+.DS I 1i
+:\|\fBs/text/texts/
+.R
+.DE
+you type
+.DS I 1i
+:\|\fB/text/&
+.R
+.DE
+or simply
+.DS I 1i
+:\|\fB//&
+.R
+.DE
+to make the same change on the next line in the buffer
+containing the characters ``text''.
+.SH
+Special characters
+.PP
+Two characters have special meanings when
+used in specifying searches: ``$'' and ``^''.
+``$'' is taken by the editor to mean ``end of the line''
+and is used to identify strings
+that occur at the end of a line.
+.DS I 1i
+:\|\fBg/text.$/s//material./p
+.R
+.DE
+tells the editor to search for all lines ending in ``text.''
+(and nothing else, not even a blank space),
+to change each final ``text.'' to ``material.'',
+and print the changed lines.
+.PP
+The symbol ``^'' indicates the beginning of a line.
+Thus,
+.DS I 1i
+:\|\fBs/^/1. /
+.R
+.DE
+instructs the editor to insert ``1.'' and a space at the beginning
+of the current line.
+.PP
+The characters ``$'' and ``^'' have special meanings only in the context
+of searching.
+At other times, they are ordinary characters.
+If you ever need to search for a character that has a special meaning,
+you must indicate that the
+character is to lose temporarily
+its special significance by typing another special character,
+the backslash (\\), before it.
+.DS I 1i
+:\|\fBs/\\\\\&$/dollar/
+.R
+.DE
+looks for the character ``$'' in the current
+line and replaces it by the word ``dollar''.
+Were it not for the backslash, the ``$'' would have represented
+``the end of the line'' in your search
+rather than the character ``$''.
+The backslash retains its special significance
+unless it is preceded by another backslash.
+.SH
+Issuing \s-2UNIX\s0 commands from the editor
+.PP
+After creating several files with the editor,
+you may want to delete files
+no longer useful to you or ask for a list of your files.
+Removing and listing files are not functions of the editor,
+and so they require the use of \s-2UNIX\s0 system commands
+(also referred to as ``shell'' commands, as
+``shell'' is the name of the program that processes \s-2UNIX\s0 commands).
+You do not need to quit the editor to execute a \s-2UNIX\s0 command
+as long as you indicate that it
+is to be sent to the shell for execution.
+To use the \s-2UNIX\s0 command
+.B rm
+to remove the file named ``junk'' type:
+.DS I 1i
+:\|\fB!rm junk
+.R
+!
+:
+.DE
+The exclamation mark (!)
+indicates that the rest of the line is to be processed as a shell command.
+If the buffer contents have not been written since the last change,
+a warning will be printed before the command is executed:
+.DS I 1i
+[No write since last change]
+.DE
+The editor prints a ``!'' when the command is completed.
+Other tutorials describe useful features of the system,
+of which an editor is only one part.
+.SH
+Filenames and file manipulation
+.PP
+Throughout each editing session,
+edit keeps track of the name of the file being edited as the
+.I "current filename."
+Edit remembers as the current filename the name given
+when you entered the editor.
+The current filename changes whenever the edit (e) command
+is used to specify a new file.
+Once edit has recorded a current filename,
+it inserts that name into any command where a filename has been omitted.
+If a write command does not specify a file,
+edit, as we have seen, supplies the current filename.
+If you are editing a file named ``draft3'' having 283 lines in it,
+you can have the editor write onto a different file
+by including its name in the write command:
+.DS I 1i
+:\fB\|w chapter3
+.R
+"chapter3" [new file] 283 lines, 8698 characters
+.DE
+The current filename remembered by the editor
+.I
+will not be changed as a result of the write command.
+.R
+Thus, if the next write command
+does not specify a name,
+edit will write onto the current file (``draft3'')
+and not onto the file ``chapter3''.
+.SH
+The file (f) command
+.PP
+To ask for the current filename, type
+.B file
+(or
+.B f ).
+In response, the editor provides current information about the buffer,
+including the filename, your current position, the number of
+lines in the buffer,
+and the percent of the distance through the file
+your current location is.
+.DS I 1i
+:\|\fBf
+.R
+"text" [Modified] line 3 of 4 --75%--
+.DE
+.\"The expression ``[Edited]'' indicates that the buffer contains
+.\"either the editor's copy of the existing file ``text''
+.\"or a file which you are just now creating.
+If the contents of the buffer have changed
+since the last time the file was written,
+the editor will tell you that the file has been ``[Modified]''.
+After you save the changes by writing onto a disk file,
+the buffer will no longer be considered modified:
+.DS I 1i
+:\|\fBw
+.R
+"text" 4 lines, 88 characters
+:\|\fBf
+.R
+"text" line 3 of 4 --75%--
+.DE
+.SH
+Reading additional files (r)
+.PP
+The
+\f3read (r)\f1 command allows you to add the contents of a file
+to the buffer
+at a specified location,
+essentially copying new lines
+between two existing lines.
+To use it, specify the line after which the new text will be placed,
+the \f3read (r)\f1 command,
+and then the name of the file.
+If you have a file named ``example'', the command
+.DS I 1i
+:\|\fB$r example
+.R
+"example" 18 lines, 473 characters
+.DE
+reads the file ``example''
+and adds it to the buffer after the last line.
+The current filename is not changed by the read command.
+.SH
+Writing parts of the buffer
+.PP
+The
+.B
+write (w)
+.R
+command can write all or part of the buffer
+to a file you specify.
+We are already familiar with
+writing the entire contents of the
+buffer to a disk file.
+To write only part of the buffer onto a file,
+indicate the beginning and ending lines before the write command,
+for example
+.DS I 1i
+:\|\fB45,$w ending
+.R
+.DE
+Here all lines from 45 through the end of the buffer
+are written onto the file named
+.I ending.
+The lines remain in the buffer
+as part of the document you are editing,
+and you may continue to edit the entire buffer.
+Your original file is unaffected
+by your command to write part of the buffer
+to another file.
+Edit still remembers whether you have saved changes to the buffer
+in your original file or not.
+.SH
+Recovering files
+.PP
+Although it does not happen very often,
+there are times \s-2UNIX\s+2 stops working
+because of some malfunction.
+This situation is known as a \fIcrash\fR.
+Under most circumstances,
+edit's crash recovery feature
+is able to save work to within a few lines of changes
+before a crash (or an accidental phone hang up).
+If you lose the contents of an editing buffer in a system crash,
+you will normally receive mail when you login that gives
+the name of the recovered file.
+To recover the file,
+enter the editor and type the command
+.B recover
+(\fBrec\fR),
+followed by the name of the lost file.
+For example,
+to recover the buffer for an edit session
+involving the file ``chap6'', the command is:
+.DS I 1i
+.R
+:\|\fBrecover chap6
+.R
+.DE
+Recover is sometimes unable to save the entire buffer successfully,
+so always check the contents of the saved buffer carefully
+before writing it back onto the original file.
+For best results,
+write the buffer to a new file temporarily
+so you can examine it without risk to the original file.
+Unfortunately,
+you cannot use the recover command
+to retrieve a file you removed
+using the shell command \f3rm\f1.
+.SH
+Other recovery techniques
+.PP
+If something goes wrong when you are using the editor,
+it may be possible to save your work by using the command
+.B preserve
+(\fBpre\fR),
+which saves the buffer as if the system had crashed.
+If you are writing a file and you get the message
+``Quota exceeded'', you have tried to use more disk storage
+than is allotted to your account.
+.I
+Proceed with caution
+.R
+because it is likely that only a part
+of the editor's buffer is now present in the file you tried to write.
+In this case you should use the shell escape from the editor (!)
+to remove some files you don't need and try to write
+the file again.
+If this is not possible and you cannot find someone to help you,
+enter the command
+.DS I 1i
+:\|\fBpreserve
+.R
+.DE
+and wait for the reply,
+.DS I 1i
+File preserved.
+.DE
+If you do not receive this reply,
+seek help immediately.
+Do not simply leave the editor.
+If you do, the buffer will be lost,
+and you may not be able to save your file.
+If the reply is ``File preserved.''
+you can leave the editor
+(or logout)
+to remedy the situation.
+After a preserve, you can use the recover command
+once the problem has been corrected,
+or the \fB\-r\fR option of the edit command
+if you leave the editor and want to return.
+.PP
+If you make an undesirable change to the buffer
+and type a write command before discovering your mistake,
+the modified version will replace any previous version of the file.
+Should you ever lose a good version of a document in this way,
+do not panic and leave the editor.
+As long as you stay in the editor,
+the contents of the buffer remain accessible.
+Depending on the nature of the problem,
+it may be possible
+to restore the buffer to a more complete
+state with the undo command.
+After fixing the damaged buffer, you can again write the file
+to disk.
+.SH
+Further reading and other information
+.PP
+Edit is an editor designed for beginning and casual users.
+It is actually a version of a more powerful editor called
+.I ex.
+These lessons are intended to introduce you to the editor
+and its more commonly-used commands.
+We have not covered all of the editor's commands,
+but a selection of commands
+that should be sufficient to accomplish most of your editing tasks.
+You can find out more about the editor in the
+.I
+Ex Reference Manual,
+.R
+which is applicable to both
+.I ex
+and
+.I edit.
+One way to become familiar with the manual is to begin by reading
+the description of commands that you already know.
+.bd I 3
+.SH
+Using
+.I ex
+.fl
+.bd I
+.PP
+As you become more experienced with using the editor,
+you may still find that edit continues to meet your needs.
+However, should you become interested in using
+.I ex,
+it is easy to switch.
+To begin an editing session with
+.I ex,
+use the name
+.B ex
+in your command instead of
+.B edit.
+.PP
+Edit commands also work in
+.I ex,
+but the editing environment is somewhat different.
+You should be aware of a few differences
+between
+.I ex
+and
+.I edit.
+In edit, only the characters ``^'', ``$'', and ``\\'' have
+special meanings in searching the buffer
+or indicating characters to be changed by a substitute command.
+Several additional characters have special
+meanings in ex, as described in the
+.I
+Ex Reference Manual.
+.R
+Another feature of the edit environment prevents users from
+accidently entering two alternative modes of editing,
+.I open
+and
+.I visual,
+in which
+the editor behaves quite differently from normal command mode.
+If you are using ex and you encounter strange behavior,
+you may have accidently entered open mode by typing ``o''.
+Type the \s-2ESC\s0 key and then a ``Q''
+to get out of open or visual mode and back into
+the regular editor command mode.
+The document
+.I
+An Introduction to Display Editing with Vi\|\|
+.R
+provide full details of visual mode.
+.bp
+.SH
+.ce 1
+\s+2Index\s0
+.LP
+.sp 2
+.2C
+.nf
+addressing, \fIsee\fR line numbers
+ampersand, 20
+append mode, 6-7
+append (a) command, 6, 7, 9
+``At end of file'' (message), 18
+backslash (\\), 21
+buffer, 3
+caret (^), 10, 20
+change (c) command, 18
+command mode, 5-6
+``Command not found'' (message), 6
+context search, 10-12, 19-21
+control characters (``^'' notation), 10
+control-H, 7
+copy (co) command, 15
+corrections, 7, 16
+current filename, 21
+current line (\|.\|), 11, 17
+delete (d) command, 15-16
+dial-up, 5
+disk, 3
+documentation, 3, 23
+dollar ($), 10, 11, 17, 20-21
+dot (\f3\|.\|\f1) 11, 17
+edit (text editor), 3, 5, 23
+edit (e) command, 5, 9, 14
+editing commands:
+.in +.25i
+append (a), 6, 7, 9
+change (c), 18
+copy (co), 15
+delete (d), 15-16
+edit (text editor), 3, 5, 23
+edit (e), 5, 9, 14
+file (f), 21-22
+global (g), 19
+move (m), 14-15
+number (nu), 11
+preserve (pre), 22-23
+print (p), 10
+quit (q), 8, 13
+read (r), 22
+recover (rec), 22, 23
+substitute (s), 11-12, 19, 20
+undo (u), 16-17, 23
+write (w), 8, 13, 21, 22
+z, 12-13
+! (shell escape), 21
+$=, 17
++, 17
+\-, 17
+//, 12, 20
+??, 20
+\&., 11, 17
+\&.=, 11, 17
+.in -.25i
+entering text, 3, 6-7
+erasing
+.in +.25i
+characters (^H), 7
+lines (@), 7
+.in -.25i
+error corrections, 7, 16
+ex (text editor), 23
+\fIEx Reference Manual\fR, 23
+exclamation (!), 21
+file, 3
+file (f) command, 21-22
+file recovery, 22-23
+filename, 3, 21
+global (g) command, 19
+input mode, 6-7
+Interrupt (message), 9
+line numbers, \fIsee also\fR current line
+.in +.25i
+dollar sign ($), 10, 11, 17
+dot (\|.\|), 11, 17
+relative (+ and \-), 17
+.in -.25i
+list, 10
+logging in, 4-6
+logging out, 8
+``Login incorrect'' (message), 5
+minus (\-), 17
+move (m) command, 14-15
+``Negative address\(emfirst buffer line is 1'' (message), 18
+``No current filename'' (message), 8
+``No such file or directory'' (message), 5, 6
+``No write since last change'' (message), 21
+non-printing characters, 10
+``Nonzero address required'' (message), 18
+``Not an editor command'' (message), 6
+``Not that many lines in buffer'' (message), 18
+number (nu) command, 11
+password, 5
+period (\|.\|), 11, 17
+plus (+), 17
+preserve (pre) command, 22-23
+print (p) command, 10
+program, 3
+prompts
+.in .25i
+% (\s-2UNIX\s0), 5
+: (edit), 5, 6, 7
+\0 (append), 7
+.in -.25i
+question (?), 20
+quit (q) command, 8, 13
+read (r) command, 22
+recover (rec) command, 22, 23
+recovery, \fIsee\fR\| file recovery
+references, 3, 23
+remove (rm) command, 21, 22
+reverse command effects (undo), 16-17, 23
+searching, 10-12, 19-21
+shell, 21
+shell escape (!), 21
+slash (/), 11-12, 20
+special characters (^, $, \\), 10, 11, 17, 20-21
+substitute (s) command, 11-12, 19, 20
+terminals, 4-5
+text input mode, 7
+undo (u) command, 16-17, 23
+\s-1UNIX\s0, 3
+write (w) command, 8, 13, 21, 22
+z command, 12-13
+
diff --git a/contrib/nvi/docs/USD.doc/exref/Makefile b/contrib/nvi/docs/USD.doc/exref/Makefile
new file mode 100644
index 000000000000..11b5423607e7
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/exref/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.8 (Berkeley) 10/10/96
+
+ROFF= groff
+TBL= tbl
+
+all: exref.ps summary.ps
+
+exref.ps: ex.rm
+ ${TBL} ex.rm | ${ROFF} -ms > $@
+ chmod 444 $@
+
+summary.ps: ex.summary
+ ${TBL} ex.summary | ${ROFF} -ms > $@
+ chmod 444 $@
+
+clean:
+ rm -f exref.ps summary.ps
diff --git a/contrib/nvi/docs/USD.doc/exref/ex.rm b/contrib/nvi/docs/USD.doc/exref/ex.rm
new file mode 100644
index 000000000000..217bad46d277
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/exref/ex.rm
@@ -0,0 +1,2213 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ex.rm 8.5 (Berkeley) 8/18/96
+.\"
+.nr LL 6.5i
+.nr FL 6.5i
+.EH 'USD:12-%''Ex Reference Manual'
+.OH 'Ex Reference Manual''USD:12-%'
+.nr )P 0
+.de ZP
+.nr pd \\n()P
+.nr )P 0
+.if \\n(.$=0 .IP
+.if \\n(.$=1 .IP "\\$1"
+.if \\n(.$>=2 .IP "\\$1" "\\$2"
+.nr )P \\n(pd
+.rm pd
+..
+.de LC
+.br
+.sp .1i
+.ne 4
+.LP
+.ta 4.0i
+..
+.bd S B 3
+.\".RP
+.TL
+Ex Reference Manual
+.br
+Version 3.7
+.AU
+William Joy
+.AU
+Mark Horton
+.AI
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, Ca. 94720
+.AB
+.I Ex
+a line oriented text editor, which supports both command and display
+oriented editing.
+This reference manual describes the command oriented part of
+.I ex;
+the display editing features of
+.I ex
+are described in
+.I "An Introduction to Display Editing with Vi."
+Other documents about the editor include the introduction
+.I "Edit: A tutorial",
+the
+.I "Ex/edit Command Summary",
+and a
+.I "Vi Quick Reference"
+card.
+.AE
+.NH 1
+Starting ex
+.PP
+.FS
+The financial support of an \s-2IBM\s0 Graduate Fellowship and the National
+Science Foundation under grants MCS74-07644-A03 and MCS78-07291 is gratefully
+acknowledged.
+.FE
+Each instance of the editor has a set of options,
+which can be set to tailor it to your liking.
+The command
+.I edit
+invokes a version of
+.I ex
+designed for more casual or beginning
+users by changing the default settings of some of these options.
+To simplify the description which follows we
+assume the default settings of the options.
+.PP
+When invoked,
+.I ex
+determines the terminal type from the \s-2TERM\s0 variable in the environment.
+It there is a \s-2TERMCAP\s0 variable in the environment, and the type
+of the terminal described there matches the \s-2TERM\s0 variable,
+then that description
+is used. Also if the \s-2TERMCAP\s0 variable contains a pathname (beginning
+with a \fB/\fR) then the editor will seek the description of the terminal
+in that file (rather than the default /etc/termcap).
+If there is a variable \s-2EXINIT\s0 in the environment, then the editor
+will execute the commands in that variable,
+otherwise if there is a file
+.I \&.exrc
+in your \s-2HOME\s0 directory
+.I ex
+reads commands from that file, simulating a
+.I source
+command.
+Option setting commands placed in
+\s-2EXINIT\s0 or
+.I \&.exrc
+will be executed before each editor session.
+.PP
+A command to enter
+.I ex
+has the following prototype:\(dg
+.FS
+\(dg Brackets `[' `]' surround optional parameters here.
+.FE
+.DS
+\fBex\fP [ \fB\-\fP ] [ \fB\-v\fP ] [ \fB\-t\fP \fItag\fP ] [ \fB\-r\fP ] [ \fB\-l\fP ] [ \fB\-w\fP\fIn\fP ] [ \fB\-x\fP ] [ \fB\-R\fP ] [ \fB+\fP\fIcommand\fP ] name ...
+.DE
+The most common case edits a single file with no options, i.e.:
+.DS
+\fBex\fR name
+.DE
+The
+.B \-
+command line option
+option suppresses all interactive-user feedback
+and is useful in processing editor scripts in command files.
+The
+.B \-v
+option is equivalent to using
+.I vi
+rather than
+.I ex.
+The
+.B \-t
+option is equivalent to an initial
+.I tag
+command, editing the file containing the
+.I tag
+and positioning the editor at its definition.
+The
+.B \-r
+option is used in recovering after an editor or system crash,
+retrieving the last saved version of the named file or,
+if no file is specified,
+typing a list of saved files.
+The
+.B \-l
+option sets up for editing \s-2LISP\s0, setting the
+.I showmatch
+and
+.I lisp
+options.
+The
+.B \-w
+option sets the default window size to
+.I n,
+and is useful on dialups to start in small windows.
+The
+.B \-x
+option causes
+.I ex
+to prompt for a
+.I key ,
+which is used to encrypt and decrypt the contents of the file,
+which should already be encrypted using the same key,
+see
+.I crypt (1).
+The
+.B \-R
+option sets the
+.I readonly
+option at the start.
+.I Name
+arguments indicate files to be edited.
+An argument of the form
+\fB+\fIcommand\fR
+indicates that the editor should begin by executing the specified command.
+If
+.I command
+is omitted, then it defaults to ``$'', positioning the editor at the last
+line of the first file initially. Other useful commands here are scanning
+patterns of the form ``/pat'' or line numbers, e.g. ``+100'' starting
+at line 100.
+.NH 1
+File manipulation
+.NH 2
+Current file
+.PP
+.I Ex
+is normally editing the contents of a single file,
+whose name is recorded in the
+.I current
+file name.
+.I Ex
+performs all editing actions in a buffer
+(actually a temporary file)
+into which the text of the file is initially read.
+Changes made to the buffer have no effect on the file being
+edited unless and until the buffer contents are written out to the
+file with a
+.I write
+command.
+After the buffer contents are written,
+the previous contents of the written file are no longer accessible.
+When a file is edited,
+its name becomes the current file name,
+and its contents are read into the buffer.
+.PP
+The current file is almost always considered to be
+.I edited.
+This means that the contents of the buffer are logically
+connected with the current file name,
+so that writing the current buffer contents onto that file,
+even if it exists,
+is a reasonable action.
+If the current file is not
+.I edited
+then
+.I ex
+will not normally write on it if it already exists.*
+.FS
+* The
+.I file
+command will say ``[Not edited]'' if the current file is not considered
+edited.
+.FE
+.NH 2
+Alternate file
+.PP
+Each time a new value is given to the current file name,
+the previous current file name is saved as the
+.I alternate
+file name.
+Similarly if a file is mentioned but does not become the current file,
+it is saved as the alternate file name.
+.NH 2
+Filename expansion
+.PP
+Filenames within the editor may be specified using the normal
+shell expansion conventions.
+In addition,
+the character `%' in filenames is replaced by the
+.I current
+file name and the character
+`#' by the
+.I alternate
+file name.\(dg
+.FS
+\(dg This makes it easy to deal alternately with
+two files and eliminates the need for retyping the
+name supplied on an
+.I edit
+command after a
+.I "No write since last change"
+diagnostic is received.
+.FE
+.NH 2
+Multiple files and named buffers
+.PP
+If more than one file is given on the command line,
+then the first file is edited as described above.
+The remaining arguments are placed with the first file in the
+.I "argument list."
+The current argument list may be displayed with the
+.I args
+command.
+The next file in the argument list may be edited with the
+.I next
+command.
+The argument list may also be respecified by specifying
+a list of names to the
+.I next
+command.
+These names are expanded,
+the resulting list of names becomes the new argument list,
+and
+.I ex
+edits the first file on the list.
+.PP
+For saving blocks of text while editing, and especially when editing
+more than one file,
+.I ex
+has a group of named buffers.
+These are similar to the normal buffer, except that only a limited number
+of operations are available on them.
+The buffers have names
+.I a
+through
+.I z.\(dd
+.FS
+\(dd It is also possible to refer to
+.I A
+through
+.I Z;
+the upper case buffers are the same as the lower but commands
+append to named buffers rather than replacing
+if upper case names are used.
+.FE
+.NH 2
+Read only
+.PP
+It is possible to use
+.I ex
+in
+.I "read only"
+mode to look at files that you have no intention of modifying.
+This mode protects you from accidently overwriting the file.
+Read only mode is on when the
+.I readonly
+option is set.
+It can be turned on with the
+.B \-R
+command line option,
+by the
+.I view
+command line invocation,
+or by setting the
+.I readonly
+option.
+It can be cleared by setting
+.I noreadonly .
+It is possible to write, even while in read only mode, by indicating
+that you really know what you are doing.
+You can write to a different file, or can use the ! form of write,
+even while in read only mode.
+.NH 1
+Exceptional Conditions
+.NH 2
+Errors and interrupts
+.PP
+When errors occur
+.I ex
+(optionally) rings the terminal bell and, in any case, prints an error
+diagnostic. If the primary input is from a file, editor processing
+will terminate. If an interrupt signal is received,
+.I ex
+prints ``Interrupt'' and returns to its command level. If the primary
+input is a file, then
+.I ex
+will exit when this occurs.
+.NH 2
+Recovering from hangups and crashes
+.PP
+If a hangup signal is received and the buffer has been modified since
+it was last written out, or if the system crashes, either the editor
+(in the first case) or the system (after it reboots in the second) will
+attempt to preserve the buffer. The next time you log in you should be
+able to recover the work you were doing, losing at most a few lines of
+changes from the last point before the hangup or editor crash. To
+recover a file you can use the
+.B \-r
+option. If you were editing the file
+.I resume,
+then you should change
+to the directory where you were when the crash occurred, giving the command
+.DS
+\fBex \-r\fP\fI resume\fP
+.DE
+After checking that the retrieved file is indeed ok, you can
+.I write
+it over the previous contents of that file.
+.PP
+You will normally get mail from the system telling you when a file has
+been saved after a crash. The command
+.DS
+\fBex\fP \-\fBr\fP
+.DE
+will print a list of the files which have been saved for you.
+(In the case of a hangup,
+the file will not appear in the list,
+although it can be recovered.)
+.NH 1
+Editing modes
+.PP
+.I Ex
+has five distinct modes. The primary mode is
+.I command
+mode. Commands are entered in command mode when a `:' prompt is
+present, and are executed each time a complete line is sent. In
+.I "text input"
+mode
+.I ex
+gathers input lines and places them in the file. The
+.I append,
+.I insert,
+and
+.I change
+commands use text input mode.
+No prompt is printed when you are in text input mode.
+This mode is left by typing a `.' alone at the beginning of a line, and
+.I command
+mode resumes.
+.PP
+The last three modes are
+.I open
+and
+.I visual
+modes, entered by the commands of the same name, and, within open and
+visual modes
+.I "text insertion"
+mode.
+.I Open
+and
+.I visual
+modes allow local editing operations to be performed on the text in the
+file. The
+.I open
+command displays one line at a time on any terminal while
+.I visual
+works on \s-2CRT\s0 terminals with random positioning cursors, using the
+screen as a (single) window for file editing changes.
+These modes are described (only) in
+.I "An Introduction to Display Editing with Vi."
+.NH
+Command structure
+.PP
+Most command names are English words,
+and initial prefixes of the words are acceptable abbreviations.
+The ambiguity of abbreviations is resolved in favor of the more commonly
+used commands.*
+.FS
+* As an example, the command
+.I substitute
+can be abbreviated `s'
+while the shortest available abbreviation for the
+.I set
+command is `se'.
+.FE
+.NH 2
+Command parameters
+.PP
+Most commands accept prefix addresses specifying the lines in the file
+upon which they are to have effect.
+The forms of these addresses will be discussed below.
+A number of commands also may take a trailing
+.I count
+specifying the number of lines to be involved in the command.\(dg
+.FS
+\(dg Counts are rounded down if necessary.
+.FE
+Thus the command ``10p'' will print the tenth line in the buffer while
+``delete 5'' will delete five lines from the buffer,
+starting with the current line.
+.PP
+Some commands take other information or parameters,
+this information always being given after the command name.\(dd
+.FS
+\(dd Examples would be option names in a
+.I set
+command i.e. ``set number'',
+a file name in an
+.I edit
+command,
+a regular expression in a
+.I substitute
+command,
+or a target address for a
+.I copy
+command, i.e. ``1,5 copy 25''.
+.FE
+.NH 2
+Command variants
+.PP
+A number of commands have two distinct variants.
+The variant form of the command is invoked by placing an
+`!' immediately after the command name.
+Some of the default variants may be controlled by options;
+in this case, the `!' serves to toggle the default.
+.NH 2
+Flags after commands
+.PP
+The characters `#', `p' and `l' may be placed after many commands.**
+.FS
+**
+A `p' or `l' must be preceded by a blank or tab
+except in the single special case `dp'.
+.FE
+In this case, the command abbreviated by these characters
+is executed after the command completes.
+Since
+.I ex
+normally prints the new current line after each change, `p' is rarely necessary.
+Any number of `+' or `\-' characters may also be given with these flags.
+If they appear, the specified offset is applied to the current line
+value before the printing command is executed.
+.NH 2
+Comments
+.PP
+It is possible to give editor commands which are ignored.
+This is useful when making complex editor scripts
+for which comments are desired.
+The comment character is the double quote: ".
+Any command line beginning with " is ignored.
+Comments beginning with " may also be placed at the ends
+of commands, except in cases where they could be confused as part
+of text (shell escapes and the substitute and map commands).
+.NH 2
+Multiple commands per line
+.PP
+More than one command may be placed on a line by separating each pair
+of commands by a `|' character.
+However the
+.I global
+commands,
+comments,
+and the shell escape `!'
+must be the last command on a line, as they are not terminated by a `|'.
+.NH 2
+Reporting large changes
+.PP
+Most commands which change the contents of the editor buffer give
+feedback if the scope of the change exceeds a threshold given by the
+.I report
+option.
+This feedback helps to detect undesirably large changes so that they may
+be quickly and easily reversed with an
+.I undo.
+After commands with more global effect such as
+.I global
+or
+.I visual,
+you will be informed if the net change in the number of lines
+in the buffer during this command exceeds this threshold.
+.NH 1
+Command addressing
+.NH 2
+Addressing primitives
+.IP \fB.\fR 20
+The current line.
+Most commands leave the current line as the last line which they affect.
+The default address for most commands is the current line,
+thus `\fB.\fR' is rarely used alone as an address.
+.IP \fIn\fR 20
+The \fIn\fRth line in the editor's buffer, lines being numbered
+sequentially from 1.
+.IP \fB$\fR 20
+The last line in the buffer.
+.IP \fB%\fR 20
+An abbreviation for ``1,$'', the entire buffer.
+.IP \fI+n\fR\ \fI\-n\fR 20
+An offset relative to the current buffer line.\(dg
+.FS
+\(dg
+The forms `.+3' `+3' and `+++' are all equivalent;
+if the current line is line 100 they all address line 103.
+.FE
+.IP \fB/\fIpat\fR\fB/\fR\ \fB?\fIpat\fR\fB?\fR 20
+Scan forward and backward respectively for a line containing \fIpat\fR, a
+regular expression (as defined below). The scans normally wrap around the end
+of the buffer.
+If all that is desired is to print the next line containing \fIpat\fR, then
+the trailing \fB/\fR or \fB?\fR may be omitted.
+If \fIpat\fP is omitted or explicitly empty, then the last
+regular expression specified is located.\(dd
+.FS
+\(dd The forms \fB\e/\fP and \fB\e?\fP scan
+using the last regular expression used in a scan; after a substitute
+\fB//\fP and \fB??\fP would scan using the substitute's regular expression.
+.FE
+.IP \fB\(aa\(aa\fP\ \fB\(aa\fP\fIx\fP 20
+Before each non-relative motion of the current line `\fB.\fP',
+the previous current line is marked with a tag, subsequently referred to as
+`\(aa\(aa'.
+This makes it easy to refer or return to this previous context.
+Marks may also be established by the
+.I mark
+command, using single lower case letters
+.I x
+and the marked lines referred to as
+`\(aa\fIx\fR'.
+.NH 2
+Combining addressing primitives
+.PP
+Addresses to commands consist of a series of addressing primitives,
+separated by `,' or `;'.
+Such address lists are evaluated left-to-right.
+When addresses are separated by `;' the current line `\fB.\fR'
+is set to the value of the previous addressing expression
+before the next address is interpreted.
+If more addresses are given than the command requires,
+then all but the last one or two are ignored.
+If the command takes two addresses, the first addressed line must
+precede the second in the buffer.\(dg
+.FS
+\(dg Null address specifications are permitted in a list of addresses,
+the default in this case is the current line `.';
+thus `,100' is equivalent to `\fB.\fR,100'.
+It is an error to give a prefix address to a command which expects none.
+.FE
+.NH 1
+Command descriptions
+.PP
+The following form is a prototype for all
+.I ex
+commands:
+.DS
+\fIaddress\fR \fBcommand\fR \fI! parameters count flags\fR
+.DE
+All parts are optional; the degenerate case is the empty command which prints
+the next line in the file. For sanity with use from within
+.I visual
+mode,
+.I ex
+ignores a ``:'' preceding any command.
+.PP
+In the following command descriptions, the
+default addresses are shown in parentheses,
+which are
+.I not,
+however,
+part of the command.
+.LC
+\fBabbreviate\fR \fIword rhs\fP abbr: \fBab\fP
+.ZP
+Add the named abbreviation to the current list.
+When in input mode in visual, if
+.I word
+is typed as a complete word, it will be changed to
+.I rhs .
+.LC
+( \fB.\fR ) \fBappend\fR abbr: \fBa\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+Reads the input text and places it after the specified line.
+After the command, `\fB.\fR'
+addresses the last line input or the
+specified line if no lines were input.
+If address `0' is given,
+text is placed at the beginning of the buffer.
+.LC
+\fBa!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+The variant flag to
+.I append
+toggles the setting for the
+.I autoindent
+option during the input of
+.I text.
+.LC
+\fBargs\fR
+.ZP
+The members of the argument list are printed, with the current argument
+delimited by `[' and `]'.
+.ig
+.PP
+\fBcd\fR \fIdirectory\fR
+.ZP
+The
+.I cd
+command is a synonym for
+.I chdir.
+..
+.LC
+( \fB.\fP , \fB.\fP ) \fBchange\fP \fIcount\fP abbr: \fBc\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.ZP
+Replaces the specified lines with the input \fItext\fP.
+The current line becomes the last line input;
+if no lines were input it is left as for a
+\fIdelete\fP.
+.LC
+\fBc!\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.ZP
+The variant toggles
+.I autoindent
+during the
+.I change.
+.ig
+.LC
+\fBchdir\fR \fIdirectory\fR
+.ZP
+The specified \fIdirectory\fR becomes the current directory.
+If no directory is specified, the current value of the
+.I home
+option is used as the target directory.
+After a
+.I chdir
+the current file is not considered to have been
+edited so that write restrictions on pre-existing files apply.
+..
+.LC
+( \fB.\fP , \fB.\fP )\|\fBcopy\fP \fIaddr\fP \fIflags\fP abbr: \fBco\fP
+.ZP
+A
+.I copy
+of the specified lines is placed after
+.I addr,
+which may be `0'.
+The current line
+`\fB.\fR'
+addresses the last line of the copy.
+The command
+.I t
+is a synonym for
+.I copy.
+.LC
+( \fB.\fR , \fB.\fR )\|\fBdelete\fR \fIbuffer\fR \fIcount\fR \fIflags\fR abbr: \fBd\fR
+.ZP
+Removes the specified lines from the buffer.
+The line after the last line deleted becomes the current line;
+if the lines deleted were originally at the end,
+the new last line becomes the current line.
+If a named
+.I buffer
+is specified by giving a letter,
+then the specified lines are saved in that buffer,
+or appended to it if an upper case letter is used.
+.LC
+\fBedit\fR \fIfile\fR abbr: \fBe\fR
+.br
+\fBex\fR \fIfile\fR
+.ZP
+Used to begin an editing session on a new file.
+The editor
+first checks to see if the buffer has been modified since the last
+.I write
+command was issued.
+If it has been,
+a warning is issued and the
+command is aborted.
+The
+command otherwise deletes the entire contents of the editor buffer,
+makes the named file the current file and prints the new filename.
+After insuring that this file is sensible\(dg
+.FS
+\(dg I.e., that it is not a binary file such as a directory,
+a block or character special file other than
+.I /dev/tty,
+a terminal,
+or a binary or executable file
+(as indicated by the first word).
+.FE
+the editor reads the file into its buffer.
+.IP
+If the read of the file completes without error,
+the number of lines and characters read is typed.
+If there were any non-\s-2ASCII\s0 characters
+in the file they are stripped of their non-\s-2ASCII\s0
+high bits,
+and any null characters in the file are discarded.
+If none of these errors occurred, the file is considered
+.I edited.
+If the last line of the input file is missing the trailing
+newline character, it will be supplied and a complaint will be issued.
+This command leaves the current line `\fB.\fR' at the last line read.\(dd
+.FS
+\(dd If executed from within
+.I open
+or
+.I visual,
+the current line is initially the first line of the file.
+.FE
+.LC
+\fBe!\fR \fIfile\fR
+.ZP
+The variant form suppresses the complaint about modifications having
+been made and not written from the editor buffer, thus
+discarding all changes which have been made before editing the new file.
+.LC
+\fBe\fR \fB+\fIn\fR \fIfile\fR
+.ZP
+Causes the editor to begin at line
+.I n
+rather than at the last line;
+\fIn\fR may also be an editor command containing no spaces, e.g.: ``+/pat''.
+.LC
+\fBfile\fR abbr: \fBf\fR
+.ZP
+Prints the current file name,
+whether it has been `[Modified]' since the last
+.I write
+command,
+whether it is
+.I "read only" ,
+the current line,
+the number of lines in the buffer,
+and the percentage of the way through the buffer of the current line.*
+.FS
+* In the rare case that the current file is `[Not edited]' this is
+noted also; in this case you have to use the form \fBw!\fR to write to
+the file, since the editor is not sure that a \fBwrite\fR will not
+destroy a file unrelated to the current contents of the buffer.
+.FE
+.LC
+\fBfile\fR \fIfile\fR
+.ZP
+The current file name is changed to
+.I file
+which is considered
+`[Not edited]'.
+.LC
+( 1 , $ ) \fBglobal\fR /\fIpat\|\fR/ \fIcmds\fR abbr: \fBg\fR
+.ZP
+First marks each line among those specified which matches
+the given regular expression.
+Then the given command list is executed with `\fB.\fR' initially
+set to each marked line.
+.IP
+The command list consists of the remaining commands on the current
+input line and may continue to multiple lines by ending all but the
+last such line with a `\e'.
+If
+.I cmds
+(and possibly the trailing \fB/\fR delimiter) is omitted, each line matching
+.I pat
+is printed.
+.I Append,
+.I insert,
+and
+.I change
+commands and associated input are permitted;
+the `\fB.\fR' terminating input may be omitted if it would be on the
+last line of the command list.
+.I Open
+and
+.I visual
+commands are permitted in the command list and take input from the terminal.
+.IP
+The
+.I global
+command itself may not appear in
+.I cmds.
+The
+.I undo
+command is also not permitted there,
+as
+.I undo
+instead can be used to reverse the entire
+.I global
+command.
+The options
+.I autoprint
+and
+.I autoindent
+are inhibited during a
+.I global,
+(and possibly the trailing \fB/\fR delimiter) and the value of the
+.I report
+option is temporarily infinite,
+in deference to a \fIreport\fR for the entire global.
+Finally, the context mark `\'\'' is set to the value of
+`.' before the global command begins and is not changed during a global
+command,
+except perhaps by an
+.I open
+or
+.I visual
+within the
+.I global.
+.LC
+\fBg!\fR \fB/\fIpat\fB/\fR \fIcmds\fR abbr: \fBv\fR
+.IP
+The variant form of \fIglobal\fR runs \fIcmds\fR at each line not matching
+\fIpat\fR.
+.LC
+( \fB.\fR )\|\fBinsert\fR abbr: \fBi\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+Places the given text before the specified line.
+The current line is left at the last line input;
+if there were none input it is left at the line before the addressed line.
+This command differs from
+.I append
+only in the placement of text.
+.KS
+.LC
+\fBi!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+The variant toggles
+.I autoindent
+during the
+.I insert.
+.KE
+.LC
+( \fB.\fR , \fB.\fR+1 ) \fBjoin\fR \fIcount\fR \fIflags\fR abbr: \fBj\fR
+.ZP
+Places the text from a specified range of lines
+together on one line.
+White space is adjusted at each junction to provide at least
+one blank character, two if there was a `\fB.\fR' at the end of the line,
+or none if the first following character is a `)'.
+If there is already white space at the end of the line,
+then the white space at the start of the next line will be discarded.
+.LC
+\fBj!\fR
+.ZP
+The variant causes a simpler
+.I join
+with no white space processing; the characters in the lines are simply
+concatenated.
+.LC
+( \fB.\fR ) \fBk\fR \fIx\fR
+.ZP
+The
+.I k
+command is a synonym for
+.I mark.
+It does not require a blank or tab before the following letter.
+.LC
+( \fB.\fR , \fB.\fR ) \fBlist\fR \fIcount\fR \fIflags\fR
+.ZP
+Prints the specified lines in a more unambiguous way:
+tabs are printed as `^I'
+and the end of each line is marked with a trailing `$'.
+The current line is left at the last line printed.
+.LC
+\fBmap\fR \fIlhs\fR \fIrhs\fR
+.ZP
+The
+.I map
+command is used to define macros for use in
+.I visual
+mode.
+.I Lhs
+should be a single character, or the sequence ``#n'', for n a digit,
+referring to function key \fIn\fR. When this character or function key
+is typed in
+.I visual
+mode, it will be as though the corresponding \fIrhs\fR had been typed.
+On terminals without function keys, you can type ``#n''.
+See section 6.9 of the ``Introduction to Display Editing with Vi''
+for more details.
+.LC
+( \fB.\fR ) \fBmark\fR \fIx\fR
+.ZP
+Gives the specified line mark
+.I x,
+a single lower case letter.
+The
+.I x
+must be preceded by a blank or a tab.
+The addressing form `\'x' then addresses this line.
+The current line is not affected by this command.
+.LC
+( \fB.\fR , \fB.\fR ) \fBmove\fR \fIaddr\fR abbr: \fBm\fR
+.ZP
+The
+.I move
+command repositions the specified lines to be after
+.I addr .
+The first of the moved lines becomes the current line.
+.LC
+\fBnext\fR abbr: \fBn\fR
+.ZP
+The next file from the command line argument list is edited.
+.LC
+\fBn!\fR
+.ZP
+The variant suppresses warnings about the modifications to the buffer not
+having been written out, discarding (irretrievably) any changes which may
+have been made.
+.LC
+\fBn\fR \fIfilelist\fR
+.br
+\fBn\fR \fB+\fIcommand\fR \fIfilelist\fR
+.ZP
+The specified
+.I filelist
+is expanded and the resulting list replaces the
+current argument list;
+the first file in the new list is then edited.
+If
+.I command
+is given (it must contain no spaces), then it is executed after editing the first such file.
+.LC
+( \fB.\fR , \fB.\fR ) \fBnumber\fR \fIcount\fR \fIflags\fR abbr: \fB#\fR or \fBnu\fR
+.ZP
+Prints each specified line preceded by its buffer line
+number.
+The current line is left at the last line printed.
+.KS
+.LC
+( \fB.\fR ) \fBopen\fR \fIflags\fR abbr: \fBo\fR
+.br
+( \fB.\fR ) \fBopen\fR /\fIpat\|\fR/ \fIflags\fR
+.ZP
+Enters intraline editing \fIopen\fR mode at each addressed line.
+If
+.I pat
+is given,
+then the cursor will be placed initially at the beginning of the
+string matched by the pattern.
+To exit this mode use Q.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.KE
+.LC
+\fBpreserve\fR
+.ZP
+The current editor buffer is saved as though the system had just crashed.
+This command is for use only in emergencies when a
+.I write
+command has resulted in an error and you don't know how to save your work.
+After a
+.I preserve
+you should seek help.
+.LC
+( \fB.\fR , \fB.\fR )\|\fBprint\fR \fIcount\fR abbr: \fBp\fR or \fBP\fR
+.ZP
+Prints the specified lines
+with non-printing characters printed as control characters `^\fIx\fR\|';
+delete (octal 177) is represented as `^?'.
+The current line is left at the last line printed.
+.LC
+( \fB.\fR )\|\fBput\fR \fIbuffer\fR abbr: \fBpu\fR
+.ZP
+Puts back
+previously
+.I deleted
+or
+.I yanked
+lines.
+Normally used with
+.I delete
+to effect movement of lines,
+or with
+.I yank
+to effect duplication of lines.
+If no
+.I buffer
+is specified, then the last
+.I deleted
+or
+.I yanked
+text is restored.*
+.FS
+* But no modifying commands may intervene between the
+.I delete
+or
+.I yank
+and the
+.I put,
+nor may lines be moved between files without using a named buffer.
+.FE
+By using a named buffer, text may be restored that was saved there at any
+previous time.
+.LC
+\fBquit\fR abbr: \fBq\fR
+.ZP
+Causes
+.I ex
+to terminate.
+No automatic write of the editor buffer to a file is performed.
+However,
+.I ex
+issues a warning message if the file has changed
+since the last
+.I write
+command was issued, and does not
+.I quit.\(dg
+.FS
+\(dg \fIEx\fR
+will also issue a diagnostic if there are more files in the argument
+list.
+.FE
+Normally, you will wish to save your changes, and you
+should give a \fIwrite\fR command;
+if you wish to discard them, use the \fBq!\fR command variant.
+.LC
+\fBq!\fR
+.ZP
+Quits from the editor, discarding changes to the buffer without complaint.
+.LC
+( \fB.\fR ) \fBread\fR \fIfile\fR abbr: \fBr\fR
+.ZP
+Places a copy of the text of the given file in the
+editing buffer after the specified line.
+If no
+.I file
+is given the current file name is used.
+The current file name is not changed unless there is none in which
+case
+.I file
+becomes the current name.
+The sensibility restrictions for the
+.I edit
+command apply here also.
+If the file buffer is empty and there is no current name then
+.I ex
+treats this as an
+.I edit
+command.
+.IP
+Address `0' is legal for this command and causes the file to be read at
+the beginning of the buffer.
+Statistics are given as for the
+.I edit
+command when the
+.I read
+successfully terminates.
+After a
+.I read
+the current line is the last line read.\(dd
+.FS
+\(dd Within
+.I open
+and
+.I visual
+the current line is set to the first line read rather than the last.
+.FE
+.LC
+( \fB.\fR ) \fBread\fR \fB!\fR\fIcommand\fR
+.ZP
+Reads the output of the command
+.I command
+into the buffer after the specified line.
+This is not a variant form of the command, rather a read
+specifying a
+.I command
+rather than a
+.I filename;
+a blank or tab before the \fB!\fR is mandatory.
+.LC
+\fBrecover \fIfile\fR
+.ZP
+Recovers
+.I file
+from the system save area.
+Used after a accidental hangup of the phone**
+.FS
+** The system saves a copy of the file you were editing only if you
+have made changes to the file.
+.FE
+or a system crash** or
+.I preserve
+command.
+Except when you use
+.I preserve
+you will be notified by mail when a file is saved.
+.LC
+\fBrewind\fR abbr: \fBrew\fR
+.ZP
+The argument list is rewound, and the first file in the list is edited.
+.LC
+\fBrew!\fR
+.ZP
+Rewinds the argument list discarding any changes made to the current buffer.
+.LC
+\fBset\fR \fIparameter\fR
+.ZP
+With no arguments, prints those options whose values have been
+changed from their defaults;
+with parameter
+.I all
+it prints all of the option values.
+.IP
+Giving an option name followed by a `?'
+causes the current value of that option to be printed.
+The `?' is unnecessary unless the option is Boolean valued.
+Boolean options are given values either by the form
+`set \fIoption\fR' to turn them on or
+`set no\fIoption\fR' to turn them off;
+string and numeric options are assigned via the form
+`set \fIoption\fR=value'.
+.IP
+More than one parameter may be given to
+.I set \|;
+they are interpreted left-to-right.
+.LC
+\fBshell\fR abbr: \fBsh\fR
+.IP
+A new shell is created.
+When it terminates, editing resumes.
+.LC
+\fBsource\fR \fIfile\fR abbr: \fBso\fR
+.IP
+Reads and executes commands from the specified file.
+.I Source
+commands may be nested.
+.LC
+( \fB.\fR , \fB.\fR ) \fBsubstitute\fR /\fIpat\fR\|/\fIrepl\fR\|/ \fIoptions\fR \fIcount\fR \fIflags\fR abbr: \fBs\fR
+.IP
+On each specified line, the first instance of pattern
+.I pat
+is replaced by replacement pattern
+.I repl.
+If the
+.I global
+indicator option character `g'
+appears, then all instances are substituted;
+if the
+.I confirm
+indication character `c' appears,
+then before each substitution the line to be substituted
+is typed with the string to be substituted marked
+with `\(ua' characters.
+By typing an `y' one can cause the substitution to be performed,
+any other input causes no change to take place.
+After a
+.I substitute
+the current line is the last line substituted.
+.IP
+Lines may be split by substituting
+new-line characters into them.
+The newline in
+.I repl
+must be escaped by preceding it with a `\e'.
+Other metacharacters available in
+.I pat
+and
+.I repl
+are described below.
+.LC
+.B stop
+.ZP
+Suspends the editor, returning control to the top level shell.
+If
+.I autowrite
+is set and there are unsaved changes,
+a write is done first unless the form
+.B stop !
+is used.
+This commands is only available where supported by the teletype driver
+and operating system.
+.LC
+( \fB.\fR , \fB.\fR ) \fBsubstitute\fR \fIoptions\fR \fIcount\fR \fIflags\fR abbr: \fBs\fR
+.ZP
+If
+.I pat
+and
+.I repl
+are omitted, then the last substitution is repeated.
+This is a synonym for the
+.B &
+command.
+.LC
+( \fB.\fR , \fB.\fR ) \fBt\fR \fIaddr\fR \fIflags\fR
+.ZP
+The
+.I t
+command is a synonym for
+.I copy .
+.LC
+\fBta\fR \fItag\fR
+.ZP
+The focus of editing switches to the location of
+.I tag,
+switching to a different line in the current file where it is defined,
+or if necessary to another file.\(dd
+.FS
+\(dd If you have modified the current file before giving a
+.I tag
+command, you must write it out; giving another
+.I tag
+command, specifying no
+.I tag
+will reuse the previous tag.
+.FE
+.IP
+The tags file is normally created by a program such as
+.I ctags,
+and consists of a number of lines with three fields separated by blanks
+or tabs. The first field gives the name of the tag,
+the second the name of the file where the tag resides, and the third
+gives an addressing form which can be used by the editor to find the tag;
+this field is usually a contextual scan using `/\fIpat\fR/' to be immune
+to minor changes in the file. Such scans are always performed as if
+.I nomagic
+was set.
+.PP
+The tag names in the tags file must be sorted alphabetically.
+.LC
+\fBunabbreviate\fR \fIword\fP abbr: \fBuna\fP
+.ZP
+Delete
+.I word
+from the list of abbreviations.
+.LC
+\fBundo\fR abbr: \fBu\fR
+.ZP
+Reverses the changes made in the buffer by the last
+buffer editing command.
+Note that
+.I global
+commands are considered a single command for the purpose of
+.I undo
+(as are
+.I open
+and
+.I visual.)
+Also, the commands
+.I write
+and
+.I edit
+which interact with the
+file system cannot be undone.
+.I Undo
+is its own inverse.
+.IP
+.I Undo
+always marks the previous value of the current line `\fB.\fR'
+as `\'\''.
+After an
+.I undo
+the current line is the first line restored
+or the line before the first line deleted if no lines were restored.
+For commands with more global effect
+such as
+.I global
+and
+.I visual
+the current line regains it's pre-command value after an
+.I undo.
+.LC
+\fBunmap\fR \fIlhs\fR
+.ZP
+The macro expansion associated by
+.I map
+for
+.I lhs
+is removed.
+.LC
+( 1 , $ ) \fBv\fR /\fIpat\fR\|/ \fIcmds\fR
+.ZP
+A synonym for the
+.I global
+command variant \fBg!\fR, running the specified \fIcmds\fR on each
+line which does not match \fIpat\fR.
+.LC
+\fBversion\fR abbr: \fBve\fR
+.ZP
+Prints the current version number of the editor
+as well as the date the editor was last changed.
+.LC
+( \fB.\fR ) \fBvisual\fR \fItype\fR \fIcount\fR \fIflags\fR abbr: \fBvi\fR
+.ZP
+Enters visual mode at the specified line.
+.I Type
+is optional and may be `\-' , `\(ua' or `\fB.\fR'
+as in the
+.I z
+command to specify the placement of the specified line on the screen.
+By default, if
+.I type
+is omitted, the specified line is placed as the first on the screen.
+A
+.I count
+specifies an initial window size; the default is the value of the option
+.I window.
+See the document
+.I "An Introduction to Display Editing with Vi"
+for more details.
+To exit this mode, type Q.
+.LC
+\fBvisual\fP file
+.br
+\fBvisual\fP +\fIn\fP file
+.ZP
+From visual mode,
+this command is the same as edit.
+.LC
+( 1 , $ ) \fBwrite\fR \fIfile\fR abbr: \fBw\fR
+.ZP
+Writes changes made back to \fIfile\fR, printing the number of lines and
+characters written.
+Normally \fIfile\fR is omitted and the text goes back where it came from.
+If a \fIfile\fR is specified, then text will be written to that file.*
+.FS
+* The editor writes to a file only if it is
+the current file and is
+.I edited ,
+if the file does not exist,
+or if the file is actually a teletype,
+.I /dev/tty,
+.I /dev/null.
+Otherwise, you must give the variant form \fBw!\fR to force the write.
+.FE
+If the file does not exist it is created.
+The current file name is changed only if there is no current file
+name; the current line is never changed.
+.IP
+If an error occurs while writing the current and
+.I edited
+file, the editor
+considers that there has been ``No write since last change''
+even if the buffer had not previously been modified.
+.LC
+( 1 , $ ) \fBwrite>>\fR \fIfile\fR abbr: \fBw>>\fR
+.ZP
+Writes the buffer contents at the end of
+an existing file.
+.IP
+.LC
+\fBw!\fR \fIname\fR
+.ZP
+Overrides the checking of the normal \fIwrite\fR command,
+and will write to any file which the system permits.
+.LC
+( 1 , $ ) \fBw\fR \fB!\fR\fIcommand\fR
+.ZP
+Writes the specified lines into
+.I command.
+Note the difference between \fBw!\fR which overrides checks and
+\fBw\ \ !\fR which writes to a command.
+.LC
+\fBwq\fR \fIname\fR
+.ZP
+Like a \fIwrite\fR and then a \fIquit\fR command.
+.LC
+\fBwq!\fR \fIname\fR
+.ZP
+The variant overrides checking on the sensibility of the
+.I write
+command, as \fBw!\fR does.
+.LC
+\fBxit\fP \fIname\fR
+.ZP
+If any changes have been made and not written, writes the buffer out.
+Then, in any case, quits.
+.LC
+( \fB.\fR , \fB.\fR )\|\fByank\fR \fIbuffer\fR \fIcount\fR abbr: \fBya\fR
+.ZP
+Places the specified lines in the named
+.I buffer,
+for later retrieval via
+.I put.
+If no buffer name is specified, the lines go to a more volatile place;
+see the \fIput\fR command description.
+.LC
+( \fB.+1\fR ) \fBz\fR \fIcount\fR
+.ZP
+Print the next \fIcount\fR lines, default \fIwindow\fR.
+.LC
+( \fB.\fR ) \fBz\fR \fItype\fR \fIcount\fR
+.ZP
+Prints a window of text with the specified line at the top.
+If \fItype\fR is `\-' the line is placed at the bottom; a `\fB.\fR' causes
+the line to be placed in the center.*
+A count gives the number of lines to be displayed rather than
+double the number specified by the \fIscroll\fR option.
+On a \s-2CRT\s0 the screen is cleared before display begins unless a
+count which is less than the screen size is given.
+The current line is left at the last line printed.
+.FS
+* Forms `z=' and `z\(ua' also exist; `z=' places the current line in the
+center, surrounds it with lines of `\-' characters and leaves the current
+line at this line. The form `z\(ua' prints the window before `z\-'
+would. The characters `+', `\(ua' and `\-' may be repeated for cumulative
+effect.
+On some v2 editors, no
+.I type
+may be given.
+.FE
+.LC
+\fB!\fR \fIcommand\fR\fR
+.ZP
+The remainder of the line after the `!' character is sent to a shell
+to be executed.
+Within the text of
+.I command
+the characters
+`%' and `#' are expanded as in filenames and the character
+`!' is replaced with the text of the previous command.
+Thus, in particular,
+`!!' repeats the last such shell escape.
+If any such expansion is performed, the expanded line will be echoed.
+The current line is unchanged by this command.
+.IP
+If there has been ``[No\ write]'' of the buffer contents since the last
+change to the editing buffer, then a diagnostic will be printed
+before the command is executed as a warning.
+A single `!' is printed when the command completes.
+.LC
+( \fIaddr\fR , \fIaddr\fR ) \fB!\fR \fIcommand\fR\fR
+.ZP
+Takes the specified address range and supplies it as
+standard input to
+.I command;
+the resulting output then replaces the input lines.
+.LC
+( $ ) \fB=\fR
+.ZP
+Prints the line number of the
+addressed line.
+The current line is unchanged.
+.KS
+.LC
+( \fB.\fR , \fB.\fR ) \fB>\fR \fIcount\fR \fIflags\fR
+.br
+( \fB.\fR , \fB.\fR ) \fB<\fR \fIcount\fR \fIflags\fR
+.IP
+Perform intelligent shifting on the specified lines;
+\fB<\fR shifts left and \fB>\fR shift right.
+The quantity of shift is determined by the
+.I shiftwidth
+option and the repetition of the specification character.
+Only white space (blanks and tabs) is shifted;
+no non-white characters are discarded in a left-shift.
+The current line becomes the last line which changed due to the
+shifting.
+.KE
+.LC
+\fB^D\fR
+.ZP
+An end-of-file from a terminal input scrolls through the file.
+The
+.I scroll
+option specifies the size of the scroll, normally a half screen of text.
+.LC
+( \fB.\fR+1 , \fB.\fR+1 )
+.br
+( \fB.\fR+1 , \fB.\fR+1 ) |
+.ZP
+An address alone causes the addressed lines to be printed.
+A blank line prints the next line in the file.
+.LC
+( \fB.\fR , \fB.\fR ) \fB&\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+.ZP
+Repeats the previous
+.I substitute
+command.
+.LC
+( \fB.\fR , \fB.\fR ) \fB\s+2~\s0\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+.ZP
+Replaces the previous regular expression with the previous
+replacement pattern from a substitution.
+.NH 1
+Regular expressions and substitute replacement patterns
+.NH 2
+Regular expressions
+.PP
+A regular expression specifies a set of strings of characters.
+A member of this set of strings is said to be
+.I matched
+by the regular expression.
+.I Ex
+remembers two previous regular expressions:
+the previous regular expression used in a
+.I substitute
+command
+and the previous regular expression used elsewhere
+(referred to as the previous \fIscanning\fR regular expression.)
+The previous regular expression
+can always be referred to by a null \fIre\fR, e.g. `//' or `??'.
+.NH 2
+Magic and nomagic
+.PP
+The regular expressions allowed by
+.I ex
+are constructed in one of two ways depending on the setting of
+the
+.I magic
+option.
+The
+.I ex
+and
+.I vi
+default setting of
+.I magic
+gives quick access to a powerful set of regular expression
+metacharacters.
+The disadvantage of
+.I magic
+is that the user must remember that these metacharacters are
+.I magic
+and precede them with the character `\e'
+to use them as ``ordinary'' characters.
+With
+.I nomagic,
+the default for
+.I edit,
+regular expressions are much simpler,
+there being only two metacharacters.
+The power of the other metacharacters is still available by preceding
+the (now) ordinary character with a `\e'.
+Note that `\e' is thus always a metacharacter.
+.PP
+The remainder of the discussion of regular expressions assumes
+that
+that the setting of this option is
+.I magic.\(dg
+.FS
+\(dg To discern what is true with
+.I nomagic
+it suffices to remember that the only
+special characters in this case will be `\(ua' at the beginning
+of a regular expression,
+`$' at the end of a regular expression,
+and `\e'.
+With
+.I nomagic
+the characters `\s+2~\s0' and `&' also lose their special meanings
+related to the replacement pattern of a substitute.
+.FE
+.NH 2
+Basic regular expression summary
+.PP
+The following basic constructs are used to construct
+.I magic
+mode regular expressions.
+.IP \fIchar\fR 15
+An ordinary character matches itself.
+The characters `\(ua' at the beginning of a line,
+`$' at the end of line,
+`*' as any character other than the first,
+`.', `\e', `[', and `\s+2~\s0' are not ordinary characters and
+must be escaped (preceded) by `\e' to be treated as such.
+.IP \fB\(ua\fR
+At the beginning of a pattern
+forces the match to succeed only at the beginning of a line.
+.IP \fB$\fR
+At the end of a regular expression forces the match to
+succeed only at the end of the line.
+.IP \&\fB.\fR
+Matches any single character except
+the new-line character.
+.IP \fB\e<\fR
+Forces the match
+to occur only at the beginning of a ``variable'' or ``word'';
+that is, either at the beginning of a line, or just before
+a letter, digit, or underline and after a character not one of
+these.
+.IP \fB\e>\fR
+Similar to `\e<', but matching the end of a ``variable''
+or ``word'', i.e. either the end of the line or before character
+which is neither a letter, nor a digit, nor the underline character.
+.IP \fB[\fIstring\fR]\fR
+Matches any (single) character in the class defined by
+.I string.
+Most characters in
+.I string
+define themselves.
+A pair of characters separated by `\-' in
+.I string
+defines the set of characters collating between the specified lower and upper
+bounds, thus `[a\-z]' as a regular expression matches
+any (single) lower-case letter.
+If the first character of
+.I string
+is an `\(ua' then the construct
+matches those characters which it otherwise would not;
+thus `[\(uaa\-z]' matches anything but a lower-case letter (and of course a
+newline).
+To place any of the characters
+`\(ua', `[', or `\-' in
+.I string
+you must escape them with a preceding `\e'.
+.NH 2
+Combining regular expression primitives
+.PP
+The concatenation of two regular expressions matches the leftmost and
+then longest string
+which can be divided with the first piece matching the first regular
+expression and the second piece matching the second.
+Any of the (single character matching) regular expressions mentioned
+above may be followed by the character `*' to form a regular expression
+which matches any number of adjacent occurrences (including 0) of characters
+matched by the regular expression it follows.
+.PP
+The character `\s+2~\s0' may be used in a regular expression,
+and matches the text which defined the replacement part
+of the last
+.I substitute
+command.
+A regular expression may be enclosed between the sequences
+`\e(' and `\e)' with side effects in the
+.I substitute
+replacement patterns.
+.NH 2
+Substitute replacement patterns
+.PP
+The basic metacharacters for the replacement pattern are
+`&' and `~'; these are
+given as `\e&' and `\e~' when
+.I nomagic
+is set.
+Each instance of `&' is replaced by the characters
+which the regular expression matched.
+The metacharacter `~' stands, in the replacement pattern,
+for the defining text of the previous replacement pattern.
+.PP
+Other metasequences possible in the replacement pattern
+are always introduced by the escaping character `\e'.
+The sequence `\e\fIn\fR' is replaced by the text matched
+by the \fIn\fR-th regular subexpression enclosed between
+`\e(' and `\e)'.\(dg
+.FS
+\(dg When nested, parenthesized subexpressions are present,
+\fIn\fR is determined by counting occurrences of `\e(' starting from the left.
+.FE
+The sequences `\eu' and `\el' cause the immediately following character in
+the replacement to be converted to upper- or lower-case respectively
+if this character is a letter.
+The sequences `\eU' and `\eL' turn such conversion on, either until
+`\eE' or `\ee' is encountered, or until the end of the replacement pattern.
+.de LC
+.br
+.sp .1i
+.ne 4
+.LP
+.ta 3i
+..
+.NH 1
+Option descriptions
+.PP
+.LC
+\fBautoindent\fR, \fBai\fR default: noai
+.ZP
+Can be used to ease the preparation of structured program text.
+At the beginning of each
+.I append ,
+.I change
+or
+.I insert
+command
+or when a new line is
+.I opened
+or created by an
+.I append ,
+.I change ,
+.I insert ,
+or
+.I substitute
+operation within
+.I open
+or
+.I visual
+mode,
+.I ex
+looks at the line being appended after,
+the first line changed
+or the line inserted before and calculates the amount of white space
+at the start of the line.
+It then aligns the cursor at the level of indentation so determined.
+.IP
+If the user then types lines of text in,
+they will continue to be justified at the displayed indenting level.
+If more white space is typed at the beginning of a line,
+the following line will start aligned with the first non-white character
+of the previous line.
+To back the cursor up to the preceding tab stop one can hit
+\fB^D\fR.
+The tab stops going backwards are defined at multiples of the
+.I shiftwidth
+option.
+You
+.I cannot
+backspace over the indent,
+except by sending an end-of-file with a \fB^D\fR.
+.IP
+Specially processed in this mode is a line with no characters added
+to it, which turns into a completely blank line (the white
+space provided for the
+.I autoindent
+is discarded.)
+Also specially processed in this mode are lines beginning with
+an `\(ua' and immediately followed by a \fB^D\fR.
+This causes the input to be repositioned at the beginning of the line,
+but retaining the previous indent for the next line.
+Similarly, a `0' followed by a \fB^D\fR
+repositions at the beginning but without
+retaining the previous indent.
+.IP
+.I Autoindent
+doesn't happen in
+.I global
+commands or when the input is not a terminal.
+.LC
+\fBautoprint\fR, \fBap\fR default: ap
+.ZP
+Causes the current line to be printed after each
+.I delete ,
+.I copy ,
+.I join ,
+.I move ,
+.I substitute ,
+.I t ,
+.I undo
+or
+shift command.
+This has the same effect as supplying a trailing `p'
+to each such command.
+.I Autoprint
+is suppressed in globals,
+and only applies to the last of many commands on a line.
+.LC
+\fBautowrite\fR, \fBaw\fR default: noaw
+.ZP
+Causes the contents of the buffer to be written to the current file
+if you have modified it and give a
+.I next,
+.I rewind,
+.I stop,
+.I tag,
+or
+.I !
+command, or a \fB^\(ua\fR (switch files) or \fB^]\fR (tag goto) command
+in
+.I visual.
+Note, that the
+.I edit
+and
+.I ex
+commands do
+.B not
+autowrite.
+In each case, there is an equivalent way of switching when autowrite
+is set to avoid the
+.I autowrite
+(\fIedit\fR
+for
+.I next ,
+.I rewind!
+for .I rewind ,
+.I stop!
+for
+.I stop ,
+.I tag!
+for
+.I tag ,
+.I shell
+for
+.I ! ,
+and
+\fB:e\ #\fR and a \fB:ta!\fR command from within
+.I visual).
+.LC
+\fBbeautify\fR, \fBbf\fR default: nobeautify
+.ZP
+Causes all control characters except tab, newline and form-feed
+to be discarded from the input.
+A complaint is registered the first time a
+backspace character is discarded.
+.I Beautify
+does not apply to command input.
+.LC
+\fBdirectory\fR, \fBdir\fR default: dir=/tmp
+.ZP
+Specifies the directory in which
+.I ex
+places its buffer file.
+If this directory in not
+writable, then the editor will exit abruptly when it fails to be
+able to create its buffer there.
+.LC
+\fBedcompatible\fR default: noedcompatible
+.ZP
+Causes the presence of absence of
+.B g
+and
+.B c
+suffixes on substitute commands to be remembered, and to be toggled
+by repeating the suffices. The suffix
+.B r
+makes the substitution be as in the
+.I ~
+command, instead of like
+.I &.
+.LC
+\fBerrorbells\fR, \fBeb\fR default: noeb
+.ZP
+Error messages are preceded by a bell.*
+.FS
+* Bell ringing in
+.I open
+and
+.I visual
+on errors is not suppressed by setting
+.I noeb.
+.FE
+If possible the editor always places the error message in a standout mode of the
+terminal (such as inverse video) instead of ringing the bell.
+.LC
+\fBhardtabs\fR, \fBht\fR default: ht=8
+.ZP
+Gives the boundaries on which terminal hardware tabs are set (or
+on which the system expands tabs).
+.LC
+\fBignorecase\fR, \fBic\fR default: noic
+.ZP
+All upper case characters in the text are mapped to lower case in regular
+expression matching.
+In addition, all upper case characters in regular expressions are mapped
+to lower case except in character class specifications.
+.LC
+\fBlisp\fR default: nolisp
+.ZP
+\fIAutoindent\fR indents appropriately for
+.I lisp
+code, and the \fB( ) { } [[\fR and \fB]]\fR commands in
+.I open
+and
+.I visual
+are modified to have meaning for \fIlisp\fR.
+.LC
+\fBlist\fR default: nolist
+.ZP
+All printed lines will be displayed (more) unambiguously,
+showing tabs and end-of-lines as in the
+.I list
+command.
+.LC
+\fBmagic\fR default: magic for \fIex\fR and \fIvi\fR\(dg
+.FS
+\(dg \fINomagic\fR for \fIedit\fR.
+.FE
+.ZP
+If
+.I nomagic
+is set, the number of regular expression metacharacters is greatly reduced,
+with only `\(ua' and `$' having special effects.
+In addition the metacharacters
+`~'
+and
+`&'
+of the replacement pattern are treated as normal characters.
+All the normal metacharacters may be made
+.I magic
+when
+.I nomagic
+is set by preceding them with a `\e'.
+.LC
+\fBmesg\fR default: mesg
+.ZP
+Causes write permission to be turned off to the terminal
+while you are in visual mode, if
+.I nomesg
+is set.
+.LC
+\fBmodeline\fR default: nomodeline
+.ZP
+If
+.I modeline
+is set, then the first 5 lines and the last five lines of the file
+will be checked for ex command lines and the comands issued.
+To be recognized as a command line, the line must have the string
+.B ex:
+or
+.B vi:
+preceeded by a tab or a space. This string may be anywhere in the
+line and anything after the
+.I :
+is interpeted as editor commands. This option defaults to off because
+of unexpected behavior when editting files such as
+.I /etc/passwd.
+.LC
+\fBnumber, nu\fR default: nonumber
+.ZP
+Causes all output lines to be printed with their
+line numbers.
+In addition each input line will be prompted for by supplying the line number
+it will have.
+.LC
+\fBopen\fR default: open
+.ZP
+If \fInoopen\fR, the commands
+.I open
+and
+.I visual
+are not permitted.
+This is set for
+.I edit
+to prevent confusion resulting from accidental entry to
+open or visual mode.
+.LC
+\fBoptimize, opt\fR default: optimize
+.ZP
+Throughput of text is expedited by setting the terminal
+to not do automatic carriage returns
+when printing more than one (logical) line of output,
+greatly speeding output on terminals without addressable
+cursors when text with leading white space is printed.
+.LC
+\fBparagraphs,\ para\fR default: para=IPLPPPQPP\0LIbp
+.ZP
+Specifies the paragraphs for the \fB{\fR and \fB}\fR operations in
+.I open
+and
+.I visual.
+The pairs of characters in the option's value are the names
+of the macros which start paragraphs.
+.LC
+\fBprompt\fR default: prompt
+.ZP
+Command mode input is prompted for with a `:'.
+.LC
+\fBredraw\fR default: noredraw
+.ZP
+The editor simulates (using great amounts of output), an intelligent
+terminal on a dumb terminal (e.g. during insertions in
+.I visual
+the characters to the right of the cursor position are refreshed
+as each input character is typed.)
+Useful only at very high speed.
+.LC
+\fBremap\fP default: remap
+.ZP
+If on, macros are repeatedly tried until they are unchanged.
+For example, if
+.B o
+is mapped to
+.B O ,
+and
+.B O
+is mapped to
+.B I ,
+then if
+.I remap
+is set,
+.B o
+will map to
+.B I ,
+but if
+.I noremap
+is set, it will map to
+.B O .
+.LC
+\fBreport\fR default: report=5\(dg
+.FS
+\(dg 2 for \fIedit\fR.
+.FE
+.ZP
+Specifies a threshold for feedback from commands.
+Any command which modifies more than the specified number of lines
+will provide feedback as to the scope of its changes.
+For commands such as
+.I global ,
+.I open ,
+.I undo ,
+and
+.I visual
+which have potentially more far reaching scope,
+the net change in the number of lines in the buffer is
+presented at the end of the command, subject to this same threshold.
+Thus notification is suppressed during a
+.I global
+command on the individual commands performed.
+.LC
+\fBscroll\fR default: scroll=\(12 window
+.ZP
+Determines the number of logical lines scrolled when an end-of-file
+is received from a terminal input in command mode,
+and the number of lines printed by a command mode
+.I z
+command (double the value of
+.I scroll ).
+.LC
+\fBsections\fR default: sections=SHNHH\0HU
+.ZP
+Specifies the section macros for the \fB[[\fR and \fB]]\fR operations
+in
+.I open
+and
+.I visual.
+The pairs of characters in the options's value are the names
+of the macros which start paragraphs.
+.LC
+\fBshell\fR, \fBsh\fR default: sh=/bin/sh
+.ZP
+Gives the path name of the shell forked for
+the shell escape command `!', and by the
+.I shell
+command.
+The default is taken from SHELL in the environment, if present.
+.LC
+\fBshiftwidth\fR, \fBsw\fR default: sw=8
+.ZP
+Gives the width a software tab stop,
+used in reverse tabbing with \fB^D\fR when using
+.I autoindent
+to append text,
+and by the shift commands.
+.LC
+\fBshowmatch, sm\fR default: nosm
+.ZP
+In
+.I open
+and
+.I visual
+mode, when a \fB)\fR or \fB}\fR is typed, move the cursor to the matching
+\fB(\fR or \fB{\fR for one second if this matching character is on the
+screen. Extremely useful with
+.I lisp.
+.LC
+\fBslowopen, slow\fR terminal dependent
+.ZP
+Affects the display algorithm used in
+.I visual
+mode, holding off display updating during input of new text to improve
+throughput when the terminal in use is both slow and unintelligent.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.LC
+\fBtabstop,\ ts\fR default: ts=8
+.ZP
+The editor expands tabs in the input file to be on
+.I tabstop
+boundaries for the purposes of display.
+.LC
+\fBtaglength,\ tl\fR default: tl=0
+.ZP
+Tags are not significant beyond this many characters.
+A value of zero (the default) means that all characters are significant.
+.LC
+\fBtags\fR default: tags=tags /usr/lib/tags
+.ZP
+A path of files to be used as tag files for the
+.I tag
+command.
+A requested tag is searched for in the specified files, sequentially.
+By default, files called
+.B tags
+are searched for in the current directory and in /usr/lib
+(a master file for the entire system).
+.LC
+\fBterm\fR from environment TERM
+.ZP
+The terminal type of the output device.
+.LC
+\fBterse\fR default: noterse
+.ZP
+Shorter error diagnostics are produced for the experienced user.
+.LC
+\fBwarn\fR default: warn
+.ZP
+Warn if there has been `[No write since last change]' before a `!'
+command escape.
+.LC
+\fBwindow\fR default: window=speed dependent
+.ZP
+The number of lines in a text window in the
+.I visual
+command.
+The default is 8 at slow speeds (600 baud or less),
+16 at medium speed (1200 baud),
+and the full screen (minus one line) at higher speeds.
+.LC
+\fBw300,\ w1200\, w9600\fR
+.ZP
+These are not true options but set
+.B window
+only if the speed is slow (300), medium (1200), or high (9600),
+respectively.
+They are suitable for an EXINIT
+and make it easy to change the 8/16/full screen rule.
+.LC
+\fBwrapscan\fR, \fBws\fR default: ws
+.ZP
+Searches using the regular expressions in addressing
+will wrap around past the end of the file.
+.LC
+\fBwrapmargin\fR, \fBwm\fR default: wm=0
+.ZP
+Defines a margin for automatic wrapover of text during input in
+.I open
+and
+.I visual
+modes. See
+.I "An Introduction to Text Editing with Vi"
+for details.
+.LC
+\fBwriteany\fR, \fBwa\fR default: nowa
+.IP
+Inhibit the checks normally made before
+.I write
+commands, allowing a write to any file which the system protection
+mechanism will allow.
+.NH 1
+Acknowledgements
+.PP
+Chuck Haley contributed greatly to the early development of
+.I ex.
+Bruce Englar encouraged the redesign which led to
+.I ex
+version 1.
+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.
diff --git a/contrib/nvi/docs/USD.doc/exref/ex.summary b/contrib/nvi/docs/USD.doc/exref/ex.summary
new file mode 100644
index 000000000000..83084a368ed8
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/exref/ex.summary
@@ -0,0 +1,730 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ex.summary 8.3 (Berkeley) 8/18/96
+.\"
+.ds p \v'-0.2'.\v'+0.2'
+.ds U \s-2UNIX\s+2
+.ds c \v'-0.2':\v'+0.2'
+.nr LL 6.5i
+.lt 6.5i
+.ll 6.5i
+.ds CH
+.ds LF Computing Services, U.C. Berkeley
+.ds RF April 3, 1979
+.de SP
+.sp 1v
+..
+.nr PI 3n
+.nr PD 0
+.ND
+.ps 12
+.ft B
+.ce 1
+Ex/Edit Command Summary (Version 2.0)
+.sp 1
+.ft R
+.nr VS 11
+.nr PS 9
+.2C
+.PP
+.I Ex
+and
+.I edit
+are text editors, used for creating
+and modifying files of text on the \*U
+computer system.
+.I Edit
+is a variant of
+.I ex
+with features designed to
+make it less complicated
+to learn and use.
+In terms of command syntax and effect
+the editors are essentially identical,
+and this command summary applies to both.
+.PP
+The summary is meant as a quick reference
+for users already acquainted
+with
+.I edit
+or \fIex\fP.
+Fuller explanations of the editors are available
+in the documents
+.I
+Edit: A Tutorial
+.R
+(a self-teaching introduction) and the
+.I
+Ex Reference Manual
+.R
+(the comprehensive reference source for
+both \fIedit\fP and \fIex\fP).
+Both of these writeups are available in the
+Computing Services Library.
+.PP
+In the examples included with the
+summary, commands and text entered by
+the user are printed in \fBboldface\fR to
+distinguish them from responses printed
+by the computer.
+.sp 0.45v
+.LP
+.B
+The Editor Buffer
+.PP
+In order to perform its tasks
+the editor sets aside a temporary
+work space,
+called a \fIbuffer\fR,
+separate from the user's permanent
+file.
+Before starting to work on an existing
+file the editor makes a copy of it in the
+buffer, leaving the original untouched.
+All editing changes are made to the
+buffer copy, which must then
+be written back to the permanent
+file in order to update the
+old version.
+The buffer disappears
+at the end of the editing session.
+.sp 0.45v
+.LP
+.B
+Editing: Command and Text Input Modes
+.PP
+.R
+During an editing session there are
+two usual modes of operation:
+\fIcommand\fP mode and \fItext input\fP
+mode.
+(This disregards, for the moment,
+.I open
+and
+.I visual
+modes, discussed below.)
+In command mode, the editor issues a
+colon prompt (:)
+to show that it is ready to
+accept and execute a command.
+In text input mode, on the other hand, there is
+no prompt and the editor merely accepts text to
+be added to the buffer.
+Text input mode is initiated by the commands
+\fIappend\fP, \fIinsert\fP, and \fIchange\fP,
+and is terminated by typing a period as the
+first and only character on a line.
+.sp 0.45v
+.LP
+.B
+Line Numbers and Command Syntax
+.PP
+.R
+The editor keeps track of lines of text
+in the buffer by numbering them consecutively
+starting with 1 and renumbering
+as lines are added or deleted.
+At any given time the editor is positioned
+at one of these lines; this position is
+called the \fIcurrent line\fP.
+Generally, commands that change the
+contents of the buffer print the
+new current line at the end of their
+execution.
+.PP
+Most commands can be preceded by one or two
+line-number addresses which indicate the lines
+to be affected.
+If one number is given the command operates on
+that line only; if two, on an inclusive range
+of lines.
+Commands that can take line-number prefixes also
+assume default prefixes if none are given.
+The default assumed by each command is designed
+to make it convenient to use in many instances
+without any line-number prefix.
+For the most part, a command used without a
+prefix operates on the current line,
+though exceptions to this rule should be noted.
+The \fIprint\fP command
+by itself, for instance, causes
+one line, the current line, to be
+printed at the terminal.
+.PP
+The summary shows the number of line addresses
+that can be
+prefixed to each command as well as
+the defaults assumed if they are omitted.
+For example,
+.I (.,.)
+means that up to 2 line-numbers may be given,
+and that if none is given the
+command operates on the current line.
+(In the address prefix notation, ``.'' stands
+for the current line and ``$'' stands for
+the last line of the buffer.)
+If no such notation appears, no
+line-number prefix may be used.
+.PP
+Some commands take trailing
+information;
+only
+the more important instances of this
+are mentioned in the summary.
+.sp 0.25v
+.LP
+.B
+Open and Visual Modes
+.PP
+.R
+Besides command and text input modes,
+.I ex
+and
+.I edit
+provide on some CRT terminals other modes of editing,
+.I open
+and
+.I visual .
+In these modes the cursor can
+be moved to individual words
+or characters in a line.
+The commands then given are very different
+from the standard editor commands; most do not appear on the screen when
+typed.
+.I
+An Introduction to Display Editing with Vi
+.R
+provides a full discussion.
+.sp 0.25v
+.LP
+.B
+Special Characters
+.PP
+.R
+.fi
+Some characters take on special meanings
+when used in context searches
+and in patterns given to the \fIsubstitute\fP command.
+For \fIedit\fR, these are ``^'' and ``$'',
+meaning the beginning and end of a line,
+respectively.
+.I Ex
+has the following additional special characters:
+.B
+.ce 1
+\&. & * [ ] ~
+.R
+To use one of the special characters as its
+simple graphic representation
+rather than with its special meaning,
+precede it by a backslash (\\).
+The backslash always has a special meaning.
+.1C
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+(.)\fBappend a T{
+Begins text input mode,
+adding lines to the buffer after
+the line specified. Appending continues
+until ``.'' is typed alone at the
+beginning of a new line, followed by
+a carriage return. \fI0a\fR places
+lines at the beginning of the buffer.
+T} T{
+.nf
+\fR:\fBa
+Three lines of text
+are added to the buffer
+after the current line.
+\*p
+.R
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBchange c T{
+Deletes indicated line(s) and
+initiates text input mode to
+replace them with new text which follows.
+New text is terminated the same way
+as with \fIappend\fR.
+T} T{
+.nf
+:\fB5,6c
+Lines 5 and 6 are
+deleted and replaced by
+these three lines.
+\*p
+.R
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBcopy \fIaddr co T{
+Places a copy of the specified lines
+after the line indicated by \fIaddr\fR.
+The example places a copy of lines 8 through
+12, inclusive, after line 25.
+T} T{
+.nf
+\fR:\fB8,12co 25
+\fRLast line copied is printed
+\fR\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBdelete d T{
+Removes lines from the buffer
+and prints the current line after the deletion.
+T} T{
+.nf
+\fR:\fB13,15d
+\fRNew current line is printed
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+T{
+\fBedit \fIfile\fP
+.br
+\fBedit! \fIfile\fP
+T} T{
+e
+.br
+e!
+T} T{
+.fi
+\fRClears the editor buffer and then
+copies into it the named \fIfile\fR,
+which becomes the current file.
+This is a way of shifting to a different
+file
+without leaving the editor.
+The editor issues a warning
+message if this command is used before
+saving changes
+made to the file already in the buffer;
+using the form \fBe!\fR overrides this protective mechanism.
+T} T{
+.nf
+\fR:\fBe ch10\fR
+No write since last change
+:\fBe! ch10\fR
+"ch10" 3 lines, 62 characters
+\*c
+.fi
+T}
+.SP
+\fBfile \fIname\fR f T{
+\fRIf followed by a \fIname\fR, renames
+the current file to \fIname\fR.
+If used without \fIname\fR, prints
+the name of the current file.
+T} T{
+.nf
+\fR:\fBf ch9
+\fR"ch9" [Modified] 3 lines ...
+:\fBf
+\fR"ch9" [Modified] 3 lines ...
+\*c
+.fi
+T}
+.SP
+(1,$)\fBglobal g \fBglobal/\fIpattern\fB/\fIcommands T{
+.nf
+:\fBg/nonsense/d
+\fR\*c
+.fi
+T}
+\fR(1,$)\fBglobal! g!\fR or \fBv T{
+Searches the entire buffer (unless a smaller
+range is specified by line-number prefixes) and
+executes \fIcommands\fR on every line with
+an expression matching \fIpattern\fR.
+The second form, abbreviated
+either \fBg!\fR or \fBv\fR,
+executes \fIcommands\fR on lines that \fIdo
+not\fR contain the expression \fIpattern\fR.
+T} \^
+.SP
+\fR(.)\fBinsert i T{
+Inserts new lines of text immediately before the specified line.
+Differs from
+.I append
+only in that text is placed before, rather than after, the indicated line.
+In other words, \fB1i\fR has the same effect as \fB0a\fR.
+T} T{
+.nf
+:\fB1i
+These lines of text will
+be added prior to line 1.
+\&.
+\fR:
+.fi
+T}
+.SP
+\fR(.,.+1)\fBjoin j T{
+Join lines together, adjusting white space (spaces
+and tabs) as necessary.
+T} T{
+.nf
+:\fB2,5j\fR
+Resulting line is printed
+:
+.fi
+T}
+.TE
+.bp
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+\fR(.,.)\fBlist l T{
+\fRPrints lines in a more
+unambiguous way than the \fIprint\fR
+command does. The end of a line,
+for example, is marked with a ``$'',
+and tabs printed as ``^I''.
+T} T{
+.nf
+:\fB9l
+\fRThis is line 9$
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fR(.,.)\fBmove \fIaddr\fB m T{
+\fRMoves the specified lines
+to a position after the line
+indicated by \fIaddr\fR.
+T} T{
+.nf
+\fR:\fB12,15m 25\fR
+New current line is printed
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBnumber nu T{
+Prints each line preceded
+by its buffer line number.
+T} T{
+.nf
+\fR:\fBnu
+\0\0\fR10\0 This is line 10
+\*c
+.fi
+T}
+.SP
+\fR(.)\fBopen o T{
+Too involved to discuss here,
+but if you enter open mode
+accidentally, press
+the \s-2ESC\s0 key followed by
+\fBq\fR to
+get back into normal editor
+command mode.
+\fIEdit\fP is designed to
+prevent accidental use of
+the open command.
+T}
+.SP
+\fBpreserve pre T{
+Saves a copy of the current buffer contents as though the system had
+just crashed. This is for use in an emergency when a
+.I write
+command has failed and you don't know how else to save your work.\(dg
+T} T{
+.nf
+:\fBpreserve\fR
+File preserved.
+:
+.fi
+T}
+.SP
+\fR(.,.)\fBprint p Prints the text of line(s). T{
+.nf
+:\fB+2,+3p\fR
+The second and third lines
+after the current line
+:
+.fi
+T}
+.TE
+.FS
+.ll 6.5i
+\(dg You should seek assistance from a system administrator as soon as
+possible after saving a file with the
+.I preserve
+command, because the preserved copy of the file is saved in a
+directory used to store temporary files, and thus, the preserved
+copy may only be available for a short period of time.
+.FE
+.SP
+.nf
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+T{
+.nf
+\fBquit
+quit!
+.fi
+T} T{
+.nf
+q
+q!
+T} T{
+.fi
+\fREnds the editing session.
+You will receive a
+warning if you have changed the buffer
+since last writing its contents
+to the file. In this event you
+must either type \fBw\fR to write,
+or type \fBq!\fR to exit from
+the editor without saving your changes.
+T} T{
+.nf
+\fR:\fBq
+\fRNo write since last change
+:\fBq!
+\fR%
+.fi
+T}
+.SP
+\fR(.)\fBread \fIfile\fP r T{
+.fi
+\fRPlaces a copy of \fIfile\fR in the
+buffer after the specified line.
+Address 0 is permissible and causes
+the copy of \fIfile\fR to be placed
+at the beginning of the buffer.
+The \fIread\fP command does not
+erase any text already in the buffer.
+If no line number is specified,
+\fIfile\fR is placed after the
+current line.
+T} T{
+.nf
+\fR:\fB0r newfile
+\fR"newfile" 5 lines, 86 characters
+\*c
+.fi
+T}
+.SP
+\fBrecover \fIfile\fP rec T{
+.fi
+Retrieves a copy of the editor buffer
+after a system crash, editor crash,
+phone line disconnection, or
+\fIpreserve\fR command.
+T}
+.SP
+\fR(.,.)\fBsubstitute s T{
+.nf
+\fBsubstitute/\fIpattern\fB/\fIreplacement\fB/
+substitute/\fIpattern\fB/\fIreplacement\fB/gc
+.fi
+\fRReplaces the first occurrence of \fIpattern\fR
+on a line
+with \fIreplacement\fP.
+Including a \fBg\fR after the command
+changes all occurrences of \fIpattern\fP
+on the line.
+The \fBc\fR option allows the user to
+confirm each substitution before it is
+made; see the manual for details.
+T} T{
+.nf
+:\fB3p
+\fRLine 3 contains a misstake
+:\fBs/misstake/mistake/
+\fRLine 3 contains a mistake
+\*c
+.fi
+T}
+.TE
+.bp
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+\fBundo u T{
+.fi
+\fRReverses the changes made in
+the buffer by the last buffer-editing
+command.
+Note that this example contains
+a notification about the number of
+lines affected.
+T} T{
+.nf
+\fR:\fB1,15d
+\fR15 lines deleted
+new line number 1 is printed
+:\fBu
+\fR15 more lines in file ...
+old line number 1 is printed
+\*c
+.fi
+T}
+.SP
+\fR(1,$)\fBwrite \fIfile\fR w T{
+.fi
+\fRCopies data from the buffer onto
+a permanent file. If no \fIfile\fR
+is named, the current filename
+is used.
+The file is automatically created
+if it does not yet exist.
+A response containing the number of
+lines and characters in the file
+indicates that the write
+has been completed successfully.
+The editor's built-in protections
+against overwriting existing files
+will in some circumstances
+inhibit a write.
+The form \fBw!\fR forces the
+write, confirming that
+an existing file is to be overwritten.
+T} T{
+.nf
+\fR:\fBw
+\fR"file7" 64 lines, 1122 characters
+:\fBw file8
+\fR"file8" File exists ...
+:\fBw! file8
+\fR"file8" 64 lines, 1122 characters
+\*c
+.fi
+T}
+\fR(1,$)\fBwrite! \fIfile\fP w! \^ \^
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fR(.)\fBz \fIcount\fP z T{
+.fi
+\fRPrints a screen full of text starting
+with the line indicated;
+or, if \fIcount\fR is specified,
+prints that number of lines.
+Variants of the \fIz\fR command
+are described in the manual.
+T}
+.SP
+\fB!\fIcommand T{
+.fi
+Executes the remainder of the line
+after \fB!\fR as a \*U command.
+The buffer is unchanged by this, and
+control is returned to the editor when
+the execution of \fIcommand\fR is complete.
+T} T{
+.nf
+\fR:\fB!date
+\fRFri Jun 9 12:15:11 PDT 1978
+!
+\*c
+.fi
+T}
+.SP
+\fRcontrol-d T{
+.fi
+Prints the next \fIscroll\fR of text,
+normally half of a screen. See the
+manual for details of the \fIscroll\fR
+option.
+T}
+.SP
+\fR(.+1)<cr> T{
+.fi
+An address alone followed by a carriage
+return causes the line to be printed.
+A carriage return by itself prints the
+line following the current line.
+T} T{
+.nf
+:\fR<cr>
+the line after the current line
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fB/\fIpattern\fB/ T{
+.fi
+\fRSearches for the next line in which
+\fIpattern\fR occurs and prints it.
+T} T{
+.nf
+\fR:\fB/This pattern/
+\fRThis pattern next occurs here.
+\*c
+.fi
+T}
+.SP
+\fB// T{
+Repeats the most recent search.
+T} T{
+.nf
+\fR:\fB//
+\fRThis pattern also occurs here.
+\*c
+.fi
+T}
+.SP
+\fB?\fIpattern\fB? T{
+Searches in the reverse direction
+for \fIpattern\fP.
+T}
+.SP
+\fB?? T{
+Repeats the most recent search,
+moving in the reverse direction
+through the buffer.
+T}
+.TE
diff --git a/contrib/nvi/docs/USD.doc/vi.man/Makefile b/contrib/nvi/docs/USD.doc/vi.man/Makefile
new file mode 100644
index 000000000000..54338646791f
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.man/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.7 (Berkeley) 8/18/96
+
+ROFF= groff
+
+all: vi.0 vi.0.ps
+
+vi.0: vi.1
+ ${ROFF} -man -Tascii < vi.1 > $@
+ chmod 444 $@
+
+vi.0.ps: vi.1
+ ${ROFF} -man < vi.1 > $@
+ chmod 444 $@
+
+clean:
+ rm -f vi.0 vi.0.ps
diff --git a/contrib/nvi/docs/USD.doc/vi.man/spell.ok b/contrib/nvi/docs/USD.doc/vi.man/spell.ok
new file mode 100644
index 000000000000..80ebcaba829d
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.man/spell.ok
@@ -0,0 +1,179 @@
+Ar
+Bostic
+CDPATH
+COLUMNSXX
+Cscope
+Ds
+EXINIT
+Ee
+Ev
+Fa
+Ff
+Fl
+HUnhsh
+IPLPPPQPP
+LIpplpipbp
+Li
+Lite
+NEXINIT
+NHSHH
+Nex
+Nn
+POSIX
+Pp
+QQ
+SIGWINCHXX
+Std
+Sy
+TMPDIR
+Tt
+USD
+Unmap
+VI
+Vi
+XXXX
+ZZ
+ags
+ai
+altwerase
+ap
+autoindent
+autoprint
+autowrite
+aw
+bf
+bigwords
+cd
+cdpath
+cedit
+cmd
+co
+creens
+cs
+ctags
+db
+dbopen
+dd
+di
+dir
+dit
+doc
+docs
+eFRrsv
+eFRrv
+eFlRrv
+ead
+eb
+edcompatible
+egrep
+elete
+errorbells
+esc
+exrc
+exu
+fg
+filec
+hange
+hardtabs
+ht
+ic
+iclower
+ignorecase
+ile
+ind
+ious
+ist
+ize
+keytime
+leftright
+lhs
+li
+libc
+lobal
+lp
+matchtime
+mber
+mesg
+mk
+modeful
+modeline
+modelines
+nex
+nexrc
+nk
+nonblank
+nooption
+noprint
+nsert
+nul
+nvi
+oin
+onnections
+ove
+ppend
+prev
+pu
+readonly
+rec
+recdir
+redist
+rhs
+rint
+rk
+ro
+rsion
+sccs
+scr
+se
+searchincr
+sh
+shareware
+shellmeta
+shiftwidth
+showmatch
+showmode
+sidescroll
+slowopen
+sm
+smd
+sourceany
+su
+sual
+sw
+ta
+tabstop
+taglength
+tagn
+tagp
+tagstring
+th's
+tildeop
+tl
+tmp
+tr
+ts
+ttytype
+ttywerase
+ubstitute
+uffers
+uit
+unm
+urce
+var
+ve
+vi
+viu
+wa
+wi
+windowname
+wl
+wm
+wn
+wq
+wraplen
+wrapmargin
+wrapscan
+writeany
+ws
+ya
+yy
diff --git a/contrib/nvi/docs/USD.doc/vi.man/vi.1 b/contrib/nvi/docs/USD.doc/vi.man/vi.1
new file mode 100644
index 000000000000..22aee3e7e952
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.man/vi.1
@@ -0,0 +1,1608 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" This document may not be republished without written permission from
+.\" Keith Bostic.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)vi.1 8.51 (Berkeley) 10/10/96
+.\"
+.TH VI 1 "October 10, 1996"
+.UC
+.SH NAME
+ex, vi, view \- text editors
+.SH SYNOPSIS
+.B ex
+[\c
+.B -eFRrSsv\c
+] [\c
+.BI -c " cmd"\c
+] [\c
+.BI -t " tag"\c
+] [\c
+.BI -w " size"\c
+] [file ...]
+.br
+.B vi
+[\c
+.B -eFlRrSv\c
+] [\c
+.BI -c " cmd"\c
+] [\c
+.BI -t " tag"\c
+] [\c
+.BI -w " size"\c
+] [file ...]
+.br
+.B view
+[\c
+.B -eFRrSv\c
+] [\c
+.BI -c " cmd"\c
+] [\c
+.BI -t " tag"\c
+] [\c
+.BI -w " size"\c
+] [file ...]
+.SH LICENSE
+The vi program is freely redistributable. You are welcome to copy,
+modify and share it with others under the conditions listed in the
+LICENSE file. If any company (not individual!) finds vi sufficiently
+useful that you would have purchased it, or if any company wishes to
+redistribute it, contributions to the authors would be appreciated.
+.SH DESCRIPTION
+.I \&Vi
+is a screen oriented text editor.
+.I \&Ex
+is a line-oriented text editor.
+.I \&Ex
+and
+.I \&vi
+are different interfaces to the same program,
+and it is possible to switch back and forth during an edit session.
+.I View
+is the equivalent of using the
+.B \-R
+(read-only) option of
+.IR \&vi .
+.PP
+This manual page is the one provided with the
+.I nex/nvi
+versions of the
+.I ex/vi
+text editors.
+.I Nex/nvi
+are intended as bug-for-bug compatible replacements for the original
+Fourth Berkeley Software Distribution (4BSD)
+.I \&ex
+and
+.I \&vi
+programs.
+For the rest of this manual page,
+.I nex/nvi
+is used only when it's necessary to distinguish it from the historic
+implementations of
+.IR ex/vi .
+.PP
+This manual page is intended for users already familiar with
+.IR 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 after the options
+description, entitled ``Fast Startup''.
+It's probably enough to get you going.
+.PP
+The following options are available:
+.TP
+.B \-c
+Execute
+.B cmd
+immediately after starting the edit session.
+Particularly useful for initial positioning in the file, however
+.B cmd
+is not limited to positioning commands.
+This is the POSIX 1003.2 interface for the historic ``+cmd'' syntax.
+.I Nex/nvi
+supports both the old and new syntax.
+.TP
+.B \-e
+Start editing in ex mode, as if the command name were
+.IR \&ex .
+.TP
+.B \-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.)
+.TP
+.B \-l
+Start editing with the lisp and showmatch options set.
+.TP
+.B \-R
+Start editing in read-only mode, as if the command name was
+.IR view ,
+or the
+.B readonly
+option was set.
+.TP
+.B \-r
+Recover the specified files, or, if no files are specified,
+list the files that could be recovered.
+If no recoverable files by the specified name exist,
+the file is edited as if the
+.B \-r
+option had not been specified.
+.TP
+.B \-S
+Run with the
+.B secure
+edit option set, disallowing all access to external programs.
+.TP
+.B \-s
+Enter batch mode; applicable only to
+.I \&ex
+edit sessions.
+Batch mode is useful when running
+.I \&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 ``\-'' argument.
+.I \&Nex/nvi
+supports both the old and new syntax.
+.TP
+.B \-t
+Start editing at the specified tag.
+(See
+.IR ctags (1)).
+.TP
+.B \-w
+Set the initial window size to the specified number of lines.
+.TP
+.B \-v
+Start editing in vi mode, as if the command name was
+.I \&vi
+or
+.IR view .
+.PP
+Command input for
+.I ex/vi
+is read from the standard input.
+In the
+.I \&vi
+interface, it is an error if standard input is not a terminal.
+In the
+.I \&ex
+interface, if standard input is not a terminal,
+.I \&ex
+will read commands from it regardless, however, the session will be a
+batch mode session, exactly as if the
+.B \-s
+option had been specified.
+.PP
+.I Ex/vi
+exits 0 on success, and greater than 0 if an error occurs.
+.SH FAST STARTUP
+This section will tell you the minimum amount that you need to
+do simple editing tasks using
+.IR \&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
+.I \&vi
+and have them walk you through this section.
+.PP
+.I \&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
+.IR \&vi ,
+and for
+.I \&vi
+to give information to you.
+.PP
+The other fact that you need to understand is that
+.I \&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 ``escape'' key, usually labeled ``esc'' on your
+terminal's keyboard.)
+If you're ever confused as to which mode you're in,
+keep entering the <escape> key until
+.I \&vi
+beeps at you.
+(Generally,
+.I \&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 ``vi file_name<carriage-return>''.
+The command you should enter as soon as you start editing is
+``: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:
+.TP
+.B h
+Move the cursor left one character.
+.TP
+.B j
+Move the cursor down one line.
+.TP
+.B k
+Move the cursor up one line.
+.TP
+.B l
+Move the cursor right one character.
+.TP
+.B <cursor-arrows>
+The cursor arrow keys should work, too.
+.TP
+.B /text<carriage-return>
+Search for the string ``text'' in the file,
+and move the cursor to its first character.
+.PP
+The commands to enter new text are:
+.TP
+.B a
+Append new text,
+.I after
+the cursor.
+.TP
+.B i
+Insert new text,
+.I before
+the cursor.
+.TP
+.B o
+Open a new line below the line the cursor is on, and start
+entering text.
+.TP
+.B O
+Open a new line above the line the cursor is on, and start
+entering text.
+.TP
+.B <escape>
+Once you've entered input mode using the one of the
+.BR \&a ,
+.BR \&i ,
+.BR \&O
+or
+.B \&o
+commands, use
+.B <escape>
+to quit entering text and return to command mode.
+.PP
+The commands to copy text are:
+.TP
+.B yy
+Copy the line the cursor is on.
+.TP
+.B p
+Append the copied line after the line the cursor is on.
+.PP
+The commands to delete text are:
+.TP
+.B dd
+Delete the line the cursor is on.
+.TP
+.B x
+Delete the character the cursor is on.
+.PP
+The commands to write the file are:
+.TP
+.B :w<carriage-return>
+Write the file back to the file with the name that you originally used
+as an argument on the
+.I \&vi
+command line.
+.TP
+.B ":w file_name<carriage-return>"
+Write the file back to the file with the name ``file_name''.
+.PP
+The commands to quit editing and exit the editor are:
+.TP
+.B :q<carriage-return>
+Quit editing and leave vi (if you've modified the file, but not
+saved your changes,
+.I \&vi
+will refuse to quit).
+.TP
+.B :q!<carriage-return>
+Quit, discarding any modifications that you may have made.
+.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 ``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 VI COMMANDS
+The following section describes the commands available in the command
+mode of the
+.I \&vi
+editor.
+In each entry below, the tag line is a usage synopsis for the command
+character.
+.PP
+.TP
+.B "[count] <control-A>"
+Search forward
+.I count
+times for the current word.
+.TP
+.B "[count] <control-B>"
+Page backwards
+.I count
+screens.
+.TP
+.B "[count] <control-D>"
+Scroll forward
+.I count
+lines.
+.TP
+.B "[count] <control-E>"
+Scroll forward
+.I count
+lines, leaving the current line and column as is, if possible.
+.TP
+.B "[count] <control-F>"
+Page forward
+.I count
+screens.
+.TP
+.B "<control-G>"
+Display the file information.
+.TP
+.B "<control-H>"
+.TP
+.B "[count] h"
+Move the cursor back
+.I count
+characters in the current line.
+.TP
+.B "[count] <control-J>"
+.TP
+.B "[count] <control-N>"
+.TP
+.B "[count] j"
+Move the cursor down
+.I count
+lines without changing the current column.
+.TP
+.B "<control-L>"
+.TP
+.B "<control-R>"
+Repaint the screen.
+.TP
+.B "[count] <control-M>"
+.TP
+.B "[count] +"
+Move the cursor down
+.I count
+lines to the first nonblank character of that line.
+.TP
+.B "[count] <control-P>"
+.TP
+.B "[count] k"
+Move the cursor up
+.I count
+lines, without changing the current column.
+.TP
+.B "<control-T>"
+Return to the most recent tag context.
+.TP
+.B "<control-U>"
+Scroll backwards
+.I count
+lines.
+.TP
+.B "<control-W>"
+Switch to the next lower screen in the window, or, to the first
+screen if there are no lower screens in the window.
+.TP
+.B "<control-Y>"
+Scroll backwards
+.I count
+lines, leaving the current line and column as is, if possible.
+.TP
+.B "<control-Z>"
+Suspend the current editor session.
+.TP
+.B "<escape>"
+Execute
+.I \&ex
+commands or cancel partial commands.
+.TP
+.B "<control-]>"
+Push a tag reference onto the tag stack.
+.TP
+.B "<control-^>"
+Switch to the most recently edited file.
+.TP
+.B "[count] <space>"
+.TP
+.B "[count] l"
+Move the cursor forward
+.I count
+characters without changing the current line.
+.TP
+.B "[count] ! motion shell-argument(s)"
+Replace text with results from a shell command.
+.TP
+.B "[count] # #|+|-"
+Increment or decrement the cursor number.
+.TP
+.B "[count] $"
+Move the cursor to the end of a line.
+.TP
+.B "%"
+Move to the matching character.
+.TP
+.B "&"
+Repeat the previous substitution command on the current line.
+.TP
+.B "'<character>"
+.TP
+.B "`<character>"
+Return to a context marked by the character
+.IR <character> .
+.TP
+.B "[count] ("
+Back up
+.I count
+sentences.
+.TP
+.B "[count] )"
+Move forward
+.I count
+sentences.
+.TP
+.B "[count] ,"
+Reverse find character
+.I count
+times.
+.TP
+.B "[count] -"
+Move to first nonblank of the previous line,
+.I count
+times.
+.TP
+.B "[count] ."
+Repeat the last
+.I \&vi
+command that modified text.
+.TP
+.B "/RE<carriage-return>"
+.TP
+.B "/RE/ [offset]<carriage-return>"
+.TP
+.B "?RE<carriage-return>"
+.TP
+.B "?RE? [offset]<carriage-return>"
+.TP
+.B "N"
+.TP
+.B "n"
+Search forward or backward for a regular expression.
+.TP
+.B "0"
+Move to the first character in the current line.
+.TP
+.B ":"
+Execute an ex command.
+.TP
+.B "[count] ;"
+Repeat the last character find
+.I count
+times.
+.TP
+.B "[count] < motion"
+.TP
+.B "[count] > motion"
+Shift lines left or right.
+.TP
+.B "@ buffer"
+Execute a named buffer.
+.TP
+.B "[count] A"
+Enter input mode, appending the text after the end of the line.
+.TP
+.B "[count] B"
+Move backwards
+.I count
+bigwords.
+.TP
+.B "[buffer] [count] C"
+Change text from the current position to the end-of-line.
+.TP
+.B "[buffer] D"
+Delete text from the current position to the end-of-line.
+.TP
+.B "[count] E"
+Move forward
+.I count
+end-of-bigwords.
+.TP
+.B "[count] F <character>"
+Search
+.I count
+times backward through the current line for
+.IR <character> .
+.TP
+.B "[count] G"
+Move to line
+.IR count ,
+or the last line of the file if
+.I count
+not specified.
+.TP
+.B "[count] H"
+Move to the screen line
+.I "count - 1"
+lines below the top of the screen.
+.TP
+.B "[count] I"
+Enter input mode, inserting the text at the beginning of the line.
+.TP
+.B "[count] J"
+Join lines.
+.TP
+.B "[count] L"
+Move to the screen line
+.I "count - 1"
+lines above the bottom of the screen.
+.TP
+.B " M"
+Move to the screen line in the middle of the screen.
+.TP
+.B "[count] O"
+Enter input mode, appending text in a new line above the current line.
+.TP
+.B "[buffer] P"
+Insert text from a buffer.
+.TP
+.B "Q"
+Exit
+.I \&vi
+(or visual) mode and switch to
+.I \&ex
+mode.
+.TP
+.B "[count] R"
+Enter input mode, replacing the characters in the current line.
+.TP
+.B "[buffer] [count] S"
+Substitute
+.I count
+lines.
+.TP
+.B "[count] T <character>"
+Search backwards,
+.I count
+times,
+through the current line for the character
+.I after
+the specified
+.IR <character> .
+.TP
+.B "U"
+Restore the current line to its state before the cursor last
+moved to it.
+.TP
+.B "[count] W"
+Move forward
+.I count
+bigwords.
+.TP
+.B "[buffer] [count] X"
+Delete
+.I count
+characters before the cursor.
+.TP
+.B "[buffer] [count] Y"
+Copy (or ``yank'')
+.I count
+lines into the specified buffer.
+.TP
+.B "ZZ"
+Write the file and exit
+.IR \&vi .
+.TP
+.B "[count] [["
+Back up
+.I count
+section boundaries.
+.TP
+.B "[count] ]]"
+Move forward
+.I count
+section boundaries.
+.TP
+.B "\&^"
+Move to first nonblank character on the current line.
+.TP
+.B "[count] _"
+Move down
+.I "count - 1"
+lines, to the first nonblank character.
+.TP
+.B "[count] a"
+Enter input mode, appending the text after the cursor.
+.TP
+.B "[count] b"
+Move backwards
+.I count
+words.
+.TP
+.B "[buffer] [count] c motion"
+Change a region of text.
+.TP
+.B "[buffer] [count] d motion"
+Delete a region of text.
+.TP
+.B "[count] e"
+Move forward
+.I count
+end-of-words.
+.TP
+.B "[count] f<character>"
+Search forward,
+.I count
+times, through the rest of the current line for
+.IR <character> .
+.TP
+.B "[count] i"
+Enter input mode, inserting the text before the cursor.
+.TP
+.B "m <character>"
+Save the current context (line and column) as
+.IR <character> .
+.TP
+.B "[count] o"
+Enter input mode, appending text in a new line under the current line.
+.TP
+.B "[buffer] p"
+Append text from a buffer.
+.TP
+.B "[count] r <character>"
+Replace
+.I count
+characters.
+.TP
+.B "[buffer] [count] s"
+Substitute
+.I count
+characters in the current line starting with the current character.
+.TP
+.B "[count] t <character>"
+Search forward,
+.I count
+times, through the current line for the character immediately
+.I before
+.IR <character> .
+.TP
+.B "u"
+Undo the last change made to the file.
+.TP
+.B "[count] w"
+Move forward
+.I count
+words.
+.TP
+.B "[buffer] [count] x"
+Delete
+.I count
+characters.
+.TP
+.B "[buffer] [count] y motion"
+Copy (or ``yank'')
+a text region specified by the
+.I count
+and motion into a buffer.
+.TP
+.B "[count1] z [count2] -|.|+|^|<carriage-return>"
+Redraw, optionally repositioning and resizing the screen.
+.TP
+.B "[count] {"
+Move backward
+.I count
+paragraphs.
+.TP
+.B "[count] |"
+Move to a specific
+.I column
+position on the current line.
+.TP
+.B "[count] }"
+Move forward
+.I count
+paragraphs.
+.TP
+.B "[count] ~"
+Reverse the case of the next
+.I count
+character(s).
+.TP
+.B "[count] ~ motion"
+Reverse the case of the characters in a text region specified by the
+.I count
+and
+.IR motion .
+.TP
+.B "<interrupt>"
+Interrupt the current operation.
+.SH VI TEXT INPUT COMMANDS
+The following section describes the commands available in the text
+input mode of the
+.I \&vi
+editor.
+.PP
+.TP
+.B "<nul>"
+Replay the previous input.
+.TP
+.B "<control-D>"
+Erase to the previous
+.B shiftwidth
+column boundary.
+.TP
+.B "^<control-D>"
+Erase all of the autoindent characters, and reset the autoindent level.
+.TP
+.B "0<control-D>"
+Erase all of the autoindent characters.
+.TP
+.B "<control-T>"
+Insert sufficient
+.I <tab>
+and
+.I <space>
+characters to move forward to the next
+.B shiftwidth
+column boundary.
+.TP
+.B "<erase>
+.TP
+.B "<control-H>"
+Erase the last character.
+.TP
+.B "<literal next>"
+Quote the next character.
+.TP
+.B "<escape>
+Resolve all text input into the file, and return to command mode.
+.TP
+.B "<line erase>"
+Erase the current line.
+.TP
+.B "<control-W>"
+.TP
+.B "<word erase>"
+Erase the last word.
+The definition of word is dependent on the
+.B altwerase
+and
+.B ttywerase
+options.
+.TP
+.B "<control-X>[0-9A-Fa-f]+"
+Insert a character with the specified hexadecimal value into the text.
+.TP
+.B "<interrupt>"
+Interrupt text input mode, returning to command mode.
+.SH EX COMMANDS
+The following section describes the commands available in the
+.I \&ex
+editor.
+In each entry below, the tag line is a usage synopsis for the command.
+.PP
+.TP
+.B "<end-of-file>"
+Scroll the screen.
+.TP
+.B "! argument(s)"
+.TP
+.B "[range]! argument(s)"
+Execute a shell command, or filter lines through a shell command.
+.TP
+.B \&"
+A comment.
+.TP
+.B "[range] nu[mber] [count] [flags]"
+.TP
+.B "[range] # [count] [flags]"
+Display the selected lines, each preceded with its line number.
+.TP
+.B "@ buffer"
+.TP
+.B "* buffer"
+Execute a buffer.
+.TP
+.B "[line] a[ppend][!]"
+The input text is appended after the specified line.
+.TP
+.B "[range] c[hange][!] [count]"
+The input text replaces the specified range.
+.TP
+.B "cs[cope] add | find | help | kill | reset"
+Execute a Cscope command.
+.TP
+.B "[range] d[elete] [buffer] [count] [flags]"
+Delete the lines from the file.
+.TP
+.B "di[splay] b[uffers] | c[onnections] | s[creens] | t[ags]"
+Display buffers, Cscope connections, screens or tags.
+.TP
+.B "[Ee][dit][!] [+cmd] [file]"
+.TP
+.B "[Ee]x[!] [+cmd] [file]"
+Edit a different file.
+.TP
+.B "exu[sage] [command]"
+Display usage for an
+.I \&ex
+command.
+.TP
+.B "f[ile] [file]"
+Display and optionally change the file name.
+.TP
+.B "[Ff]g [name]"
+.I \&Vi
+mode only.
+Foreground the specified screen.
+.TP
+.B "[range] g[lobal] /pattern/ [commands]"
+.TP
+.B "[range] v /pattern/ [commands]"
+Apply commands to lines matching (or not matching) a pattern.
+.TP
+.B "he[lp]"
+Display a help message.
+.TP
+.B "[line] i[nsert][!]"
+The input text is inserted before the specified line.
+.TP
+.B "[range] j[oin][!] [count] [flags]"
+Join lines of text together.
+.TP
+.B "[range] l[ist] [count] [flags]"
+Display the lines unambiguously.
+.TP
+.B "map[!] [lhs rhs]"
+Define or display maps (for
+.I \&vi
+only).
+.TP
+.B "[line] ma[rk] <character>"
+.TP
+.B "[line] k <character>"
+Mark the line with the mark
+.IR <character> .
+.TP
+.B "[range] m[ove] line"
+Move the specified lines after the target line.
+.TP
+.B "mk[exrc][!] file"
+Write the abbreviations, editor options and maps to the specified
+file.
+.TP
+.B "[Nn][ext][!] [file ...]"
+Edit the next file from the argument list.
+.TP
+.B "[line] o[pen] /pattern/ [flags]"
+Enter open mode.
+.TP
+.B "pre[serve]"
+Save the file in a form that can later be recovered using the
+.I \&ex
+.B \-r
+option.
+.TP
+.B "[Pp]rev[ious][!]"
+Edit the previous file from the argument list.
+.TP
+.B "[range] p[rint] [count] [flags]"
+Display the specified lines.
+.TP
+.B "[line] pu[t] [buffer]"
+Append buffer contents to the current line.
+.TP
+.B "q[uit][!]"
+End the editing session.
+.TP
+.B "[line] r[ead][!] [file]"
+Read a file.
+.TP
+.B "rec[over] file"
+Recover
+.I file
+if it was previously saved.
+.TP
+.B "res[ize] [+|-]size"
+.I \&Vi
+mode only.
+Grow or shrink the current screen.
+.TP
+.B "rew[ind][!]"
+Rewind the argument list.
+.TP
+.B "se[t] [option[=[value]] ...] [nooption ...] [option? ...] [all]"
+Display or set editor options.
+.TP
+.B "sh[ell]"
+Run a shell program.
+.TP
+.B "so[urce] file"
+Read and execute
+.I \&ex
+commands from a file.
+.TP
+.B "[range] s[ubstitute] [/pattern/replace/] [options] [count] [flags]"
+.TP
+.B "[range] & [options] [count] [flags]"
+.TP
+.B "[range] ~ [options] [count] [flags]"
+Make substitutions.
+.TP
+.B "su[spend][!]"
+.TP
+.B "st[op][!]"
+.TP
+.B <suspend>
+Suspend the edit session.
+.TP
+.B "[Tt]a[g][!] tagstring"
+Edit the file containing the specified tag.
+.TP
+.B "tagn[ext][!]"
+Edit the file containing the next context for the current tag.
+.TP
+.B "tagp[op][!] [file | number]"
+Pop to the specified tag in the tags stack.
+.TP
+.B "tagp[rev][!]"
+Edit the file containing the previous context for the current tag.
+.TP
+.B "unm[ap][!] lhs"
+Unmap a mapped string.
+.TP
+.B "ve[rsion]"
+Display the version of the
+.I \&ex/vi
+editor.
+.TP
+.B "[line] vi[sual] [type] [count] [flags]"
+.I \&Ex
+mode only.
+Enter
+.IR \&vi .
+.TP
+.B "[Vi]i[sual][!] [+cmd] [file]"
+.I \&Vi
+mode only.
+Edit a new file.
+.TP
+.B "viu[sage] [command]"
+Display usage for a
+.I \&vi
+command.
+.TP
+.B "[range] w[rite][!] [>>] [file]"
+.TP
+.B "[range] w[rite] [!] [file]"
+.TP
+.B "[range] wn[!] [>>] [file]"
+.TP
+.B "[range] wq[!] [>>] [file]"
+Write the file.
+.TP
+.B "[range] x[it][!] [file]"
+Write the file if it has been modified.
+.TP
+.B "[range] ya[nk] [buffer] [count]"
+Copy the specified lines to a buffer.
+.TP
+.B "[line] z [type] [count] [flags]"
+Adjust the window.
+.SH SET OPTIONS
+There are a large number of options that may be set (or unset) to
+change the editor's behavior.
+This section describes the options, their abbreviations and their
+default values.
+.PP
+In each entry below, the first part of the tag line is the full name
+of the option, followed by any equivalent abbreviations.
+The part in square brackets is the default value of the option.
+Most of the options are boolean, i.e. they are either on or off,
+and do not have an associated value.
+.PP
+Options apply to both
+.I \&ex
+and
+.I \&vi
+modes, unless otherwise specified.
+.PP
+.TP
+.B "altwerase [off]"
+.I \&Vi
+only.
+Select an alternate word erase algorithm.
+.TP
+.B "autoindent, ai [off]"
+Automatically indent new lines.
+.TP
+.B "autoprint, ap [off]"
+.I \&Ex
+only.
+Display the current line automatically.
+.TP
+.B "autowrite, aw [off]"
+Write modified files automatically when changing files.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms backup [QQ]
+.TP
+.B "\*(ms"
+.tr QQ
+Backup files before they are overwritten.
+.TP
+.B "beautify, bf [off]"
+Discard control characters.
+.TP
+.B "cdpath [environment variable CDPATH, or current directory]"
+The directory paths used as path prefixes for the
+.B cd
+command.
+.TP
+.B "cedit [no default]"
+Set the character to edit the colon command-line history.
+.TP
+.B "columns, co [80]"
+Set the number of columns in the screen.
+.TP
+.B "comment [off]"
+.I \&Vi
+only.
+Skip leading comments in shell, C and C++ language files.
+.TP
+.B "directory, dir [environment variable TMPDIR, or /tmp]"
+The directory where temporary files are created.
+.TP
+.B "edcompatible, ed [off]"
+Remember the values of the ``c'' and ``g'' suffices to the
+.B substitute
+commands, instead of initializing them as unset for each new
+command.
+.TP
+.B "errorbells, eb [off]"
+.I \&Ex
+only.
+Announce error messages with a bell.
+.TP
+.B "exrc, ex [off]"
+Read the startup files in the local directory.
+.TP
+.B "extended [off]"
+Regular expressions are extended (i.e.
+.IR egrep (1)\-\c
+style) expressions.
+.TP
+.B "filec [no default]"
+Set the character to perform file path completion on the colon
+command line.
+.TP
+.B "flash [on]"
+Flash the screen instead of beeping the keyboard on error.
+.TP
+.B "hardtabs, ht [8]"
+Set the spacing between hardware tab settings.
+.TP
+.B "iclower [off]"
+Makes all Regular Expressions case-insensitive,
+as long as an upper-case letter does not appear in the search string.
+.TP
+.B "ignorecase, ic [off]"
+Ignore case differences in regular expressions.
+.TP
+.B "keytime [6]"
+The 10th's of a second
+.I ex/vi
+waits for a subsequent key to complete a key mapping.
+.TP
+.B "leftright [off]"
+.I \&Vi
+only.
+Do left-right scrolling.
+.TP
+.B "lines, li [24]"
+.I \&Vi
+only.
+Set the number of lines in the screen.
+.TP
+.B "lisp [off]"
+.I \&Vi
+only.
+Modify various search commands and options to work with Lisp.
+.I "This option is not yet implemented."
+.TP
+.B "list [off]"
+Display lines in an unambiguous fashion.
+.TP
+.B "lock [on]"
+Attempt to get an exclusive lock on any file being edited,
+read or written.
+.TP
+.B "magic [on]"
+Treat certain characters specially in regular expressions.
+.TP
+.B "matchtime [7]"
+.I \&Vi
+only.
+The 10th's of a second
+.I ex/vi
+pauses on the matching character when the
+.B showmatch
+option is set.
+.TP
+.B "mesg [on]"
+Permit messages from other users.
+.TP
+.B "modelines, modeline [off]"
+Read the first and last few lines of each file for
+.I ex
+commands.
+.I "This option will never be implemented."
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms noprint [QQ]
+.TP
+.B "\*(ms"
+.tr QQ
+Characters that are never handled as printable characters.
+.TP
+.B "number, nu [off]"
+Precede each line displayed with its current line number.
+.TP
+.B "octal [off]"
+Display unknown characters as octal numbers, instead of the default
+hexadecimal.
+.TP
+.B "open [on]"
+.I \&Ex
+only.
+If this option is not set, the
+.B open
+and
+.B visual
+commands are disallowed.
+.TP
+.B "optimize, opt [on]"
+.I \&Vi
+only.
+Optimize text throughput to dumb terminals.
+.I "This option is not yet implemented."
+.TP
+.B "paragraphs, para [IPLPPPQPP LIpplpipbp]"
+.I \&Vi
+only.
+Define additional paragraph boundaries for the
+.B \&{
+and
+.B \&}
+commands.
+.TP
+.B "path []"
+Define additional directories to search for files being edited.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms print [QQ]
+.TP
+.B "\*(ms"
+.tr QQ
+Characters that are always handled as printable characters.
+.TP
+.B "prompt [on]"
+.I \&Ex
+only.
+Display a command prompt.
+.TP
+.B "readonly, ro [off]"
+Mark the file and session as read-only.
+.TP
+.B "recdir [/var/tmp/vi.recover]"
+The directory where recovery files are stored.
+.TP
+.B "redraw, re [off]"
+.I \&Vi
+only.
+Simulate an intelligent terminal on a dumb one.
+.I "This option is not yet implemented."
+.TP
+.B "remap [on]"
+Remap keys until resolved.
+.TP
+.B "report [5]"
+Set the number of lines about which the editor reports changes
+or yanks.
+.TP
+.B "ruler [off]"
+.I \&Vi
+only.
+Display a row/column ruler on the colon command line.
+.TP
+.B "scroll, scr [window / 2]"
+Set the number of lines scrolled.
+.TP
+.B "searchincr [off]"
+Makes the
+.B \&/
+and
+.B \&?
+commands incremental.
+.TP
+.B "sections, sect [NHSHH HUnhsh]"
+.I \&Vi
+only.
+Define additional section boundaries for the
+.B \&[[
+and
+.B \&]]
+commands.
+.TP
+.B "secure [off]"
+Turns off all access to external programs.
+.TP
+.B "shell, sh [environment variable SHELL, or /bin/sh]"
+Select the shell used by the editor.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms shellmeta [~{[*?$`'Q\e]
+.TP
+.B "\*(ms"
+.tr QQ
+Set the meta characters checked to determine if file name expansion
+is necessary.
+.TP
+.B "shiftwidth, sw [8]"
+Set the autoindent and shift command indentation width.
+.TP
+.B "showmatch, sm [off]"
+.I \&Vi
+only.
+Note matching ``{'' and ``('' for ``}'' and ``)'' characters.
+.TP
+.B "showmode, smd [off]"
+.I \&Vi
+only.
+Display the current editor mode and a ``modified'' flag.
+.TP
+.B "sidescroll [16]"
+.I \&Vi
+only.
+Set the amount a left-right scroll will shift.
+.TP
+.B "slowopen, slow [off]"
+Delay display updating during text input.
+.I "This option is not yet implemented."
+.TP
+.B "sourceany [off]"
+Read startup files not owned by the current user.
+.I "This option will never be implemented."
+.TP
+.B "tabstop, ts [8]"
+This option sets tab widths for the editor display.
+.TP
+.B "taglength, tl [0]"
+Set the number of significant characters in tag names.
+.TP
+.B "tags, tag [tags /var/db/libc.tags /sys/kern/tags]"
+Set the list of tags files.
+.TP
+.B "term, ttytype, tty [environment variable TERM]"
+Set the terminal type.
+.TP
+.B "terse [off]"
+This option has historically made editor messages less verbose.
+It has no effect in this implementation.
+.TP
+.B "tildeop [off]"
+Modify the
+.B \&~
+command to take an associated motion.
+.TP
+.B "timeout, to [on]"
+Time out on keys which may be mapped.
+.TP
+.B "ttywerase [off]"
+.I \&Vi
+only.
+Select an alternate erase algorithm.
+.TP
+.B "verbose [off]"
+.I \&Vi
+only.
+Display an error message for every error.
+.TP
+.B "w300 [no default]"
+.I \&Vi
+only.
+Set the window size if the baud rate is less than 1200 baud.
+.TP
+.B "w1200 [no default]"
+.I \&Vi
+only.
+Set the window size if the baud rate is equal to 1200 baud.
+.TP
+.B "w9600 [no default]"
+.I \&Vi
+only.
+Set the window size if the baud rate is greater than 1200 baud.
+.TP
+.B "warn [on]"
+.I \&Ex
+only.
+This option causes a warning message to the terminal if the file has
+been modified, since it was last written, before a
+.B \&!
+command.
+.TP
+.B "window, w, wi [environment variable LINES]"
+Set the window size for the screen.
+.TP
+.B "windowname [off]"
+Change the icon/window name to the current file name even if it can't
+be restored on editor exit.
+.TP
+.B "wraplen, wl [0]"
+.I \&Vi
+only.
+Break lines automatically, the specified number of columns from the
+left-hand margin.
+If both the
+.B wraplen
+and
+.B wrapmargin
+edit options are set, the
+.B wrapmargin
+value is used.
+.TP
+.B "wrapmargin, wm [0]"
+.I \&Vi
+only.
+Break lines automatically, the specified number of columns from the
+right-hand margin.
+If both the
+.B wraplen
+and
+.B wrapmargin
+edit options are set, the
+.B wrapmargin
+value is used.
+.TP
+.B "wrapscan, ws [on]"
+Set searches to wrap around the end or beginning of the file.
+.TP
+.B "writeany, wa [off]"
+Turn off file-overwriting checks.
+.SH ENVIRONMENTAL VARIABLES
+.TP
+.I COLUMNS
+The number of columns on the screen.
+This value overrides any system or terminal specific values.
+If the
+.I COLUMNS
+environmental variable is not set when
+.I ex/vi
+runs, or the
+.B columns
+option is explicitly reset by the user,
+.I ex/vi
+enters the value into the environment.
+.TP
+.I EXINIT
+A list of
+.I \&ex
+startup commands, read if the variable
+.I NEXINIT
+is not set.
+.TP
+.I HOME
+The user's home directory, used as the initial directory path
+for the startup ``$\fIHOME\fP/.nexrc'' and ``$\fIHOME\fP/.exrc''
+files.
+This value is also used as the default directory for the
+.I \&vi
+.B \&cd
+command.
+.TP
+.I LINES
+The number of rows on the screen.
+This value overrides any system or terminal specific values.
+If the
+.I LINES
+environmental variable is not set when
+.I ex/vi
+runs, or the
+.B lines
+option is explicitly reset by the user,
+.I ex/vi
+enters the value into the environment.
+.TP
+.I NEXINIT
+A list of
+.I \&ex
+startup commands.
+.TP
+.I SHELL
+The user's shell of choice (see also the
+.B shell
+option).
+.TP
+.I TERM
+The user's terminal type.
+The default is the type ``unknown''.
+If the
+.I TERM
+environmental variable is not set when
+.I ex/vi
+runs, or the
+.B term
+option is explicitly reset by the user,
+.I ex/vi
+enters the value into the environment.
+.TP
+.I TMPDIR
+The location used to stored temporary files (see also the
+.B directory
+edit option).
+.SH ASYNCHRONOUS EVENTS
+.TP
+SIGALRM
+.I \&Vi/ex
+uses this signal for periodic backups of file modifications and to
+display ``busy'' messages when operations are likely to take a long time.
+.TP
+SIGHUP
+.TP
+SIGTERM
+If the current buffer has changed since it was last written in its
+entirety, the editor attempts to save the modified file so it can
+be later recovered.
+See the
+.I \&vi/ex
+Reference manual section entitled ``Recovery'' for more information.
+.TP
+SIGINT
+When an interrupt occurs,
+the current operation is halted,
+and the editor returns to the command level.
+If interrupted during text input,
+the text already input is resolved into the file as if the text
+input had been normally terminated.
+.TP
+SIGWINCH
+The screen is resized.
+See the
+.I \&vi/ex
+Reference manual section entitled ``Sizing the Screen'' for more information.
+.TP
+SIGCONT
+.TP
+SIGQUIT
+.TP
+SIGTSTP
+.I \&Vi/ex
+ignores these signals.
+.SH FILES
+.TP
+/bin/sh
+The default user shell.
+.TP
+/etc/vi.exrc
+System-wide vi startup file.
+.TP
+/tmp
+Temporary file directory.
+.TP
+/var/tmp/vi.recover
+The default recovery file directory.
+.TP
+$HOME/.nexrc
+1st choice for user's home directory startup file.
+.TP
+$HOME/.exrc
+2nd choice for user's home directory startup file.
+.TP
+\&.nexrc
+1st choice for local directory startup file.
+.TP
+\&.exrc
+2nd choice for local directory startup file.
+.SH SEE ALSO
+.IR ctags (1),
+.IR more (3),
+.IR curses (3),
+.IR dbopen (3)
+.sp
+The ``Vi Quick Reference'' card.
+.sp
+``An Introduction to Display Editing with Vi'', found in the
+``UNIX User's Manual Supplementary Documents''
+section of both the 4.3BSD and 4.4BSD manual sets.
+This document is the closest thing available to an introduction to the
+.I \&vi
+screen editor.
+.sp
+``Ex Reference Manual (Version 3.7)'',
+found in the
+``UNIX User's Manual Supplementary Documents''
+section of both the 4.3BSD and 4.4BSD manual sets.
+This document is the final reference for the
+.I \&ex
+editor, as distributed in most historic 4BSD and System V systems.
+.sp
+``Edit: A tutorial'',
+found in the
+``UNIX User's Manual Supplementary Documents''
+section of the 4.3BSD manual set.
+This document is an introduction to a simple version of the
+.I \&ex
+screen editor.
+.sp
+``Ex/Vi Reference Manual'',
+found in the
+``UNIX User's Manual Supplementary Documents''
+section of the 4.4BSD manual set.
+This document is the final reference for the
+.I \&nex/nvi
+text editors, as distributed in 4.4BSD and 4.4BSD-Lite.
+.PP
+.I Roff
+source for all of these documents is distributed with
+.I nex/nvi
+in the
+.I nvi/USD.doc
+directory of the
+.I nex/nvi
+source code.
+.sp
+The files ``autowrite'', ``input'', ``quoting'' and ``structures''
+found in the
+.I nvi/docs/internals
+directory of the
+.I nex/nvi
+source code.
+.SH HISTORY
+The
+.I nex/nvi
+replacements for the
+.I ex/vi
+editor first appeared in 4.4BSD.
+.SH STANDARDS
+.I \&Nex/nvi
+is close to IEEE Std1003.2 (``POSIX'').
+That document differs from historical
+.I ex/vi
+practice in several places; there are changes to be made on both sides.
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/Makefile b/contrib/nvi/docs/USD.doc/vi.ref/Makefile
new file mode 100644
index 000000000000..0e1b6343350d
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/Makefile
@@ -0,0 +1,32 @@
+# @(#)Makefile 8.20 (Berkeley) 8/18/96
+
+MACROS= -me
+ROFF= groff
+TBL= tbl
+
+all: vi.ref.txt vi.ref.ps
+
+vi.ref.txt: vi.ref index.so
+ soelim vi.ref | ${TBL} | groff ${MACROS} -Tascii > $@
+ rm -f index
+ chmod 444 $@
+
+vi.ref.ps: vi.ref index.so
+ soelim vi.ref | ${TBL} | ${ROFF} ${MACROS} > $@
+ rm -f index
+ chmod 444 $@
+
+index.so: vi.ref
+ # Build index.so, side-effect of building the paper.
+ soelim vi.ref | ${TBL} | ${ROFF} ${MACROS} > /dev/null
+ sed -e 's/MINUSSIGN/\\-/' \
+ -e 's/DOUBLEQUOTE/""/' \
+ -e "s/SQUOTE/'/" \
+ -e 's/ /__SPACE/g' < index | \
+ sort -u '-t ' +0 -1 +1n | awk -f merge.awk | \
+ sed -e 's/__SPACE/ /g' > $@
+ rm -f index
+ chmod 444 $@
+
+clean:
+ rm -f vi.ref.ps vi.ref.txt index index.so
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff b/contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff
new file mode 100644
index 000000000000..382e635a6fdd
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff
@@ -0,0 +1,1924 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)ex.cmd.roff 8.41 (Berkeley) 8/17/96
+.\"
+.SH 1 "Ex Description"
+.pp
+The following words have special meanings for
+.CO ex
+commands.
+.KY "<end-of-file>"
+.IP "<end-of-file>"
+The end-of-file character is used to scroll the screen in the
+.CO ex
+editor.
+This character is normally
+.LI <control-D> .
+However, whatever character is set for the current terminal is supported
+as well as
+.LI <control-D> .
+.KY "line"
+.IP "line"
+A single-line address, given in any of the forms described in the
+section entitled
+.QB "Ex Addressing" .
+The default for
+.LI line
+is the current line.
+.KY "range"
+.IP "range"
+A line, or a pair of line addresses, separated by a comma or semicolon.
+(See the section entitled
+.QB "Ex Addressing"
+for more information.)
+The default for range is the current line
+.i only ,
+i.e.
+.QT \&.,. .
+A percent sign
+.PQ %
+stands for the range
+.QT 1,$ .
+The starting address must be less than, or equal to, the ending address.
+.KY "count"
+.IP "count"
+A positive integer, specifying the number of lines to be affected by
+the command; the default is 1.
+Generally, a count past the end-of-file may be specified, e.g. the
+command
+.QT "p 3000"
+in a 10 line file is acceptable, and will print from the current line
+through the last line in the file.
+.KY "flags"
+.IP "flags"
+One or more of the characters
+.QQ # ,
+.QQ p ,
+and
+.QQ l .
+When a command that accepts these flags completes, the addressed line(s)
+are written out as if by the corresponding
+.CO # ,
+.CO l
+or
+.CO p
+commands.
+In addition, any number of
+.QT +
+or
+.QT \-
+characters can be specified before, after, or during the flags, in which
+case the line written is not necessarily the one affected by the command,
+but rather the line addressed by the offset address specified.
+The default for
+.LI flags
+is none.
+.KY "file"
+.IP "file"
+A pattern used to derive a pathname; the default is the current file.
+File names are subjected to normal
+.XR sh 1
+word expansions.
+.pp
+Anywhere a file name is specified, it is also possible to use
+the special string
+.QT /tmp .
+This will be replaced with a temporary file name which can be used
+for temporary work, e.g.
+.QT ":e /tmp"
+creates and edits a new file.
+.pp
+If both a count and a range are specified for commands that use either,
+the starting line for the command is the
+.i last
+line addressed by the range, and
+.LI count - 1
+subsequent lines are affected by the command, e.g. the command
+.QT 2,3p4
+prints out lines 3, 4, 5 and 6.
+.pp
+When only a line or range is specified, with no command, the implied
+command is either a
+.CO list ,
+.CO number
+or
+.CO print
+command.
+The command used is the most recent of the three commands to have been
+used (including any use as a flag).
+If none of these commands have been used before, the
+.CO print
+command is the implied command.
+When no range or count is specified and the command line is a blank line,
+the current line is incremented by 1 and then the current line is displayed.
+.pp
+Zero or more whitespace characters may precede or follow the addresses,
+count, flags, or command name.
+Any object following a command name (such as buffer, file, etc.),
+that begins with an alphabetic character,
+should be separated from the command name by at least one whitespace
+character.
+.pp
+Any character, including
+.LI <carriage-return> ,
+.QT %
+and
+.QT #
+retain their literal value when preceded by a backslash.
+.SH 1 "Ex Commands"
+.pp
+The following section describes the commands available in the
+.CO ex
+editor.
+In each entry below, the tag line is a usage synopsis for the command.
+.pp
+Each command can be entered as the abbreviation
+(those characters in the synopsis command word preceding the
+.QQ [
+character),
+the full command (all characters shown for the command word,
+omitting the
+.QQ [
+and
+.QQ ]
+characters),
+or any leading subset of the full command down to the abbreviation.
+For example, the args command (shown as
+.QT ar[gs]
+in the synopsis)
+can be entered as
+.QT ar ,
+.QT arg
+or
+.QT args .
+.pp
+Each
+.CO ex
+command described below notes the new current line after it
+is executed, as well as any options that affect the command.
+.\" I cannot get a double quote to print to save my life. The ONLY way
+.\" I've been able to get this to work is with the .tr command.
+.tr Q"
+.ds ms Q
+.KY DOUBLEQUOTE
+.IP "\*(ms"
+.tr QQ
+A comment.
+Command lines beginning with the double-quote character
+.PQ """"
+are ignored.
+This permits comments in editor scripts and startup files.
+.KY "<control-D>"
+.KY "<end-of-file>"
+.IP "<control-D>"
+.IP "<end-of-file>"
+Scroll the screen.
+Write the next N lines, where N is the value of the
+.OP scroll
+option.
+The command is the end-of-file terminal character, which may be
+different on different terminals.
+Traditionally, it is the
+.LI <control-D>
+key.
+.sp
+Historically, the
+.CO eof
+command ignored any preceding count, and the
+.LI <end-of-file>
+character was ignored unless it was entered as the first character
+of the command.
+This implementation treats it as a command
+.i only
+if entered as the first character of the command line, and otherwise
+treats it as any other character.
+.SS
+.SP Line:
+Set to the last line written.
+.SP Options:
+Affected by the
+.OP scroll
+option.
+.SE
+.KY "!"
+.IP "! argument(s)"
+.Ip "[range]! argument(s)"
+Execute a shell command, or filter lines through a shell command.
+In the first synopsis, the remainder of the line after the
+.QT !
+character is passed to the program named by the
+.OP shell
+option, as a single argument.
+.sp
+Within the rest of the line,
+.QT %
+and
+.QT #
+are expanded into the current and alternate pathnames, respectively.
+The character
+.QT !
+is expanded with the command text of the previous
+.CO !
+command.
+(Therefore, the command
+.CO !!
+repeats the previous
+.CO !
+command.)
+The special meanings of
+.QT % ,
+.QT # ,
+and
+.QT !
+can be overridden by escaping them with a backslash.
+If no
+.CO !
+or
+.CO :!
+command has yet been executed, it is an error to use an unescaped
+.QT !
+character.
+The
+.CO !
+command does
+.i not
+do shell expansion on the strings provided as arguments.
+If any of the above expansions change the command the user entered,
+the command is redisplayed at the bottom of the screen.
+.sp
+.CO Ex
+then executes the program named by the
+.OP shell
+option, with a
+.b \-c
+flag followed by the arguments (which are bundled into a single argument).
+.sp
+The
+.CO !
+command is permitted in an empty file.
+.sp
+If the file has been modified since it was last completely written,
+the
+.Co !
+command will warn you.
+.sp
+A single
+.QT !
+character is displayed when the command completes.
+.sp
+In the second form of the
+.CO !
+command, the remainder of the line after the
+.QT !
+is passed to the program named by the
+.OP shell
+option, as described above.
+The specified lines are passed to the program as standard input,
+and the standard and standard error output of the program replace
+the original lines.
+.SS
+.SP Line:
+Unchanged if no range was specified, otherwise set to the first
+line of the range.
+.SP Options:
+Affected by the
+.OP shell
+and
+.OP warn
+options.
+.SE
+.KY "#"
+.IP "[range] # [count] [flags]"
+.KY "number"
+.Ip "[range] nu[mber] [count] [flags]"
+Display the selected lines, each preceded with its line number.
+.sp
+The line number format is
+.QQ %6d ,
+followed by two spaces.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+Affected by the
+.OP list
+option.
+.SE
+.KY "@"
+.IP "@ buffer"
+.KY "*"
+.Ip "* buffer"
+Execute a buffer.
+Each line in the named buffer is executed as an
+.CO ex
+command.
+If no buffer is specified, or if the specified buffer is
+.QT @
+or
+.QT * ,
+the last buffer executed is used.
+.KY <
+.IP "[range] <[< ...] [count] [flags]"
+Shift lines left or right.
+The specified lines are shifted to the left (for the
+.CO <
+command) or right (for the
+.CO >
+command), by the number of columns specified by the
+.OP shiftwidth
+option.
+Only leading whitespace characters are deleted when shifting left;
+once the first column of the line contains a nonblank character,
+the
+.CO shift
+command will succeed, but the line will not be modified.
+.sp
+If the command character
+.CO <
+or
+.CO >
+is repeated more than once, the command is repeated once for each
+additional command character.
+.SS
+.SP Line:
+If the current line is set to one of the lines that are affected
+by the command, it is unchanged.
+Otherwise, it is set to the first nonblank character of the lowest
+numbered line shifted.
+.SP Options:
+Affected by the
+.OP shiftwidth
+option.
+.SE
+.KY =
+.IP "[line] = [flags]"
+Display the line number of
+.LI line
+(which defaults to the last line in the file).
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY >
+.IP "[range] >[> ...] [count] [flags]"
+Shift right.
+The specified lines are shifted to the right by the number of columns
+specified by the
+.OP shiftwidth
+option, by inserting tab and space characters.
+Empty lines are not changed.
+.sp
+If the command character
+.QT >
+is repeated more than once, the command is repeated once for each
+additional command character.
+.SS
+.SP Line:
+Set to the last line modified by the command.
+.SP Options:
+Affected by the
+.OP shiftwidth
+option.
+.SE
+.KY abbrev
+.IP "ab[brev] lhs rhs"
+Add an abbreviation to the current abbreviation list.
+When inserting text in
+.CO vi ,
+each time a non-word character is entered after a word character,
+a set of characters ending at the word character are checked for
+a match with
+.LI lhs .
+If a match is found, they are replaced with
+.LI rhs .
+The set of characters that are checked for a match are defined as follows,
+for inexplicable historical reasons.
+If only one or two characters were entered before the non-word character
+that triggered the check,
+and after the beginning of the insertion,
+or the beginning of the line or the file,
+or the last
+.LI <blank>
+character that was entered,
+then the one or the both characters are checked for a match.
+Otherwise, the set includes both characters,
+as well as the characters that precede them that are the same word
+class (i.e. word or non-word) as the
+.b second
+to last character entered before the non-word character that triggered
+the check,
+back to the first
+.LI <blank> character,
+the beginning of the insertion,
+or the beginning of the line or the file.
+.sp
+For example, the abbreviations:
+.sp
+.ne 3v
+.ft C
+.TS
+r l l.
+:abbreviate abc ABC
+:abbreviate #i #include
+:abbreviate /*#i /*#include
+.TE
+.ft R
+will all work, while the abbreviations:
+.sp
+.ne 2v
+.ft C
+.TS
+r l l.
+:abbreviate a#i A#include
+:abbreviate /* /********************
+.TE
+.ft R
+will not work, and are not permitted by
+.CO nvi .
+.sp
+To keep the abbreviation expansion from happening,
+the character immediately following the
+.LI lhs
+characters should be quoted with a
+.LI <literal-next>
+character.
+.sp
+The replacement
+.LI rhs
+is itself subject to both further abbreviation expansion and further
+map expansion.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY append
+.IP "[line] a[ppend][!]"
+The input text is appended to the specified line.
+If line 0 is specified, the text is inserted at the beginning of the file.
+Set to the last line input.
+If no lines are input, then set to
+.LI line ,
+or to the first line of the file if a
+.LI line
+of 0 was specified.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option to be toggled for the duration of the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP autoindent
+and
+.OP number
+options.
+.SE
+.KY args
+.IP "ar[gs]"
+Display the argument list.
+The current argument is displayed inside of
+.QT [
+and
+.QT ]
+characters.
+The argument list is the list of operands specified on startup,
+which can be replaced using the
+.CO next
+command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY bg
+.IP bg
+.CO Vi
+mode only.
+Background the current screen.
+The screen is unchanged,
+but is no longer accessible and disappears from the display.
+Use the
+.CO fg
+command to bring the screen back to the display foreground.
+.SS
+.SP Line:
+Set to the current line when the screen was last edited.
+.SP Options:
+None.
+.SE
+.KY change
+.IP "[range] c[hange][!] [count]"
+Replace the lines with input text.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option to be toggled for the duration of the command.
+.SS
+.SP Line:
+Set to the last line input, or, if no lines were input,
+set to the line before the target line, or to the first
+line of the file if there are no lines preceding the target line.
+.SP Options:
+Affected by the
+.OP autoindent
+and
+.OP number
+options.
+.SE
+.KY cd
+.KY chdir
+.IP "chd[ir][!] [directory]"
+.Ip "cd[!] [directory]"
+Change the current working directory.
+The
+.LI directory
+argument is subjected to
+.XR sh 1
+word expansions.
+When invoked with no directory argument and the
+.LI HOME
+environment variable is set, the directory named by the
+.LI HOME
+environment variable becomes the new current directory.
+Otherwise, the new current directory becomes the directory returned
+by the
+.XR getpwent 3
+routine.
+.sp
+The
+.CO chdir
+command will fail if the file has been modified since the last complete
+write of the file.
+You can override this check by appending a
+.QT !
+character to the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP cdpath
+option.
+.SE
+.KY copy
+.KY t
+.IP "[range] co[py] line [flags]"
+.Ip "[range] t line [flags]"
+Copy the specified lines (range) after the destination line.
+Line 0 may be specified to insert the lines at the beginning of
+the file.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY cscope
+.IP "cs[cope] command [args]"
+Execute a
+.CO cscope
+command.
+For more information, see the section of the reference manual entitled
+.QB "Tags, Tag Stacks, and Cscope" .
+.KY delete
+.IP "[range] d[elete] [buffer] [count] [flags]"
+Delete the lines from the file.
+The deleted text is saved in the specified buffer, or, if no buffer
+is specified, in the unnamed buffer.
+If the command name is followed by a letter that could be interpreted
+as either a buffer name or a flag value (because neither a
+.LI count
+or
+.LI flags
+values were given),
+.CO ex
+treats the letter as a
+.LI flags
+value if the letter immediately follows the command name,
+without any whitespace separation.
+If the letter is preceded by whitespace characters,
+it treats it as a buffer name.
+.SS
+.SP Line:
+Set to the line following the deleted lines,
+or to the last line if the deleted lines were at the end.
+.SP Options:
+None.
+.SE
+.KY display
+.IP "di[splay] b[uffers] | c[onnections] | s[creens] | t[ags]"
+Display buffers,
+.CO cscope
+connections, screens or tags.
+The
+.CO display
+command takes one of three additional arguments, which are as follows:
+.SS
+.SP b[uffers]
+Display all buffers (including named, unnamed, and numeric)
+that contain text.
+.SP c[onnections]
+Display the source directories for all attached
+.CO cscope
+databases.
+.SP s[creens]
+Display the file names of all background screens.
+.SP t[ags]
+Display the tags stack.
+.SE
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY edit
+.IP "e[dit][!] [+cmd] [file]"
+.Ip "ex[!] [+cmd] [file]"
+Edit a different file.
+If the current buffer has been modified since the last complete write,
+the command will fail.
+You can override this by appending a
+.QT !
+character to the command name.
+.sp
+If the
+.QT +cmd
+option is specified, that
+.CO ex
+command will be executed in the new file.
+Any
+.CO ex
+command may be used, although the most common use of this feature is
+to specify a line number or search pattern to set the initial location
+in the new file.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Edit
+or
+.CO Ex ,
+while in
+.CO vi
+mode, will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+If you have previously edited the file, the current line will be set
+to your last position in the file.
+If that position does not exist, or you have not previously edited the
+file, the current line will be set to the first line of the file if
+you are in
+.CO vi
+mode, and the last line of the file if you are in
+.CO ex .
+.SP Options:
+None.
+.SE
+.KY exusage
+.IP "exu[sage] [command]"
+Display usage for an
+.CO ex
+command.
+If
+.LI command
+is specified, a usage statement for that command is displayed.
+Otherwise, usage statements for all
+.CO ex
+commands are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY file
+.IP "f[ile] [file]"
+Display and optionally change the file name.
+If a file name is specified, the current pathname is changed to the
+specified name.
+The current pathname, the number of lines, and the current position
+in the file are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY fg
+.IP "fg [name]"
+.CO Vi
+mode only.
+Foreground the specified screen.
+If the argument name doesn't exactly match the name of a file displayed
+by a background screen,
+it is compared against the last component of each of the file names.
+If no background screen is specified,
+the first background screen is foregrounded.
+.sp
+By default,
+foregrounding causes the current screen to be swapped with the backgrounded
+screen.
+Capitalizing the first letter of the command, i.e.
+.CO Fg ,
+will foreground the backgrounded screen in a new screen instead of
+swapping it with the current screen.
+.SS
+.SP Line:
+Set to the current line when the screen was last edited.
+.SP Options:
+None.
+.SE
+.KY global
+.IP "[range] g[lobal] /pattern/ [commands]"
+.KY v
+.Ip "[range] v /pattern/ [commands]"
+Apply commands to lines matching (or not matching) a pattern.
+The lines within the given range that match
+.PQ g[lobal] ,
+or do not match
+.PQ v
+the given pattern are selected.
+Then, the specified
+.CO ex
+command(s) are executed with the current line
+.PQ \&.
+set to each selected line.
+If no range is specified, the entire file is searched for matching,
+or not matching, lines.
+.sp
+Multiple commands can be specified, one per line, by escaping each
+.LI <newline>
+character with a backslash, or by separating commands with a
+.QT |
+character.
+If no commands are specified, the command defaults to the
+.CO print
+command.
+.sp
+For the
+.CO append ,
+.CO change
+and
+.CO insert
+commands, the input text must be part of the global command line.
+In this case, the terminating period can be omitted if it ends the commands.
+.sp
+The
+.CO visual
+command may also be specified as one of the
+.CO ex
+commands.
+In this mode, input is taken from the terminal.
+Entering a
+.CO Q
+command in
+.CO vi
+mode causes the next line matching the pattern to be selected and
+.CO vi
+to be reentered, until the list is exhausted.
+.sp
+The
+.CO global ,
+.CO v
+and
+.CO undo
+commands cannot be used as part of these commands.
+.sp
+The editor options
+.OP autoindent ,
+.OP autoprint
+and
+.OP report
+are turned off for the duration of the
+.CO global
+and
+.CO v
+commands.
+.SS
+.SP Line:
+The last line modified.
+.SP Options:
+Affected by the
+.OP ignorecase
+and
+.OP magic
+options.
+Turns off the
+.OP autoindent ,
+.OP autoprint
+and
+.OP report
+options.
+.SE
+.KY help
+.IP "he[lp]"
+Display a help message.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY insert
+.IP "[line] i[nsert][!]"
+The input text is inserted before the specified line.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option setting to be toggled for the duration of this command.
+.SS
+.SP Line:
+Set to the last line input; if no lines were input,
+set to the line before the target line, or to the first line
+of the file if there are no lines preceding the target line.
+Affected by the
+.OP autoindent
+and
+.OP number
+options.
+.SE
+.KY join
+.IP "[range] j[oin][!] [count] [flags]"
+Join lines of text together.
+.sp
+A
+.LI count
+specified to the
+.Sy join
+command specifies that the last line of the
+.LI range
+plus
+.LI count
+subsequent lines will be joined.
+(Note, this differs by one from the general rule where only
+.LI count - 1
+subsequent lines are affected.)
+.sp
+If the current line ends with a whitespace character, all whitespace
+is stripped from the next line.
+Otherwise, if the next line starts with a open parenthesis
+.PQ ( ,
+do nothing.
+Otherwise, if the current line ends with a question mark
+.PQ ? ,
+period
+.PQ \&.
+or exclamation point
+.PQ ! ,
+insert two spaces.
+Otherwise, insert a single space.
+.sp
+Appending a
+.QT !
+character to the command name causes a simpler join with no
+white-space processing.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY list
+.IP "[range] l[ist] [count] [flags]"
+Display the lines unambiguously.
+Tabs are displayed as
+.QT ^I ,
+and the end of the line is marked with a
+.QT $
+character.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+Affected by the
+.OP number
+option.
+.SE
+.KY map
+.IP "map[!] [lhs rhs]"
+Define or display maps (for
+.CO vi
+only).
+.sp
+If
+.QT lhs
+and
+.QT rhs
+are not specified, the current set of command mode maps are displayed.
+If a
+.QT !
+character is appended to to the command,
+the text input mode maps are displayed.
+.sp
+Otherwise, when the
+.QT lhs
+character sequence is entered in
+.CO vi ,
+the action is as if the corresponding
+.QT rhs
+had been entered.
+If a
+.QT !
+character is appended to the command name,
+the mapping is effective during text input mode,
+otherwise, it is effective during command mode.
+This allows
+.QT lhs
+to have two different macro definitions at the same time: one for command
+mode and one for input mode.
+.sp
+Whitespace characters require escaping with a
+.LI <literal-next>
+character to be entered in the
+.LI lhs
+string in visual mode.
+.sp
+Normally, keys in the
+.LI rhs
+string are remapped (see the
+.OP remap
+option),
+and it is possible to create infinite loops.
+However, keys which map to themselves are not further remapped,
+regardless of the setting of the
+.OP remap
+option.
+For example, the command
+.QT ":map n nz."
+maps the
+.QT n
+key to the
+.CO n
+and
+.CO z
+commands.
+.sp
+To exit an infinitely looping map, use the terminal
+.LI <interrupt>
+character.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP remap
+option.
+.SE
+.KY mark
+.KY k
+.IP "[line] ma[rk] <character>"
+.Ip "[line] k <character>"
+Mark the line with the mark
+.LI <character> .
+The expressions
+.QT '<character>
+and
+.QT `<character>
+can then be used as an address in any command that uses one.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY move
+.IP "[range] m[ove] line"
+Move the specified lines after the target line.
+A target line of 0 places the lines at the beginning of the file.
+.SS
+.SP Line:
+Set to the first of the moved lines.
+.SP Options:
+None.
+.SE
+.KY mkexrc
+.IP "mk[exrc][!] file"
+Write the abbreviations, editor options and maps to the specified
+file.
+Information is written in a form which can later be read back in
+using the
+.CO ex
+.CO source
+command.
+If
+.LI file
+already exists, the
+.CO mkexrc
+command will fail.
+This check can be overridden by appending a
+.QT !
+character to the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY next
+.IP "n[ext][!] [file ...]"
+Edit the next file from the argument list.
+The
+.CO next
+command will fail if the file has been modified since the last complete
+write.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+The argument list can optionally be replaced by specifying a new one
+as arguments to this command.
+In this case, editing starts with the first file on the new list.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Next ,
+while in
+.CO vi
+mode, will set the argument list and edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the options
+.OP autowrite
+and
+.OP writeany .
+.SE
+.KY open
+.IP "[line] o[pen] /pattern/ [flags]"
+Enter open mode.
+Open mode is the same as being in
+.CO vi ,
+but with a one-line window.
+All the standard
+.CO vi
+commands are available.
+If a match is found for the optional RE argument,
+the cursor is set to the start of the matching pattern.
+.sp
+.i "This command is not yet implemented."
+.SS
+.SP Line:
+Unchanged, unless the optional RE is specified, in which case it is
+set to the line where the matching pattern is found.
+.SP Options:
+Affected by the
+.OP open
+option.
+.SE
+.KY preserve
+.IP "pre[serve]"
+Save the file in a form that can later be recovered using the
+.CO ex
+.b \-r
+option.
+When the file is preserved, an email message is sent to the user.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY previous
+.IP "prev[ious][!]"
+Edit the previous file from the argument list.
+The
+.CO previous
+command will fail if the file has been modified since the last complete
+write.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Previous ,
+while in
+.CO vi
+mode, will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the options
+.OP autowrite
+and
+.OP writeany .
+None.
+.SE
+.KY print
+.IP "[range] p[rint] [count] [flags]"
+Display the specified lines.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+Affected by the
+.OP list
+and
+.OP number
+option.
+.SE
+.KY put
+.IP "[line] pu[t] [buffer]"
+Append buffer contents to the current line.
+If a buffer is specified, its contents are appended to the line,
+otherwise, the contents of the unnamed buffer are used.
+.SS
+.SP Line:
+Set to the line after the current line.
+.SP Options:
+None.
+.SE
+.KY quit
+.IP "q[uit][!]"
+End the editing session.
+If the file has been modified since the last complete write, the
+.CO quit
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command.
+.sp
+If there are more files to edit, the
+.CO quit
+command will fail.
+Appending a
+.QT !
+character to the command name or entering two
+.CO quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY read
+.IP "[line] r[ead][!] [file]"
+Read a file.
+A copy of the specified file is appended to the line.
+If
+.LI line
+is 0, the copy is inserted at the beginning of the file.
+If no file is specified, the current file is read; if there is no
+current file, then
+.LI file
+becomes the current file.
+If there is no current file and no
+.LI file
+is specified, then the
+.CO read
+command will fail.
+.sp
+If
+.LI file
+is preceded by a
+.QT !
+character,
+.LI file
+is treated as if it were a shell command, and passed to the program
+named by the
+.OP shell
+edit option.
+The standard and standard error outputs of that command are read into
+the file after the specified line.
+The special meaning of the
+.QT !
+character can be overridden by escaping it with a backslash
+.PQ \e
+character.
+.SS
+.SP Line:
+When executed from
+.CO ex ,
+the current line is set to the last line read.
+When executed from
+.CO vi ,
+the current line is set to the first line read.
+.SP Options:
+None.
+.SE
+.KY recover
+.IP "rec[over] file"
+Recover
+.LI file
+if it was previously saved.
+If no saved file by that name exists, the
+.CO recover
+command behaves equivalently to the
+.CO edit
+command.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+None.
+.SE
+.KY resize
+.IP "res[ize] [+|-]size"
+.CO Vi
+mode only.
+Grow or shrink the current screen.
+If
+.LI size
+is a positive, signed number, the current screen is grown by that many lines.
+If
+.LI size
+is a negative, signed number, the current screen is shrunk by that many lines.
+If
+.LI size
+is not signed, the current screen is set to the specified
+.LI size .
+Applicable only to split screens.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY rewind
+.IP "rew[ind][!]"
+Rewind the argument list.
+If the current file has been modified since the last complete write,
+the
+.CO rewind
+command will fail.
+This check may be overridden by appending the
+.QT !
+character to the command.
+.sp
+Otherwise, the current file is set to the first file in the argument
+list.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY set
+.IP "se[t] [option[=[value]] ...] [nooption ...] [option? ...] [all]"
+Display or set editor options.
+When no arguments are specified, the editor option
+.OP term ,
+and any editor options whose values have been changed from the
+default settings are displayed.
+If the argument
+.LI all
+is specified, the values of all of editor options are displayed.
+.sp
+Specifying an option name followed by the character
+.QT ?
+causes the current value of that option to be displayed.
+The
+.QT ?
+can be separated from the option name by whitespace characters.
+The
+.QT ?
+is necessary only for Boolean valued options.
+Boolean options can be given values by the form
+.QT "set option"
+to turn them on, or
+.QT "set nooption"
+to turn them off.
+String and numeric options can be assigned by the form
+.QT "set option=value" .
+Any whitespace characters in strings can be included literally by preceding
+each with a backslash.
+More than one option can be set or listed by a single set command,
+by specifying multiple arguments, each separated from the next by
+whitespace characters.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY shell
+.IP "sh[ell]"
+Run the shell program.
+The program named by the
+.OP shell
+option is run with a
+.b \-i
+(for interactive) flag.
+Editing is resumed when that program exits.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP shell
+option.
+.SE
+.KY source
+.IP "so[urce] file"
+Read and execute
+.CO ex
+commands from a file.
+.CO Source
+commands may be nested.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY substitute
+.IP "[range] s[ubstitute] [/pattern/replace/] [options] [count] [flags]"
+.KY &
+.Ip "[range] & [options] [count] [flags]"
+.KY ~
+.Ip "[range] ~ [options] [count] [flags]"
+Make substitutions.
+Replace the first instance of
+.LI pattern
+with the string
+.LI replace
+on the specified line(s).
+If the
+.QT /pattern/repl/
+argument is not specified, the
+.QT /pattern/repl/
+from the previous
+.CO substitute
+command is used.
+Any character other than an alphabetic, numeric, <blank> or backslash
+character may be used as the delimiter.
+.sp
+If
+.LI options
+includes the letter
+.QT c
+(confirm), you will be prompted for confirmation before each replacement
+is done.
+An affirmative response (in English, a
+.QT y
+character) causes the replacement to be made.
+A quit response (in English, a
+.QT q
+character) causes the
+.CO substitute
+command to be terminated.
+Any other response causes the replacement not to be made, and the
+.CO substitute
+command continues.
+If
+.LI options
+includes the letter
+.QT g
+(global), all nonoverlapping instances of
+.LI pattern
+in the line are replaced.
+.sp
+The
+.CO &
+version of the command is the same as not specifying a pattern
+or replacement string to the
+.CO substitute
+command, and the
+.QT &
+is replaced by the pattern and replacement information from the
+previous substitute command.
+.sp
+The
+.CO ~
+version of the command is the same as
+.CO &
+and
+.CO s ,
+except that the search pattern used is the last RE used in
+.i any
+command, not necessarily the one used in the last
+.CO substitute
+command.
+.sp
+For example, in the sequence
+.ft C
+.(b
+s/red/blue/
+/green
+~
+.)b
+.ft R
+the
+.QT ~
+is equivalent to
+.QT s/green/blue/ .
+.sp
+The
+.CO substitute
+command may be interrupted, using the terminal interrupt character.
+All substitutions completed before the interrupt are retained.
+.SS
+.SP Line:
+Set to the last line upon which a substitution was made.
+.SP Options:
+Affected by the
+.OP ignorecase
+and
+.OP magic
+option.
+.SE
+.KY suspend
+.IP "su[spend][!]"
+.KY stop
+.Ip "st[op][!]"
+.KY <control-Z>
+.Ip <control-Z>
+Suspend the edit session.
+Appending a
+.QT !
+character to these commands turns off the
+.OP autowrite
+option for the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY tag
+.IP "ta[g][!] tagstring"
+Edit the file containing the specified tag.
+If the tag is in a different file, then the new file is edited.
+If the current file has been modified since the last complete write,
+the
+.CO tag
+command will fail.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.sp
+The
+.CO tag
+command searches for
+.LI tagstring
+in the tags file(s) specified by the
+.Op tags
+option.
+(See
+.XR ctags 1
+for more information on tags files.)
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Tag ,
+while in
+.CO vi
+mode, will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite ,
+.OP taglength ,
+.OP tags
+and
+.OP writeany
+options.
+.SE
+.KY tagnext
+.IP "tagn[ext][!]"
+Edit the file containing the next context for the current tag.
+If the context is in a different file, then the new file is edited.
+If the current file has been modified since the last complete write,
+the
+.CO tagnext
+command will fail.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Tagnext ,
+while in
+.CO vi
+mode, will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY tagpop
+.IP "tagp[op][!] [file | number]"
+Pop to the specified tag in the tags stack.
+If neither
+.LI file
+or
+.LI number
+is specified, the
+.CO tagpop
+command pops to the most recent entry on the tags stack.
+If
+.LI file
+or
+.LI number
+is specified, the
+.CO tagpop
+command pops to the most recent entry in the tags stack for that file,
+or numbered entry in the tags stack, respectively.
+(See the
+.CO display
+command for information on displaying the tags stack.)
+.sp
+If the file has been modified since the last complete write, the
+.CO tagpop
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command name.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY tagprev
+.IP "tagp[rev][!]"
+Edit the file containing the previous context for the current tag.
+If the context is in a different file, then the new file is edited.
+If the current file has been modified since the last complete write,
+the
+.CO tagprev
+command will fail.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Tagprev ,
+while in
+.CO vi
+mode, will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY tagtop
+.IP "tagt[op][!]"
+Pop to the least recent tag on the tags stack, clearing the tags stack.
+.sp
+If the file has been modified since the last complete write, the
+.CO tagtop
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command name.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY unabbrev
+.IP "una[bbrev] lhs"
+Delete an abbreviation.
+Delete
+.LI lhs
+from the current list of abbreviations.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY undo
+.IP "u[ndo]"
+Undo the last change made to the file.
+Changes made by
+.CO global ,
+.CO v ,
+.CO visual
+and map sequences are considered a single command.
+If repeated, the
+.CO u
+command alternates between these two states, and is its own inverse.
+.SS
+.SP Line:
+Set to the last line modified by the command.
+.SP Options:
+None.
+.SE
+.KY unmap
+.IP "unm[ap][!] lhs"
+Unmap a mapped string.
+Delete the command mode map definition for
+.LI lhs .
+If a
+.QT !
+character is appended to the command name, delete the text input mode
+map definition instead.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY version
+.IP "ve[rsion]"
+Display the version of the
+.CO ex/vi
+editor.
+.KY visual
+.IP "[line] vi[sual] [type] [count] [flags]"
+.CO Ex
+mode only.
+Enter
+.CO vi .
+The
+.LI type
+is optional, and can be
+.QT \- ,
+.QT +
+or
+.QT ^ ,
+as in the
+.CO ex
+.CO z
+command, to specify the position of the specified line in the screen
+window.
+(The default is to place the line at the top of the screen window.)
+A
+.LI count
+specifies the number of lines that will initially be displayed.
+(The default is the value of the
+.OP window
+editor option.)
+.SS
+.SP Line:
+Unchanged unless
+.LI line
+is specified, in which case it is set to that line.
+.SP Options:
+None.
+.SE
+.KY visual
+.IP "vi[sual][!] [+cmd] [file]"
+.CO Vi
+mode only.
+Edit a new file.
+Identical to the
+.QT "edit[!] [+cmd] [file]"
+command.
+.sp
+Capitalizing the first letter of the command, i.e.
+.CO Visual ,
+will edit the file in a new screen.
+In this case, any modifications to the current file are ignored.
+.KY viusage
+.IP "viu[sage] [command]"
+Display usage for a
+.CO vi
+command.
+If
+.LI command
+is specified, a usage statement for that command is displayed.
+Otherwise, usage statements for all
+.CO vi
+commands are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY write
+.IP "[range] w[rite][!] [>>] [file]"
+.Ip "[range] w[rite] [!] [file]"
+.KY wn
+.Ip "[range] wn[!] [>>] [file]"
+.KY wq
+.Ip "[range] wq[!] [>>] [file]"
+Write the file.
+The specified lines (the entire file, if no range is given) is written
+to
+.LI file .
+If
+.LI file
+is not specified, the current pathname is used.
+If
+.LI file
+is specified, and it exists, or if the current pathname was set using the
+.CO file
+command, and the file already exists, these commands will fail.
+Appending a
+.QT !
+character to the command name will override this check and the write
+will be attempted, regardless.
+.sp
+Specifying the optional
+.QT >>
+string will cause the write to be appended to the file, in which case
+no tests are made for the file already existing.
+.sp
+If the file is preceded by a
+.QT !
+character, the program named by the shell edit option is
+invoked with file as its second argument, and the specified
+lines are passed as standard input to that command.
+The
+.QT !
+in this usage must be separated from command name by at least one
+whitespace character.
+The special meaning of the
+.QT !
+may be overridden by escaping it with a backslash
+.PQ \e
+character.
+.sp
+The
+.CO wq
+version of the write command will exit the editor after writing the file,
+if there are no further files to edit.
+Appending a
+.QT !
+character to the command name or entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.sp
+The
+.CO wn
+version of the write command will move to the next file after writing
+the file, unless the write fails.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP readonly
+and
+.OP writeany
+options.
+.SE
+.KY xit
+.IP "[range] x[it][!] [file]"
+Write the file if it has been modified.
+The specified lines are written to
+.LI file ,
+if the file has been modified since the last complete write to any
+file.
+If no
+.LI range
+is specified, the entire file is written.
+.sp
+The
+.CO xit
+command will exit the editor after writing the file,
+if there are no further files to edit.
+Appending a
+.QT !
+character to the command name or entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP readonly
+and
+.OP writeany
+options.
+.SE
+.KY yank
+.IP "[range] ya[nk] [buffer] [count]"
+Copy the specified lines to a buffer.
+If no buffer is specified, the unnamed buffer is used.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY z
+.IP "[line] z [type] [count] [flags]"
+Adjust the window.
+If no
+.LI type
+is specified, then
+.LI count
+lines following the specified line are displayed.
+The default
+.LI count
+is the value of the
+.OP window
+option.
+The
+.LI type
+argument changes the position at which
+.LI line
+is displayed on the screen by changing the number of lines
+displayed before and after
+.LI line .
+The following
+.LI type
+characters may be used:
+.SS
+.SP \-
+Place the line at the bottom of the screen.
+.SP +
+Place the line at the top of the screen.
+.SP \&.
+Place the line in the middle of the screen.
+.SP ^
+Write out count lines starting
+.LI "count * 2"
+lines before
+.LI line ;
+the net effect of this is that a
+.QT z^
+command following a
+.CO z
+command writes the previous page.
+.SP =
+Center
+.LI line
+on the screen with a line of hyphens displayed immediately before and
+after it.
+The number of preceding and following lines of text displayed are
+reduced to account for those lines.
+.SE
+.SS
+.SP Line:
+Set to the last line displayed, with the exception of the
+.Dq Li \&=
+.LI type ,
+where the current line is set to the line specified by the command.
+.SP Options:
+Affected by the
+.OP scroll
+option.
+.SE
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/index.so b/contrib/nvi/docs/USD.doc/vi.ref/index.so
new file mode 100644
index 000000000000..4c3acb6e4f04
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/index.so
@@ -0,0 +1,260 @@
+! 21, 42
+"" 42
+# 22, 43
+$ 22
+% 22
+& 23, 51
+'<character> 23
+( 23
+) 24
+* 43
++ 19
+, 24
+. 24
+/RE/ 25
+0 25
+0<control-D> 38
+: 26
+; 26
+< 26, 43
+<carriage-return> 14
+<control-A> 17
+<control-B> 17
+<control-D> 17, 38, 42
+<control-E> 18
+<control-F> 18
+<control-G> 18
+<control-H> 18, 39
+<control-J> 19
+<control-L> 19
+<control-M> 19
+<control-N> 19
+<control-P> 19
+<control-R> 19
+<control-T> 19, 38
+<control-U> 20
+<control-W> 20, 39
+<control-X> 39
+<control-Y> 20
+<control-Z> 20, 52
+<control-]> 20
+<control-^> 21
+<end-of-file> 40, 42
+<erase> 39
+<escape> 20, 39
+<interrupt> 12, 37, 39
+<line erase> 39
+<literal-next> 12, 39
+<newline> 14
+<nul> 38
+<space> 21
+<word erase> 39
+= 43
+> 26, 43
+?RE? 25
+@ 26, 43
+A 27
+B 27
+C 27
+D 27
+E 28
+F 28
+G 28
+H 28
+I 28
+J 29
+L 29
+M 29
+N 25
+O 29
+P 29
+Q 30
+R 30
+S 30
+T 30
+U 30
+W 31
+X 31
+Y 31
+ZZ 31
+[[ 31
+\- 24
+]] 32
+^ 32
+^<control-D> 38
+_ 32
+`<character> 23
+a 32
+abbrev 43
+alternate pathname 13
+altwerase 56
+append 44
+args 44
+autoindent 56
+autoprint 56
+autowrite 57
+b 32
+backup 57
+beautify 57
+bg 44
+bigword 16
+buffer 13
+c 33
+cd 45
+cdpath 57
+cedit 57
+change 45
+chdir 45
+columns 58
+comment 58
+copy 45
+count 16, 41
+cscope 45
+current pathname 12
+d 33
+delete 45
+directory 58
+display 45
+e 33
+edcompatible 58
+edit 46
+errorbells 58
+escapetime 58
+exrc 58
+extended 58
+exusage 46
+f 33
+fg 46
+file 41, 46
+filec 58
+flags 41
+flash 59
+global 47
+hardtabs 59
+help 47
+i 33
+iclower 59
+ignorecase 59
+insert 47
+j 19
+join 47
+k 19, 48
+keytime 59
+l 21
+leftright 59
+line 41
+lines 59
+lisp 59
+list 48, 59
+lock 59
+m 34
+magic 60
+map 48
+mark 48
+matchtime 60
+mesg 60
+mkexrc 49
+modelines 60
+motion 15
+move 48
+msgcat 60
+n 25
+next 49
+noprint 60
+number 43, 61
+o 34
+octal 61
+open 49, 61
+optimize 61
+p 34
+paragraph 16
+paragraphs 61
+path 61
+preserve 49
+previous 49
+previous context 15
+print 50, 61
+prompt 61
+put 50
+quit 50
+r 34
+range 41
+read 50
+readonly 61
+recdir 62
+recover 50
+redraw 62
+remap 62
+report 62
+resize 50
+rewind 51
+ruler 62
+s 34
+scroll 62
+searchincr 62
+section 17
+sections 63
+secure 63
+sentence 17
+set 51
+shell 51, 63
+shellmeta 63
+shiftwidth 63
+showmatch 63
+showmode 63
+sidescroll 63
+slowopen 63
+source 51
+sourceany 64
+stop 52
+substitute 51
+suspend 52
+t 35, 45
+tabstop 64
+tag 52
+taglength 64
+tagnext 52
+tagpop 53
+tagprev 53
+tags 64
+tagtop 53
+term 64
+terse 64
+tildeop 64
+timeout 64
+ttywerase 64
+u 35
+unabbrev 53
+undo 53
+unmap 54
+unnamed buffer 14
+v 47
+verbose 64
+version 54
+visual 54
+viusage 54
+w 35
+w1200 64
+w300 64
+w9600 64
+warn 65
+whitespace 14
+window 65
+windowname 65
+wn 54
+word 16
+wq 54
+wraplen 65
+wrapmargin 65
+wrapscan 65
+write 54
+writeany 66
+x 35
+xit 55
+y 35
+yank 55
+z 36, 55
+{ 36
+| 36
+} 37
+~ 37, 51
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/merge.awk b/contrib/nvi/docs/USD.doc/vi.ref/merge.awk
new file mode 100644
index 000000000000..c65207c106a7
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/merge.awk
@@ -0,0 +1,16 @@
+# @(#)merge.awk 8.3 (Berkeley) 5/25/94
+#
+# merge index entries into one line per label
+$1 == prev {
+ printf ", %s", $2;
+ next;
+}
+{
+ if (NR != 1)
+ printf "\n";
+ printf "%s \t%s", $1, $2;
+ prev = $1;
+}
+END {
+ printf "\n"
+}
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/ref.so b/contrib/nvi/docs/USD.doc/vi.ref/ref.so
new file mode 100644
index 000000000000..a82c79258bb1
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/ref.so
@@ -0,0 +1,103 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)ref.so 8.9 (Berkeley) 8/17/96
+.\"
+.\"
+.\" indented paragraph, with spaces between the items, bold font
+.de IP
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.sp 1
+.nr PS \\n(ps
+.nr ps 0
+.ip "\fB\\$1\fP" \\$2
+.nr ps \\n(PS
+.br
+..
+.\" indented paragraph, no spaces between the items, bold font
+.de Ip
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.nr PS \\n(ps
+.nr ps 0
+.ns
+.ip "\fB\\$1\fP" \\$2
+.nr ps \\n(PS
+.br
+..
+.\" start nested .IP
+.de SS
+.sp
+.ba +5n
+..
+.\" end nested .IP
+.de SE
+.ba -5n
+..
+.\" nested .IP, no spaces, normal font
+.de SP
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.nr PS \\n(ps
+.nr ps 0
+.ns
+.ip "\\$1" 9n
+.nr ps \\n(PS
+..
+.\" typewriter font
+.de LI
+\&\fC\\$1\fP\\$2
+..
+.\" ex/vi names in command font
+.de EV
+\&\fB\\$1\fP/\fB\\$2\fP\\$3
+..
+.\" command names
+.de CO
+\&\fB\\$1\fP\\$2
+..
+.\" key words for index
+.de KY
+.sy echo >>index '\\$1 \\n%'
+..
+.\" option names
+.de OP
+\&\fB\\$1\fP\\$2
+..
+.\" paren quoted (typewriter font)
+.de PQ
+(\*(lq\fC\\$1\fP\*(rq)\\$2
+..
+.\" quoted bold
+.de QB
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted command
+.de QC
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted option
+.de QO
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted (no font change)
+.de QQ
+\*(lq\\$1\*(rq\\$2
+..
+.\" quoted (typewriter font)
+.de QT
+\*(lq\fC\\$1\fP\*(rq\\$2
+..
+.\" section macro to build TOC
+.de SH
+.(x
+\\$2
+.)x
+.sh \\$1 "\\$2"
+..
+.\" manual section
+.de XR
+\&\fI\\$1\fP(\\$2)\\$3
+..
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff b/contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff
new file mode 100644
index 000000000000..838412897ab0
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff
@@ -0,0 +1,1303 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)set.opt.roff 8.66 (Berkeley) 10/10/96
+.\"
+.SH 1 "Set Options"
+.pp
+There are a large number of options that may be set (or unset) to
+change the editor's behavior.
+This section describes the options, their abbreviations and their
+default values.
+.pp
+In each entry below, the first part of the tag line is the full name
+of the option, followed by any equivalent abbreviations.
+(Regardless of the abbreviations, it is only necessary to use the
+minimum number of characters necessary to distinguish an abbreviation
+from all other commands for it to be accepted, in
+.EV nex nvi .
+Historically, only the full name and the official abbreviations
+were accepted by
+.EV ex vi .
+Using full names in your startup files and environmental variables will
+probably make them more portable.)
+The part in square brackets is the default value of the option.
+Most of the options are boolean, i.e. they are either on or off,
+and do not have an associated value.
+.pp
+Options apply to both
+.CO ex
+and
+.CO vi
+modes, unless otherwise specified.
+.pp
+With a few exceptions,
+all options are settable per screen, i.e. the
+.OP tags
+option can be set differently in each screen.
+The exceptions are the
+.OP columns ,
+.OP lines ,
+.OP secure
+and
+.OP term
+options.
+Changing these options modifies the respective information for all screens.
+.pp
+For information on modifying the options or to display the options and
+their current values, see the
+.QQ set
+command in the section entitled
+.QB "Ex Commands" .
+.KY altwerase
+.IP "altwerase [off]"
+.CO Vi
+only.
+Change how
+.CO vi
+does word erase during text input.
+When this option is set, text is broken up into three classes:
+alphabetic, numeric and underscore characters, other nonblank
+characters, and blank characters.
+Changing from one class to another marks the end of a word.
+In addition, the class of the first character erased is ignored
+(which is exactly what you want when erasing pathname components).
+.KY autoindent
+.IP "autoindent, ai [off]"
+If this option is set, whenever you create a new line (using the
+.CO vi
+.CO A ,
+.CO a ,
+.CO C ,
+.CO c ,
+.CO I ,
+.CO i ,
+.CO O ,
+.CO o ,
+.CO R ,
+.CO r ,
+.CO S ,
+and
+.CO s
+commands, or the
+.CO ex
+.CO append ,
+.CO change ,
+and
+.CO insert
+commands) the new line is automatically indented to align the cursor with
+the first nonblank character of the line from which you created it.
+Lines are indented using tab characters to the extent possible (based on
+the value of the
+.OP tabstop
+option) and then using space characters as necessary.
+For commands inserting text into the middle of a line, any blank characters
+to the right of the cursor are discarded, and the first nonblank character
+to the right of the cursor is aligned as described above.
+.sp
+The indent characters are themselves somewhat special.
+If you do not enter more characters on the new line before moving to
+another line, or entering
+.LI <escape> ,
+the indent character will be deleted and the line will be empty.
+For example, if you enter
+.LI <carriage-return>
+twice in succession,
+the line created by the first
+.LI <carriage-return>
+will not have any characters in it,
+regardless of the indentation of the previous or subsequent line.
+.sp
+Indent characters also require that you enter additional erase characters
+to delete them.
+For example,
+if you have an indented line, containing only blanks, the first
+.LI <word-erase>
+character you enter will erase up to end of the indent characters,
+and the second will erase back to the beginning of the line.
+(Historically, only the
+.LI <control-D>
+key would erase the indent characters.
+Both the
+.LI <control-D>
+key and the usual erase keys work in
+.CO nvi .)
+In addition, if the cursor is positioned at the end of the indent
+characters, the keys
+.QT 0<control-D>
+will erase all of the indent characters for the current line,
+resetting the indentation level to 0.
+Similarly, the keys
+.QT ^<control-D>
+will erase all of the indent characters for the current line,
+leaving the indentation level for future created lines unaffected.
+.sp
+Finally, if the
+.OP autoindent
+option is set, the
+.CO S
+and
+.CO cc
+commands change from the first nonblank of the line to the end of the
+line, instead of from the beginning of the line to the end of the line.
+.KY autoprint
+.IP "autoprint, ap [off]"
+.CO Ex
+only.
+Cause the current line to be automatically displayed after the
+.CO ex
+commands
+.CO < ,
+.CO > ,
+.CO copy ,
+.CO delete ,
+.CO join ,
+.CO move ,
+.CO put ,
+.CO t ,
+.CO Undo ,
+and
+.CO undo .
+This automatic display is suppressed during
+.CO global
+and
+.CO v
+commands, and for any command where optional flags are used to explicitly
+display the line.
+.KY autowrite
+.IP "autowrite, aw [off]"
+If this option is set, the
+.CO vi
+.CO ! ,
+.CO ^^ ,
+.CO ^]
+and
+.CO <control-Z>
+commands, and the
+.CO ex
+.CO edit ,
+.CO next ,
+.CO rewind ,
+.CO stop ,
+.CO suspend ,
+.CO tag ,
+.CO tagpop ,
+and
+.CO tagtop
+commands automatically write the current file back to the current file name
+if it has been modified since it was last written.
+If the write fails, the command fails and goes no further.
+.sp
+Appending the optional force flag character
+.QT !
+to the
+.CO ex
+commands
+.CO next ,
+.CO rewind ,
+.CO stop ,
+.CO suspend ,
+.CO tag ,
+.CO tagpop ,
+and
+.CO tagtop
+stops the automatic write from being attempted.
+.sp
+(Historically, the
+.CO next
+command ignored the optional force flag.)
+Note, the
+.CO ex
+commands
+.CO edit ,
+.CO quit ,
+.CO shell ,
+and
+.CO xit
+are
+.i not
+affected by the
+.OP autowrite
+option.
+.sp
+The
+.OP autowrite
+option is ignored if the file is considered read-only for any reason.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms backup [QQ]
+.KY backup
+.IP "\*(ms"
+.tr QQ
+If this option is set, it specifies a pathname used as a backup file,
+and, whenever a file is written, the file's current contents are copied
+to it.
+The pathname is
+.QT \&# ,
+.QT \&%
+and
+.QT \&!
+expanded.
+.sp
+If the first character of the pathname is
+.QT \&N ,
+a version number is appended to the pathname (and the
+.QT \&N
+character is then discarded).
+Version numbers are always incremented, and each backup file will have
+a version number one greater than the highest version number currently
+found in the directory.
+.sp
+Backup files must be regular files, owned by the real user ID of the
+user running the editor, and not accessible by any other user.
+.KY beautify
+.IP "beautify, bf [off]"
+If this option is set, all control characters that are not currently being
+specially interpreted, other than
+.LI <tab> ,
+.LI <newline> ,
+and
+.LI <form-feed> ,
+are
+discarded from commands read in by
+.CO ex
+from command files, and from input text entered to
+.CO vi
+(either into the file or to the colon command line).
+Text files read by
+.EV ex vi
+are
+.i not
+affected by the
+.OP beautify
+option.
+.KY cdpath
+.IP "cdpath [environment variable CDPATH, or current directory]"
+This option is used to specify a colon separated list of directories
+which are used as path prefixes for any relative path names used as
+arguments for the
+.CO cd
+command.
+The value of this option defaults to the value of the environmental
+variable
+.LI CDPATH
+if it is set, otherwise to the current directory.
+For compatibility with the POSIX 1003.2 shell, the
+.CO cd
+command does
+.i not
+check the current directory as a path prefix for relative path names
+unless it is explicitly specified.
+It may be so specified by entering an empty string or a
+.QT \&.
+character into the
+.LI CDPATH
+variable or the option value.
+.KY cedit
+.IP "cedit [no default]"
+This option adds the ability to edit the colon command-line history.
+This option is set to a string.
+Whenever the first character of that string is entered on the colon
+command line,
+you will enter a normal editing window on the collected commands that
+you've entered on the
+.CO vi
+colon command-line.
+You may then modify and/or execute the commands.
+All normal text editing is available,
+except that you cannot use
+.CO <control-W>
+to switch to an alternate screen.
+Entering a
+.CO <carriage-return>
+will execute the current line of the screen window as an ex command in
+the context of the screen from which you created the colon command-line
+screen,
+and you will then return to that screen.
+.sp
+Because of
+.CO vi \&'s
+parsing rules, it can be difficult to set the colon command-line edit
+character to the
+.LI <escape>
+character.
+To set it to
+.LI <escape> ,
+use
+.QT "set cedit=<literal-next><escape>" .
+.sp
+If the
+.OP cedit
+edit option is set to the same character as the
+.OP filec
+edit option,
+.CO vi
+will perform colon command-line editing if the character is entered as
+the first character of the line,
+otherwise,
+.CO vi
+will perform file name expansion.
+.KY columns
+.IP "columns, co [80]"
+The number of columns in the screen.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI COLUMNS .
+See the section entitled
+.QB "Sizing the Screen"
+more information.
+.KY comment
+.IP "comment [off]"
+.CO Vi
+only.
+If the first non-empty line of the file begins with the string
+.QT # ,
+.QT /\&*
+or
+.QT // ,
+this option causes
+.CO vi
+to skip to the end of that shell, C or C++ comment (probably a
+terribly boring legal notice) before displaying the file.
+.KY directory
+.IP "directory, dir [environment variable TMPDIR, or /tmp]"
+The directory where temporary files are created.
+The environmental variable
+.LI TMPDIR
+is used as the default value if it exists, otherwise
+.LI /tmp
+is used.
+.KY edcompatible
+.IP "edcompatible, ed [off]"
+Remember the values of the
+.QQ c
+and
+.QQ g
+suffixes to the
+.CO substitute
+commands, instead of initializing them as unset for each new
+command.
+Specifying pattern and replacement strings to the
+.CO substitute
+command unsets the
+.QQ c
+and
+.QQ g
+suffixes as well.
+.KY escapetime
+.IP "escapetime [1]"
+The 10th's of a second
+.EV ex vi
+waits for a subsequent key to complete an
+.LI <escape>
+key mapping.
+.KY errorbells
+.IP "errorbells, eb [off]"
+.CO Ex
+only.
+.CO Ex
+error messages are normally presented in inverse video.
+If that is not possible for the terminal, setting this option causes
+error messages to be announced by ringing the terminal bell.
+.KY exrc
+.IP "exrc, ex [off]"
+If this option is turned on in the EXINIT environment variables,
+or the system or $HOME startup files,
+the local startup files are read,
+unless they are the same as the system or $HOME startup files or
+fail to pass the standard permission checks.
+See the section entitled
+.QB "Startup Information"
+for more information.
+.KY extended
+.IP "extended [off]"
+This option causes all regular expressions to be treated as POSIX
+1003.2 Extended Regular Expressions (which are similar to historic
+.XR egrep 1
+style expressions).
+.KY filec
+.IP "filec [no default]"
+This option adds the ability to do shell expansion when entering input
+on the colon command line.
+This option is set to a string.
+Whenever the first character of that string is entered on the colon
+command line,
+the <blank> delimited string immediately before the cursor is expanded
+as if it were followed by a
+.LI \&*
+character, and file name expansion for the
+.CO ex
+edit command was done.
+If no match is found, the screen is flashed and text input resumed.
+If a single match results, that match replaces the expanded text.
+In addition, if the single match is for a directory, a
+.LI \&/
+character is appended and file completion is repeated.
+If more than a single match results,
+any unique prefix shared by the matches replaces the expanded text,
+the matches are displayed,
+and text input resumed.
+.sp
+Because of
+.CO vi \&'s
+parsing rules, it can be difficult to set the path completion character
+to two command values,
+.LI <escape>
+and
+.LI <tab> .
+To set it to
+.LI <escape> ,
+use
+.QT "set filec=<literal-next><escape>" .
+To set it to
+.LI <tab> ,
+use
+.QT "set filec=\e<tab>" .
+.sp
+If the
+.OP cedit
+edit option is set to the same character as the
+.OP filec
+edit option,
+.CO vi
+will perform colon command-line editing if the character is entered as
+the first character of the line,
+otherwise,
+.CO vi
+will perform file name expansion.
+.KY flash
+.IP "flash [on]"
+This option causes the screen to flash instead of beeping the keyboard,
+on error, if the terminal has the capability.
+.KY hardtabs
+.IP "hardtabs, ht [8]"
+This option defines the spacing between hardware tab settings, i.e.
+the tab expansion done by the operating system and/or the terminal
+itself.
+As
+.EV nex nvi
+never writes
+.LI <tab>
+characters to the terminal, unlike historic versions of
+.EV ex vi ,
+this option does not currently have any affect.
+.KY iclower
+.IP "iclower [off]"
+The
+.OP iclower
+edit option makes all Regular Expressions case-insensitive,
+as long as an upper-case letter does not appear in the search string.
+.KY ignorecase
+.IP "ignorecase, ic [off]"
+This option causes regular expressions, both in
+.CO ex
+commands and in searches,
+to be evaluated in a case-insensitive manner.
+.KY keytime
+.IP "keytime [6]"
+The 10th's of a second
+.EV ex vi
+waits for a subsequent key to complete a key mapping.
+.KY leftright
+.IP "leftright [off]"
+.CO Vi
+only.
+This option causes the screen to be scrolled left-right to view
+lines longer than the screen, instead of the traditional
+.CO vi
+screen interface which folds long lines at the right-hand margin
+of the terminal.
+.KY lines
+.IP "lines, li [24]"
+.CO Vi
+only.
+The number of lines in the screen.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI LINES .
+See the section entitled
+.QB "Sizing the Screen"
+for more information.
+.KY lisp
+.IP "lisp [off]"
+.CO Vi
+only.
+This option changes the behavior of the
+.CO vi
+.CO ( ,
+.CO ) ,
+.CO { ,
+.CO } ,
+.CO [[
+and
+.CO ]]
+commands to match the Lisp language.
+Also, the
+.OP autoindent
+option's behavior is changed to be appropriate for Lisp.
+.sp
+.i "This option is not yet implemented."
+.KY list
+.IP "list [off]"
+This option causes lines to be displayed in an unambiguous fashion.
+Specifically, tabs are displayed as control characters, i.e.
+.QT ^I ,
+and the ends of lines are marked with a
+.QT $
+character.
+.KY lock
+.IP "lock [on]"
+This option causes the editor to attempt to get an exclusive lock on
+any file being edited, read or written.
+Reading or writing a file that cannot be locked produces a warning
+message, but no other effect.
+Editing a file that cannot be locked results in a read only edit session,
+as if the
+.OP readonly
+edit option were set.
+.KY magic
+.IP "magic [on]"
+This option is on by default.
+Turning the
+.OP magic
+option off causes all regular expression characters except for
+.QT ^
+and
+.QT $ ,
+to be treated as ordinary characters.
+To re-enable characters individually, when the
+.OP magic
+option is off,
+precede them with a backslash
+.QT \e
+character.
+See the section entitled
+.QB "Regular Expressions and Replacement Strings"
+for more information.
+.KY matchtime
+.IP "matchtime [7]"
+.CO Vi
+only.
+The 10th's of a second
+.CO vi
+pauses on the matching character when the
+.OP showmatch
+option is set.
+.KY mesg
+.IP "mesg [on]"
+This option allows other users to contact you using the
+.XR talk 1
+and
+.XR write 1
+utilities, while you are editing.
+.EV Ex vi
+does not turn message on, i.e. if messages were turned off when the
+editor was invoked, they will stay turned off.
+This option only permits you to disallow messages for the edit session.
+See the
+.XR mesg 1
+utility for more information.
+.KY msgcat
+.IP "msgcat [./]"
+This option selects a message catalog to be used to display error and
+informational messages in a specified language.
+If the value of this option ends with a '/', it is treated as the name
+of a directory that contains a message catalog
+.QT "vi_XXXX" ,
+where
+.QT XXXX
+is the value of the
+.LI LANG
+environmental variable, if it's set, or the value of the
+.LI LC_MESSAGES
+environmental variable if it's not.
+If neither of those environmental variables are set,
+or if the option doesn't end in a '/',
+the option is treated as the full path name of the message catalog to use.
+.sp
+If any messages are missing from the catalog,
+the backup text (English) is used instead.
+.sp
+See the distribution file
+.LI catalog/README
+for additional information on building and installing message catalogs.
+.KY modelines
+.IP "modelines, modeline [off]"
+If the
+.OP modelines
+option is set,
+.EV ex vi
+has historically scanned the first and last five lines of each file as
+it is read for editing, looking for any
+.CO ex
+commands that have been placed in those lines.
+After the startup information has been processed, and before the user
+starts editing the file, any commands embedded in the file are executed.
+.sp
+Commands were recognized by the letters
+.QQ e
+or
+.QQ v
+followed by
+.QQ x
+or
+.QQ i ,
+at the beginning of a line or following a tab or space character,
+and followed by a
+.QQ : ,
+an
+.CO ex
+command, and another
+.QQ : .
+.sp
+This option is a security problem of immense proportions,
+and should not be used under any circumstances.
+.sp
+.i "This option will never be implemented."
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms noprint [QQ]
+.KY noprint
+.IP "\*(ms"
+.tr QQ
+Characters that are never handled as printable characters.
+By default, the C library function
+.XR isprint 3
+is used to determine if a character is printable or not.
+This edit option overrides that decision.
+.KY number
+.IP "number, nu [off]"
+Precede each line displayed with its current line number.
+.KY octal
+.IP "octal [off]"
+Display unknown characters as octal numbers
+.PQ "\e###" ,
+instead of the default
+hexadecimal
+.PQ "\ex##" .
+.KY open
+.IP "open [on]"
+.CO Ex
+only.
+If this option is not set, the
+.CO open
+and
+.CO visual
+commands are disallowed.
+.KY optimize
+.IP "optimize, opt [on]"
+.CO Vi
+only.
+Throughput of text is expedited by setting the terminal not to do automatic
+carriage returns when printing more than one (logical) line of output,
+greatly speeding output on terminals without addressable cursors when text
+with leading white space is printed.
+.sp
+.i "This option is not yet implemented."
+.KY paragraphs
+.IP "paragraphs, para [IPLPPPQPP LIpplpipbp]"
+.CO Vi
+only.
+Define additional paragraph boundaries for the
+.CO {
+and
+.CO }
+commands.
+The value of this option must be a character string consisting
+of zero or more character pairs.
+.sp
+In the text to be edited, the character string
+.LI "<newline>.<char-pair>" ,
+(where
+.LI <char-pair>
+is one of the character pairs in the option's value)
+defines a paragraph boundary.
+For example, if the option were set to
+.LI "LaA<space>##" ,
+then all of the following additional paragraph boundaries would be
+recognized:
+.sp
+.(l
+<newline>.La
+<newline>.A<space>
+<newline>.##
+.)l
+.KY path
+.IP "path []"
+The path option can be used to specify a <colon>-separated list of
+paths, similar to the
+.LI PATH
+environment variable in the shells.
+If this option is set,
+the name of the file to be edited is not an absolute pathname,
+the first component of the filename is not
+.QT \&.
+or
+.QT \&.. ,
+and the file to be edited doesn't exist in the current directory,
+the elements of the
+.OP path
+option are sequentially searched for a file of the specified name.
+If such a file is found, it is edited.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms print [QQ]
+.KY print
+.IP "\*(ms"
+.tr QQ
+Characters that are always handled as printable characters.
+By default, the C library function
+.XR isprint 3
+is used to determine if a character is printable or not.
+This edit option overrides that decision.
+.KY prompt
+.IP "prompt [on]"
+.CO Ex
+only.
+This option causes
+.CO ex
+to prompt for command input with a
+.QT :
+character; when it is not set, no prompt is displayed.
+.KY readonly
+.IP "readonly, ro [off]"
+This option causes a force flag to be required to attempt to write the file.
+Setting this option is equivalent to using the
+.b \-R
+command line option,
+or executing the
+.CO vi
+program using the name
+.CO view .
+.sp
+The
+.OP readonly
+edit option is not usually persistent, like other edit options.
+If the
+.b \-R
+command line option is set,
+.CO vi
+is executed as
+.CO view ,
+or the
+.OP readonly
+edit option is explicitly set,
+all files edited in the screen will be marked readonly,
+and the force flag will be required to write them.
+However, if none of these conditions are true,
+or the
+.OP readonly
+edit option is explicitly unset,
+then the
+.OP readonly
+edit option will toggle based on the write permissions of the file currently
+being edited as of when it is loaded into the edit buffer.
+In other words, the
+.OP readonly
+edit option will be set if the current file lacks write permissions,
+and will not be set if the user has write permissions for the file.
+.KY recdir
+.IP "recdir [/var/tmp/vi.recover]"
+The directory where recovery files are stored.
+.sp
+If you change the value of
+.OP recdir ,
+be careful to choose a directory whose contents are not regularly
+deleted.
+Bad choices include directories in memory based filesystems,
+or
+.LI /tmp ,
+on most systems,
+as their contents are removed when the machine is rebooted.
+.sp
+Public directories like
+.LI /usr/tmp
+and
+.LI /var/tmp
+are usually safe, although some sites periodically prune old files
+from them.
+There is no requirement that you use a public directory,
+e.g. a sub-directory of your home directory will work fine.
+.sp
+Finally, if you change the value of
+.OP recdir ,
+you must modify the recovery script to operate in your chosen recovery
+area.
+.sp
+See the section entitled
+.QB "Recovery"
+for further information.
+.KY redraw
+.IP "redraw, re [off]"
+.CO Vi
+only.
+The editor simulates (using great amounts of output), an intelligent
+terminal on a dumb terminal (e.g. during insertions in
+.CO vi
+the characters to the right of the cursor are refreshed as each input
+character is typed).
+.sp
+.i "This option is not yet implemented."
+.KY remap
+.IP "remap [on]"
+If this option is set,
+it is possible to define macros in terms of other macros.
+Otherwise, each key is only remapped up to one time.
+For example, if
+.QT A
+is mapped to
+.QT B ,
+and
+.QT B
+is mapped to
+.QT C ,
+The keystroke
+.QT A
+will be mapped to
+.QT C
+if the
+.OP remap
+option is set, and to
+.QT B
+if it is not set.
+.KY report
+.IP "report [5]"
+Set the threshold of the number of lines that need to be changed or
+yanked before a message will be displayed to the user.
+For everything but the yank command, the value is the largest value
+about which the editor is silent, i.e. by default, 6 lines must be
+deleted before the user is notified.
+However, if the number of lines yanked is greater than
+.i "or equal to"
+the set value, it is reported to the user.
+.KY ruler
+.IP "ruler [off]"
+.CO Vi
+only.
+Display a row/column ruler on the colon command line.
+.KY scroll
+.IP "scroll, scr [(environment variable LINES - 1) / 2]"
+Set the number of lines scrolled by the
+.CO ex
+.CO <control-D>
+and
+.CO <end-of-file>
+commands.
+.sp
+Historically, the
+.CO ex
+.CO z
+command, when specified without a count, used two times the size of the
+scroll value; the POSIX 1003.2 standard specified the window size, which
+is a better choice.
+.KY searchincr
+.IP "searchincr [off]"
+The
+.OP searchincr
+edit option makes the search commands
+.CO \&/
+and
+.CO \&?
+incremental, i.e. the screen is updated and the cursor moves to the matching
+text as the search pattern is entered.
+If the search pattern is not found,
+the screen is beeped and the cursor remains on the colon-command line.
+Erasing characters from the search pattern backs the cursor up to the
+previous matching text.
+.KY sections
+.IP "sections, sect [NHSHH HUnhsh]"
+.CO Vi
+only.
+Define additional section boundaries for the
+.CO [[
+and
+.CO ]]
+commands.
+The
+.OP sections
+option should be set to a character string consisting of zero or
+more character pairs.
+In the text to be edited, the character string
+.LI "<newline>.<char-pair>" ,
+(where
+.LI <char-pair>
+is one of the character pairs in the option's value),
+defines a section boundary in the same manner that
+.OP paragraphs
+option boundaries are defined.
+.KY secure
+.IP "secure [off]"
+The
+.OP secure
+edit option turns off all access to external programs.
+This means that the versions of the
+.CO read
+and
+.CO write
+commands that filter text through other programs,
+the
+.CO vi
+.CO \&!
+and
+.CO <control-Z>
+commands,
+the
+.CO ex
+.CO \&! ,
+.CO script ,
+.CO shell ,
+.CO stop
+and
+.CO suspend
+commands and file name expansion will not be permitted.
+Once set,
+the
+.OP secure
+edit option may not be unset.
+.KY shell
+.IP "shell, sh [environment variable SHELL, or /bin/sh]"
+Select the shell used by the editor.
+The specified path is the pathname of the shell invoked by the
+.CO vi
+.CO !
+shell escape command and by the
+.CO ex
+.CO shell
+command.
+This program is also used to resolve any shell meta-characters in
+.CO ex
+commands.
+.\" I cannot get a double quote to print between the square brackets
+.\" to save my life. The ONLY way I've been able to get this to work
+.\" is with the .tr command.
+.tr Q"
+.ds ms shellmeta [~{[*?$`'Q\e]
+.KY shellmeta
+.IP "\*(ms"
+.tr QQ
+The set of characters that
+.CO ex
+checks for when doing file name expansion.
+If any of the specified characters are found in the file name arguments
+to the
+.CO ex
+commands,
+the arguments are expanded using the program defined by the
+.OP shell
+option.
+The default set of characters is a union of meta characters
+from the Version 7 and the Berkeley C shell.
+.KY shiftwidth
+.IP "shiftwidth, sw [8]"
+Set the autoindent and shift command indentation width.
+This width is used by the
+.OP autoindent
+option and by the
+.CO < ,
+.CO > ,
+and
+.CO shift
+commands.
+.KY showmatch
+.IP "showmatch, sm [off]"
+.CO Vi
+only.
+This option causes
+.CO vi ,
+when a
+.QT }
+or
+.QT )
+is entered, to briefly move the cursor the matching
+.QT {
+or
+.QT ( .
+See the
+.OP matchtime
+option for more information.
+.KY showmode
+.IP "showmode, smd [off]"
+.CO Vi
+only.
+This option causes
+.CO vi
+to display a string identifying the current editor mode on the colon
+command line.
+The string is preceded by an asterisk (``*'') if the file has been
+modified since it was last completely written,
+.KY sidescroll
+.IP "sidescroll [16]"
+.CO Vi
+only.
+Sets the number of columns that are shifted to the left or right,
+when
+.CO vi
+is doing left-right scrolling and the left or right margin is
+crossed.
+See the
+.OP leftright
+option for more information.
+.KY slowopen
+.IP "slowopen, slow [off]"
+This option affects the display algorithm used by
+.CO vi ,
+holding off display updating during input of new text to improve
+throughput when the terminal in use is slow and unintelligent.
+.sp
+.i "This option is not yet implemented."
+.KY sourceany
+.IP "sourceany [off]"
+If this option is turned on,
+.CO vi
+historically read startup files that were owned by someone other than
+the editor user.
+See the section entitled
+.QB "Startup Information"
+for more information.
+This option is a security problem of immense proportions,
+and should not be used under any circumstances.
+.sp
+.i "This option will never be implemented."
+.KY tabstop
+.IP "tabstop, ts [8]"
+This option sets tab widths for the editor display.
+.KY taglength
+.IP "taglength, tl [0]"
+This option sets the maximum number of characters that are considered
+significant in a tag name.
+Setting the value to 0 makes all of the characters in the tag name
+significant.
+.KY tags
+.IP "tags, tag [tags /var/db/libc.tags /sys/kern/tags]"
+Sets the list of tags files, in search order,
+which are used when the editor searches for a tag.
+.KY term
+.IP "term, ttytype, tty [environment variable TERM]"
+Set the terminal type.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI TERM .
+.KY terse
+.IP "terse [off]"
+This option has historically made editor messages less verbose.
+It has no effect in this implementation.
+See the
+.OP verbose
+option for more information.
+.KY tildeop
+.IP "tildeop [off]"
+Modify the
+.CO ~
+command to take an associated motion.
+.KY timeout
+.IP "timeout, to [on]"
+If this option is set,
+.EV ex vi
+waits for a specific period for a subsequent key to complete a key
+mapping (see the
+.OP keytime
+option).
+If the option is not set, the editor waits until enough keys are
+entered to resolve the ambiguity, regardless of how long it takes.
+.KY ttywerase
+.IP "ttywerase [off]"
+.CO Vi
+only.
+This option changes how
+.CO vi
+does word erase during text input.
+If this option is set, text is broken up into two classes,
+blank characters and nonblank characters.
+Changing from one class to another marks the end of a word.
+.KY verbose
+.IP "verbose [off]"
+.CO Vi
+only.
+.CO Vi
+historically bells the terminal for many obvious mistakes, e.g. trying
+to move past the left-hand margin, or past the end of the file.
+If this option is set, an error message is displayed for all errors.
+.KY w300
+.IP "w300 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is less than 1200 baud.
+See the
+.OP window
+option for more information.
+.KY w1200
+.IP "w1200 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is equal to 1200 baud.
+See the
+.OP window
+option for more information.
+.KY w9600
+.IP "w9600 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is greater than 1200 baud.
+See the
+.OP window
+option for more information.
+.KY warn
+.IP "warn [on]"
+.CO Ex
+only.
+This option causes a warning message to the terminal if the file has
+been modified, since it was last written, before a
+.CO !
+command.
+.KY window
+.IP "window, w, wi [environment variable LINES - 1]"
+This option determines the default number of lines in a screenful,
+as displayed by the
+.CO z
+command.
+It also determines the number of lines scrolled by the
+.CO vi
+commands
+.CO <control-B>
+and
+.CO <control-F> ,
+and the default number of lines scrolled by the
+.CO vi
+commands
+.CO <control-D>
+and
+.CO <control-U> .
+The value of window can be unrelated to the real screen size,
+although it starts out as the number of lines on the screen.
+See the section entitled
+.QB "Sizing the Screen"
+for more information.
+Setting the value of the
+.OP window
+option is the same as using the
+.b \-w
+command line option.
+.sp
+If the value of the
+.OP window
+option (as set by the
+.OP window ,
+.OP w300 ,
+.OP w1200
+or
+.OP w9600
+options) is smaller than the actual size of the screen,
+large screen movements will result in displaying only that smaller
+number of lines on the screen.
+(Further movements in that same area will result in the screen being
+filled.)
+This can provide a performance improvement when viewing different
+places in one or more files over a slow link.
+.sp
+Resetting the window size does not reset the default number of lines
+scrolled by the
+.CO <control-D>
+and
+.CO <control-U>
+commands.
+.KY windowname
+.IP "windowname [off]"
+.CO Vi
+changes the name of the editor's icon/window to the current file name
+when it's possible and not destructive, i.e.,
+when the editor can restore it to its original value on exit or when
+the icon/window will be discarded as the editor exits.
+If the
+.OP windowname
+edit option is set,
+.CO vi
+will change the icon/window name even when it's destructive and the
+icon/window name will remain after the editor exits.
+(This is the case for
+.XR xterm 1 ).
+.KY wraplen
+.IP "wraplen, wl [0]"
+This option is identical to the
+.OP wrapmargin
+option, with the exception that it specifies the number of columns
+from the
+.i left
+margin before the line splits, not the right margin.
+.sp
+If both
+.OP wraplen
+and
+.OP wrapmargin
+are set, the
+.OP wrapmargin
+value is used.
+.KY wrapmargin
+.IP "wrapmargin, wm [0]"
+.CO Vi
+only.
+If the value of the
+.OP wrapmargin
+option is non-zero,
+.CO vi
+will split lines so that they end at least that number of columns
+before the right-hand margin of the screen.
+(Note, the value of
+.OP wrapmargin
+is
+.i not
+a text length.
+In a screen that is 80 columns wide, the command
+.QT ":set wrapmargin=8"
+attempts to keep the lines less than or equal to 72 columns wide.)
+.sp
+Lines are split at the previous whitespace character closest to the
+number.
+Any trailing whitespace characters before that character are deleted.
+If the line is split because of an inserted
+.LI <space>
+or
+.LI <tab>
+character, and you then enter another
+.LI <space>
+character, it is discarded.
+.sp
+If wrapmargin is set to 0,
+or if there is no blank character upon which to split the line,
+the line is not broken.
+.sp
+If both
+.OP wraplen
+and
+.OP wrapmargin
+are set, the
+.OP wrapmargin
+value is used.
+.KY wrapscan
+.IP "wrapscan, ws [on]"
+This option causes searches to wrap around the end or the beginning
+of the file, and back to the starting point.
+Otherwise, the end or beginning of the file terminates the search.
+.KY writeany
+.IP "writeany, wa [off]"
+If this option is set, file-overwriting checks that would usually be
+made before the
+.CO write
+and
+.CO xit
+commands, or before an automatic write (see the
+.OP autowrite
+option), are not made.
+This allows a write to any file, provided the file permissions allow it.
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/spell.ok b/contrib/nvi/docs/USD.doc/vi.ref/spell.ok
new file mode 100644
index 000000000000..a7d95e35d03a
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/spell.ok
@@ -0,0 +1,414 @@
+ABC
+Amir
+Autoindent
+Autoprint
+BRE's
+Bostic
+Bourne
+CDPATH
+CSCOPE
+Cscope
+DIRS
+DOUBLEQUOTE
+Dq
+Ds
+ERE's
+EXINIT
+Englar
+Ev
+FF
+Fa
+Fg
+FindScreen
+Fl
+Foregrounding
+HUnhsh
+IPLPPPQPP
+Kirkendall
+Korn
+LC
+LIpplpipbp
+LaA
+Li
+Lowercase
+MINUSSIGN
+Makefiles
+Mayoff
+NEX
+NEXINIT
+NHSHH
+NVI
+Neville
+Nex
+Nvi
+OS
+POSIX
+Perl
+PostScript
+QQ
+RE's
+README
+RECDIR
+Reference''USD:13
+SENDMAIL
+SIGHUP
+SIGWINCH
+SQUOTE
+Se
+Std
+Std1003.2
+Sven
+Sy
+TANDARDS
+TIOCGWINSZ
+TMPDIR
+TOC
+Tagnext
+Tagprev
+Tcl
+Tk
+Todo
+USD
+USD.doc
+USD:13
+UUNET
+Unmap
+VI
+Verdoolaege
+Vi
+Vx
+Whitespace
+XOFF
+XON
+XOptions
+XXCOLUMNS
+XXXX
+XXXXXX
+XXb
+ZZ
+ab
+abbrev
+abc
+ags
+ai
+al
+altwerase
+ap
+api
+ar
+arg
+args
+att
+autoindent
+autoprint
+autowrite
+aw
+backgrounded
+backgrounding
+bbrev
+berkeley
+bf
+bg
+bigword
+bigwords
+bostic
+bp
+brev
+bsd
+bugs.current
+c2w
+carat
+cd
+cdpath
+cdy
+cedit
+changelog
+chd
+chdir
+cmd
+co
+count1
+count2
+creens
+cs
+cs.berkeley.edu
+cscope
+ctags
+cw
+db
+dbopen
+dd
+def
+di
+dir
+dit
+docs
+eE
+eFlRsv
+eFlRv
+eL
+eU
+ead
+eb
+edcompatible
+edu
+ee
+egrep
+elete
+elp
+elvis
+email
+enum
+eof
+errorbells
+esc
+escapetime
+eset
+eu
+ex.cmd.roff
+exrc
+ext
+exu
+exusage
+fcntl
+fg
+fi
+filec
+filesystem
+filesystems
+foo
+foregrounded
+foregrounding
+ftp.cs.berkeley.edu
+ftp.uu.net
+gdb
+gdb.script
+getpwent
+gs
+gzip'd
+halfbyte
+hange
+hangup
+hardtabs
+ht
+html
+http
+ic
+iclower
+ifdef
+ignorecase
+ile
+ind
+initially
+ious
+ir
+iscntrl
+isprint
+ist
+ize
+keystroke
+keystrokes
+keytime
+leftright
+lhs
+li
+lib
+libc
+libc.tags
+lineNum
+lineNumber
+lobal
+lowercase
+lp
+luR
+matchtime
+mber
+mesg
+meta
+mk
+mkexrc
+modeful
+modeline
+modelines
+ms
+msgcat
+ndo
+nex
+nexrc
+nk
+nomagic
+nonblank
+nonoverlapping
+nooption
+noprint
+nsert
+nul
+nvi
+nvi.tar.Z
+nvi.tar.z
+nz
+oin
+onnections
+op
+ove
+para
+pathname
+pathnames
+pe
+perl
+perld
+ppend
+prev
+pu
+py
+rc
+rc.local
+readonly
+rec
+recdir
+recfile
+recover.XXXX
+recover.XXXXXX
+recover.c
+recover.script
+redist
+redistributable
+reimplementations
+remapmax
+remapped
+repl
+res
+rew
+rhs
+rint
+ript
+rk
+rl
+ro
+roff
+rsion
+sc
+sccs
+scr
+screeen
+screenId
+se
+searchincr
+sendmail
+set.opt.roff
+settable
+setuid
+sh
+shareware
+shellmeta
+shiftwidth
+showmatch
+showmode
+sidescroll
+slowopen
+sm
+smd
+sourceany
+sp
+spell.ok
+ssg
+st
+su
+sual
+svi
+sw
+ta
+tabstop
+taglength
+tagn
+tagnext
+tagp
+tagpop
+tagprev
+tagstring
+tagt
+tagtop
+tc
+tcl
+tclproc
+terminfo
+th
+th's
+tildeop
+tl
+tmp
+toolchest
+tpath
+tr
+ts
+ttytype
+ttywerase
+uR
+ubstitute
+ucb
+uffers
+uit
+una
+unabbrev
+unescaped
+unm
+unmap
+unsets
+uppercase
+urce
+usr
+uunet
+v
+var
+ve
+vi
+vi.0.ps
+vi.0.txt
+vi.1
+vi.XXXX
+vi.XXXXXX
+vi.cmd.roff
+vi.exrc
+vi.recover
+viAppendLine
+viDelLine
+viEndScreen
+viFindScreen
+viGetCursor
+viGetLine
+viGetMark
+viGetOpt
+viInsertLine
+viLastLine
+viMapKey
+viMsg
+viNewScreen
+viSetCursor
+viSetLine
+viSetMark
+viSetOpt
+viSwitchScreen
+viUnmMapKey
+vibackup
+virecovery
+viu
+viusage
+wa
+whitespace
+wi
+windowname
+wl
+wm
+wn
+wq
+wraplen
+wrapmargin
+wrapscan
+writeany
+ws
+www
+xaw
+xit
+xterm
+ya
+yy
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff b/contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff
new file mode 100644
index 000000000000..12030cd52b08
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff
@@ -0,0 +1,3085 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)vi.cmd.roff 8.49 (Berkeley) 8/17/96
+.\"
+.SH 1 "Vi Description"
+.pp
+.CO Vi
+takes up the entire screen to display the edited file,
+except for the bottom line of the screen.
+The bottom line of the screen is used to enter
+.CO ex
+commands, and for
+.CO vi
+error and informational messages.
+If no other information is being displayed,
+the default display can show the current cursor row and cursor column,
+an indication of whether the file has been modified,
+and the current mode of the editor.
+See the
+.OP ruler
+and
+.OP showmode
+options for more information.
+.pp
+Empty lines do not have any special representation on the screen,
+but lines on the screen that would logically come after the end of
+the file are displayed as a single tilde
+.PQ ~
+character.
+To differentiate between empty lines and lines consisting of only
+whitespace characters, use the
+.OP list
+option.
+Historically, implementations of
+.CO vi
+have also displayed some lines as single asterisk
+.PQ @
+characters.
+These were lines that were not correctly displayed, i.e. lines on the
+screen that did not correspond to lines in the file, or lines that did
+not fit on the current screen.
+.CO Nvi
+never displays lines in this fashion.
+.pp
+.CO Vi
+is a modeful editor, i.e. it has two modes,
+.QQ command
+mode and
+.QQ "text input"
+mode.
+When
+.CO vi
+first starts, it is in command mode.
+There are several commands that change
+.CO vi
+into text input mode.
+The
+.LI <escape>
+character is used to resolve the text input into the file,
+and exit back into command mode.
+In
+.CO vi
+command mode, the cursor is always positioned on the last column of
+characters which take up more than one column on the screen.
+In
+.CO vi
+text insert mode, the cursor is positioned on the first column of
+characters which take up more than one column on the screen.
+.pp
+When positioning the cursor to a new line and column,
+the type of movement is defined by the distance to the new cursor position.
+If the new position is close,
+the screen is scrolled to the new location.
+If the new position is far away,
+the screen is repainted so that the new position is on the screen.
+If the screen is scrolled,
+it is moved a minimal amount,
+and the cursor line will usually appear at the top or bottom of the screen.
+If the screen is repainted,
+the cursor line will appear in the center of the screen,
+unless the cursor is sufficiently close to the beginning or end of the file
+that this isn't possible.
+If the
+.OP leftright
+option is set, the screen may be scrolled or repainted in a horizontal
+direction as well as in a vertical one.
+.pp
+A major difference between the historical
+.CO vi
+presentation and
+.CO nvi
+is in the scrolling and screen oriented position commands,
+.CO <control-B> ,
+.CO <control-D> ,
+.CO <control-E> ,
+.CO <control-F> ,
+.CO <control-U> ,
+.CO <control-Y> ,
+.CO H ,
+.CO L
+and
+.CO M .
+In historical implementations of
+.CO vi ,
+these commands acted on physical (as opposed to logical, or screen)
+lines.
+For lines that were sufficiently long in relation to the size of the
+screen, this meant that single line scroll commands might repaint the
+entire screen, scrolling or screen positioning commands might not change
+the screen or move the cursor at all, and some lines simply could not
+be displayed, even though
+.CO vi
+would edit the file that contained them.
+In
+.CO nvi ,
+these commands act on logical, i.e. screen lines.
+You are unlikely to notice any difference unless you are editing files
+with lines significantly longer than a screen width.
+.pp
+.CO Vi
+keeps track of the currently
+.QQ "most attractive"
+cursor position.
+Each command description (for commands that alter the current cursor
+position),
+specifies if the cursor is set to a specific location in the line,
+or if it is moved to the
+.QQ "most attractive cursor position" .
+The latter means that the cursor is moved to the cursor position that
+is horizontally as close as possible to the current cursor position.
+If the current line is shorter than the cursor position
+.CO vi
+would select, the cursor is positioned on the last character in the line.
+(If the line is empty, the cursor is positioned on the first column
+of the line.)
+If a command moves the cursor to the most attractive position,
+it does not alter the current cursor position, and a subsequent
+movement will again attempt to move the cursor to that position.
+Therefore, although a movement to a line shorter than the currently
+most attractive position will cause the cursor to move to the end of
+that line, a subsequent movement to a longer line will cause the
+cursor to move back to the most attractive position.
+.pp
+In addition, the
+.CO $
+command makes the end of each line the most attractive cursor position
+rather than a specific column.
+.pp
+Each
+.CO vi
+command described below notes where the cursor ends up after it is
+executed.
+This position is described in terms of characters on the line, i.e.
+.QQ "the previous character" ,
+or,
+.QQ "the last character in the line" .
+This is to avoid needing to continually refer to on what part of the
+character the cursor rests.
+.pp
+The following words have special meaning for
+.CO vi
+commands.
+.KY "previous context"
+.IP "previous context"
+The position of the cursor before the command which caused the
+last absolute movement was executed.
+Each
+.CO vi
+command described in the next section that is considered an
+absolute movement is so noted.
+In addition, specifying
+.i any
+address to an
+.CO ex
+command is considered an absolute movement.
+.KY "motion"
+.IP "motion"
+A second
+.CO vi
+command can be used as an optional trailing argument to the
+.CO vi
+.CO \&< ,
+.CO \&> ,
+.CO \&! ,
+.CO \&c ,
+.CO \&d ,
+.CO \&y ,
+and (depending on the
+.OP tildeop
+option)
+.CO \&~
+commands.
+This command indicates the end of the region of text that's affected by
+the command.
+The motion command may be either the command character repeated (in
+which case it means the current line) or a cursor movement command.
+In the latter case, the region affected by the command is from the
+starting or stopping cursor position which comes first in the file,
+to immediately before the starting or stopping cursor position which
+comes later in the file.
+Commands that operate on lines instead of using beginning and ending
+cursor positions operate on all of the lines that are wholly or
+partially in the region.
+In addition, some other commands become line oriented depending on
+where in the text they are used.
+The command descriptions below note these special cases.
+.sp
+The following commands may all be used as motion components for
+.CO vi
+commands:
+.sp
+.ne 12v
+.ft C
+.TS
+r r r r.
+<control-A> <control-H> <control-J> <control-M>
+<control-N> <control-P> <space> $
+% '<character> ( )
++ , - /
+0 ; ? B
+E F G H
+L M N T
+W [[ ]] ^
+\&_ `<character> b e
+f h j k
+l n t w
+{ | }
+.TE
+.ft R
+.sp
+The optional count prefix available for some of the
+.CO vi
+commands that take motion commands,
+or the count prefix available for the
+.CO vi
+commands that are used as motion components,
+may be included and is
+.i always
+considered part of the motion argument.
+For example, the commands
+.QT c2w
+and
+.QT 2cw
+are equivalent, and the region affected by the
+.CO c
+command is two words of text.
+In addition,
+if the optional count prefix is specified for both the
+.CO vi
+command and its motion component,
+the effect is multiplicative and is considered part of the motion argument.
+For example, the commands
+.QT 4cw
+and
+.QT 2c2w
+are equivalent, and the region affected by the
+.CO c
+command is four words of text.
+.KY "count"
+.IP "count"
+A positive number used as an optional argument to most commands,
+either to give a size or a position (for display or movement commands),
+or as a repeat count (for commands that modify text).
+The count argument is always optional and defaults to 1 unless otherwise
+noted in the command description.
+.sp
+When a
+.CO vi
+command synopsis shows both a
+.LI [buffer]
+and
+.LI [count] ,
+they may be presented in any order.
+.KY word
+.IP word
+Generally, in languages where it is applicable,
+.CO vi
+recognizes two kinds of words.
+First, a sequence of letters, digits and underscores,
+delimited at both ends by:
+characters other than letters, digits, or underscores,
+the beginning or end of a line, and the beginning or end of the file.
+Second, a sequence of characters other than letters, digits, underscores,
+or whitespace characters, delimited at both ends by: a letter, digit,
+underscore, or whitespace character,
+the beginning or end of a line, and the beginning or end of the file.
+For example, the characters
+.QT " !@#abc$%^ "
+contain three words:
+.QT "!@#" ,
+.QT "abc"
+and
+.QT "$%^" .
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single word.
+.KY "bigword"
+.IP "bigword"
+A set of non-whitespace characters preceded and followed by whitespace
+characters or the beginning or end of the file or line.
+For example, the characters
+.QT " !@#abc$%^ "
+contain one bigword:
+.QT "!@#abc$%^" .
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single bigword.
+.KY "paragraph"
+.IP "paragraph"
+An area of text that begins with either the beginning of a file,
+an empty line, or a section boundary, and continues until either
+an empty line, section boundary, or the end of the file.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single paragraph.
+.sp
+Additional paragraph boundaries can be defined using the
+.OP paragraphs
+option.
+.KY "section"
+.IP "section"
+An area of text that starts with the beginning of the file or a line
+whose first character is an open brace
+.PQ {
+and continues until the next section or the end of the file.
+.sp
+Additional section boundaries can be defined using the
+.OP sections
+option.
+.KY "sentence"
+.IP "sentence"
+An area of text that begins with either the beginning of the file or the
+first nonblank character following the previous sentence, paragraph, or
+section boundary and continues until the end of the file or a period
+.PQ \&.
+exclamation point
+.PQ !
+or question mark
+.PQ ?
+character,
+followed by either an end-of-line or two whitespace characters.
+Any number of closing parentheses
+.PQ ) ,
+brackets
+.PQ ] ,
+double-quote
+.PQ """"
+or single quote
+.PQ '
+characters can appear between the period, exclamation point,
+or question mark and the whitespace characters or end-of-line.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single sentence.
+.SH 1 "Vi Commands"
+.pp
+The following section describes the commands available in the command
+mode of the
+.CO vi
+editor.
+In each entry below, the tag line is a usage synopsis for the command
+character.
+In addition, the final line and column the cursor rests upon,
+and any options which affect the command are noted.
+.KY <control-A>
+.IP "[count] <control-A>"
+Search forward
+.LI count
+times for the current word.
+The current word begins at the first non-whitespace character on or
+after the current cursor position,
+and extends up to the next non-word character or the end of the line.
+The search is literal, i.e. no characters in the word have any special
+meaning in terms of Regular Expressions.
+It is an error if no matching pattern is found between the starting position
+and the end of the file.
+.sp
+The
+.CO <control-A>
+command is an absolute movement.
+The
+.CO <control-A>
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line where the word is found.
+.SP Column:
+Set to the first character of the word.
+.SP Options:
+Affected by the
+.OP ignorecase
+and
+.OP wrapscan
+options.
+.SE
+.KY <control-B>
+.IP "[count] <control-B>"
+Page backward
+.LI count
+screens.
+Two lines of overlap are maintained, if possible,
+by displaying the window starting at line
+.LI "(top_line - count * window_size) + 2" ,
+where
+.LI window_size
+is the value of the
+.OP window
+option.
+(In the case of split screens, this size is corrected to the
+current screen size.)
+It is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Set to the last line of text displayed on the screen.
+.SP Column:
+Set to the first nonblank character of the line.
+.SP Options:
+Affected by the
+.OP window
+option.
+.SE
+.KY <control-D>
+.IP "[count] <control-D>"
+Scroll forward
+.LI count
+lines.
+If
+.LI count
+is not specified, scroll forward the number of lines specified by the last
+.CO <control-D>
+or
+.CO <control-U>
+command.
+If this is the first
+.CO <control-D>
+or
+.CO <control-U>
+command,
+scroll forward half the number of lines in the screen.
+(In the case of split screens, the default scrolling distance is
+corrected to half the current screen size.)
+It is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Set to the current line plus the number of lines scrolled.
+.SP Column:
+Set to the first nonblank character of the line.
+.SP Options:
+None.
+.SE
+.KY <control-E>
+.IP "[count] <control-E>"
+Scroll forward
+.LI count
+lines, leaving the cursor on the current line and column, if possible.
+It is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the first line on the screen.
+.SP Column:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-F>
+.IP "[count] <control-F>"
+Page forward
+.LI count
+screens.
+Two lines of overlap are maintained, if possible,
+by displaying the window starting at line
+.LI "top_line + count * window_size - 2" ,
+where
+.LI window_size
+is the value of the
+.OP window
+option.
+(In the case of split screens, this size is corrected to the
+current screen size.)
+It is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Set to the first line on the screen.
+.SP Column:
+Set to the first nonblank character of the current line.
+.SP Options:
+Affected by the
+.OP window
+option.
+.SE
+.KY <control-G>
+.IP "<control-G>"
+Display the file information.
+The information includes the current pathname, the current line,
+the number of total lines in the file, the current line as a percentage
+of the total lines in the file, if the file has been modified,
+was able to be locked, if the file's name has been changed,
+and if the edit session is read-only.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-H>
+.IP "[count] <control-H>"
+.Ip "[count] h"
+Move the cursor back
+.LI count
+characters in the current line.
+It is an error if the cursor is on the first character in the line.
+.sp
+The
+.CO <control-H>
+and
+.CO h
+commands may be used as the motion component of other
+.CO vi
+commands,
+in which case any text copied into a buffer is character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the
+.LI "current - count"
+character, or, the first character in the line if
+.LI count
+is greater than or equal to the number of characters in the line
+before the cursor.
+.SP Options:
+None.
+.SE
+.KY <control-J>
+.IP "[count] <control-J>"
+.KY <control-N>
+.Ip "[count] <control-N>"
+.KY j
+.Ip "[count] j"
+Move the cursor down
+.LI count
+lines without changing the current column.
+It is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-J> ,
+.CO <control-N>
+and
+.CO j
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count .
+.SP Column:
+The most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-L>
+.IP "<control-L>"
+.KY <control-R>
+.Ip "<control-R>"
+Repaint the screen.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-M>
+.IP "[count] <control-M>"
+.KY +
+.Ip "[count] +"
+Move the cursor down
+.LI count
+lines to the first nonblank character of that line.
+It is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-M>
+and
+.CO +
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count .
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY <control-P>
+.IP "[count] <control-P>"
+.KY k
+.Ip "[count] k"
+Move the cursor up
+.LI count
+lines, without changing the current column.
+It is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO <control-P>
+and
+.CO k
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line minus
+.LI count .
+.SP Column:
+The most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-T>
+.IP "<control-T>"
+Return to the most recent tag context.
+The
+.CO <control-T>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the context of the previous tag command.
+.SP Column:
+Set to the context of the previous tag command.
+.SP Options:
+None.
+.SE
+.KY <control-U>
+.IP "[count] <control-U>"
+Scroll backward
+.LI count
+lines.
+If
+.LI count
+is not specified, scroll backward the number of lines specified by the
+last
+.CO <control-D>
+or
+.CO <control-U>
+command.
+If this is the first
+.CO <control-D>
+or
+.CO <control-U>
+command,
+scroll backward half the number of lines in the screen.
+(In the case of split screens, the default scrolling distance is
+corrected to half the current screen size.)
+It is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Set to the current line minus the amount scrolled.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY <control-W>
+.IP "<control-W>"
+Switch to the next lower screen in the window, or, to the first
+screen if there are no lower screens in the window.
+.SS
+.SP Line:
+Set to the previous cursor position in the window.
+.SP Column:
+Set to the previous cursor position in the window.
+.SP Options:
+None.
+.SE
+.KY <control-Y>
+.IP "[count] <control-Y>"
+Scroll backward
+.LI count
+lines, leaving the current line and column as is, if possible.
+It is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the last line of text displayed
+on the screen.
+.SP Column:
+Unchanged unless the current line scrolls off the screen,
+in which case it is the most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-Z>
+.IP "<control-Z>"
+Suspend the current editor session.
+If the file has been modified since it was last completely written,
+and the
+.OP autowrite
+option is set, the file is written before the editor session is
+suspended.
+If this write fails, the editor session is not suspended.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+Affected by the
+.OP autowrite
+option.
+.SE
+.KY <escape>
+.IP "<escape>"
+Execute
+.CO ex
+commands or cancel partial commands.
+If an
+.CO ex
+command is being entered (e.g.
+.CO / ,
+.CO ? ,
+.CO :
+or
+.CO ! ),
+the command is executed.
+If a partial command has been entered, e.g.
+.QT "[0-9]*" ,
+or
+.QT "[0-9]*[!<>cdy]" ,
+the command is cancelled.
+Otherwise, it is an error.
+.SS
+.SP Line:
+When an
+.CO ex
+command is being executed, the current line is set as described for
+that command.
+Otherwise, unchanged.
+.SP Column:
+When an
+.CO ex
+command is being executed, the current column is set as described for
+that command.
+Otherwise, unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-]>
+.IP "<control-]>"
+Push a tag reference onto the tag stack.
+The tags files (see the
+.OP tags
+option for more information) are searched for a tag matching the
+current word.
+The current word begins at the first non-whitespace character on or
+after the current cursor position,
+and extends up to the next non-word character or the end of the line.
+If a matching tag is found, the current file is discarded and the
+file containing the tag reference is edited.
+.sp
+If the current file has been modified since it was last completely
+written, the command will fail.
+The
+.CO <control-]>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the line containing the matching tag string.
+.SP Column:
+Set to the start of the matching tag string.
+.SP Options:
+Affected by the
+.OP tags
+and
+.OP taglength
+options.
+.SE
+.KY <control-^>
+.IP "<control-^>"
+Switch to the most recently edited file.
+.sp
+If the file has been modified since it was last completely written,
+and the
+.OP autowrite
+option is set, the file is written out.
+If this write fails, the command will fail.
+Otherwise, if the current file has been modified since it was last
+completely written, the command will fail.
+.SS
+.SP Line:
+Set to the line the cursor was on when the file was last edited.
+.SP Column:
+Set to the column the cursor was on when the file was last edited.
+.SP Options:
+Affected by the
+.OP autowrite
+option.
+.SE
+.KY <space>
+.IP "[count] <space>"
+.KY l
+.Ip "[count] l"
+Move the cursor forward
+.LI count
+characters without changing the current line.
+It is an error if the cursor is on the last character in the line.
+.sp
+The
+.CO <space>
+and
+.CO \&l
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+In addition, these commands may be used as the motion components
+of other commands when the cursor is on the last character in the
+line, without error.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the current character plus the next
+.LI count
+characters, or to the last character on the line if
+.LI count
+is greater than the number of characters in the line after the
+current character.
+.SP Options:
+None.
+.SE
+.KY !
+.IP "[count] ! motion shell-argument(s)<carriage-return>"
+Replace text with results from a shell command.
+Pass the lines specified by the
+.LI count
+and
+.LI motion
+arguments as standard input to the program named by the
+.OP shell
+option, and replace those lines with the output (both
+standard error and standard output) of that command.
+.sp
+After the motion is entered,
+.CO vi
+prompts for arguments to the shell command.
+.sp
+Within those arguments,
+.QT %
+and
+.QT #
+characters are expanded to the current and alternate pathnames,
+respectively.
+The
+.QT !
+character is expanded with the command text of the previous
+.CO !
+or
+.CO :!
+commands.
+(Therefore, the command
+.CO !<motion>!
+repeats the previous
+.CO !
+command.)
+The special meanings of
+.QT % ,
+.QT #
+and
+.QT !
+can be overridden by escaping them with a backslash.
+If no
+.CO !
+or
+.CO :!
+command has yet been executed,
+it is an error to use an unescaped
+.QT !
+character as a shell argument.
+The
+.CO !
+command does
+.i not
+do shell expansion on the strings provided as arguments.
+If any of the above expansions change the arguments the user entered,
+the command is redisplayed at the bottom of the screen.
+.sp
+.CO Vi
+then executes the program named by the
+.OP shell
+option, with a
+.b \-c
+flag followed by the arguments (which are bundled into a single argument).
+.sp
+The
+.CO !
+command is permitted in an empty file.
+.sp
+If the file has been modified since it was last completely written,
+the
+.CO !
+command will warn you.
+.SS
+.SP Line:
+The first line of the replaced text.
+.SP Column:
+The first column of the replaced text.
+.SP Options:
+Affected by the
+.OP shell
+option.
+.SE
+.KY #
+.IP "[count] # #|+|-"
+Increment or decrement the number referenced by the cursor.
+If the trailing character is a
+.LI \&+
+or
+.LI \&# ,
+the number is incremented by
+.LI count .
+If the trailing character is a
+.LI \&- ,
+the number is decremented by
+.LI count .
+.sp
+A leading
+.QT \&0X
+or
+.QT \&0x
+causes the number to be interpreted as a hexadecimal number.
+Otherwise, a leading
+.QT \&0
+causes the number to be interpreted as an octal number, unless a non-octal
+digit is found as part of the number.
+Otherwise, the number is interpreted as a decimal number, and may
+have a leading
+.LI \&+
+or
+.LI \&-
+sign.
+The current number begins at the first non-blank character at or after
+the current cursor position, and extends up to the end of the line or
+the first character that isn't a possible character for the numeric type.
+The format of the number (e.g. leading 0's, signs) is retained unless
+the new value cannot be represented in the previous format.
+.sp
+Octal and hexadecimal numbers, and the result of the operation, must fit
+into an
+.QT "unsigned long" .
+Similarly, decimal numbers and their result must fit into a
+.QT "signed long" .
+It is an error to use this command when the cursor is not positioned at
+a number.
+.sp
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first character in the cursor number.
+.SP Options:
+None.
+.SE
+.KY $
+.IP "[count] $"
+Move the cursor to the end of a line.
+If
+.LI count
+is specified, the cursor moves down
+.LI "count - 1"
+lines.
+.sp
+It is not an error to use the
+.CO $
+command when the cursor is on the last character in the line or
+when the line is empty.
+.sp
+The
+.CO $
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the cursor is at, or before the first
+nonblank character in the line, in which case it is line oriented.
+It is not an error to use the
+.CO $
+command as a motion component when the cursor is on the last character
+in the line, although it is an error when the line is empty.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count
+minus 1.
+.SP Column:
+Set to the last character in the line.
+.SP Options:
+None.
+.SE
+.KY %
+.IP %
+Move to the matching character.
+The cursor moves to the parenthesis or curly brace which
+.i matches
+the parenthesis or curly brace found at the current cursor position
+or which is the closest one to the right of the cursor on the line.
+It is an error to execute the
+.CO %
+command on a line without a parenthesis or curly brace.
+Historically, any
+.LI count
+specified to the
+.CO %
+command was ignored.
+.sp
+The
+.CO %
+command is an absolute movement.
+The
+.CO %
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting point of the region is at
+or before the first nonblank character on its line, and the ending
+point is at or after the last nonblank character on its line, in
+which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the matching character.
+.SP Column:
+Set to the matching character.
+.SP Options:
+None.
+.SE
+.KY &
+.IP "&"
+Repeat the previous substitution command on the current line.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO &
+command was ignored.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged if the cursor was on the last character in the line,
+otherwise, set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP edcompatible ,
+.OP extended ,
+.OP ignorecase
+and
+.OP magic
+options.
+.SE
+.KY SQUOTE<character>
+.IP \'<character>
+.KY `<character>
+.Ip `<character>
+Return to a context marked by the character
+.LI <character> .
+If
+.LI <character>
+is the
+.QT '
+or
+.QT `
+character, return to the previous context.
+If
+.LI <character>
+is any other character,
+return to the context marked by that character (see the
+.CO m
+command for more information).
+If the command is the
+.CO \'
+command, only the line value is restored,
+and the cursor is placed on the first nonblank character of that line.
+If the command is the
+.CO `
+command, both the line and column values are restored.
+.sp
+It is an error if the context no longer exists because of
+line deletion.
+(Contexts follow lines that are moved, or which are deleted
+and then restored.)
+.sp
+The
+.CO \'
+and
+.CO `
+commands are both absolute movements.
+They may be used as a motion component for other
+.CO vi
+commands.
+For the
+.CO \'
+command, any text copied into a buffer is line oriented.
+For the
+.CO `
+command,
+any text copied into a buffer is character oriented,
+unless it both starts and stops at the first character in the line,
+in which case it is line oriented.
+In addition, when using the
+.CO `
+command as a motion component,
+commands which move backward and started at the first character in the line,
+or move forward and ended at the first character in the line,
+are corrected to the last character of the line preceding the starting and
+ending lines, respectively.
+.SS
+.SP Line:
+Set to the line from the context.
+.SP Column:
+Set to the first nonblank character in the line, for the
+.CO \'
+command, and set to the context's column for the
+.CO `
+command.
+.SP Options:
+None.
+.SE
+.KY (
+.IP "[count] ("
+Back up
+.LI count
+sentences.
+.sp
+The
+.CO (
+command is an absolute movement.
+The
+.CO (
+command may be used as the motion component of other
+.CO vi
+commands,
+in which case any text copied into a buffer is character oriented,
+unless the starting and stopping points of the region are the first
+character in the line,
+in which case it is line oriented.
+If it is line oriented,
+the starting point of the region is adjusted to be the end of the line
+immediately before the starting cursor position.
+.SS
+.SP Line:
+Set to the line containing the beginning of the sentence.
+.SP Column:
+Set to the first nonblank character of the sentence.
+.SP Options:
+Affected by the
+.OP lisp
+option.
+.SE
+.KY )
+.IP "[count] )"
+Move forward
+.LI count
+sentences.
+.sp
+The
+.CO )
+command is an absolute movement.
+The
+.CO )
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting point of the region is the
+first character in the line, in which case it is line oriented.
+In the latter case, if the stopping point of the region is also
+the first character in the line, it is adjusted to be the end of the
+line immediately before it.
+.SS
+.SP Line:
+Set to the line containing the beginning of the sentence.
+.SP Column:
+Set to the first nonblank character of the sentence.
+.SP Options:
+Affected by the
+.OP lisp
+option.
+.SE
+.KY ,
+.IP "[count] ,"
+Reverse find character
+.LI count
+times.
+Reverse the last
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+command, searching the other way in the line,
+.LI count
+times.
+It is an error if a
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+command has not been performed yet.
+.sp
+The
+.CO ,
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character for the
+.CO F
+and
+.CO f
+commands,
+before the character for the
+.CO t
+command
+and after the character for the
+.CO T
+command.
+.SP Options:
+None.
+.SE
+.KY MINUSSIGN
+.IP "[count] \-"
+Move to the first nonblank of the previous line,
+.LI count
+times.
+.sp
+It is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO -
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line minus
+.LI count .
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY \&.
+.IP "[count] \&."
+Repeat the last
+.CO vi
+command that modified text.
+The repeated command may be a command and motion component combination.
+If
+.LI count
+is specified, it replaces
+.i both
+the count specified for the repeated command, and, if applicable, for
+the repeated motion component.
+If
+.LI count
+is not specified, the counts originally specified to the command being
+repeated are used again.
+.sp
+As a special case, if the
+.CO \.
+command is executed immediately after the
+.CO u
+command, the change log is rolled forward or backward, depending on
+the action of the
+.CO u
+command.
+.SS
+.SP Line:
+Set as described for the repeated command.
+.SP Column:
+Set as described for the repeated command.
+.SP Options:
+None.
+.SE
+.KY /RE/
+.IP "/RE<carriage-return>"
+.Ip "/RE/ [offset]<carriage-return>"
+.KY ?RE?
+.Ip "?RE<carriage-return>"
+.Ip "?RE? [offset]<carriage-return>"
+.KY N
+.Ip "N"
+.KY n
+.Ip "n"
+Search forward or backward for a regular expression.
+The commands beginning with a slash
+.PQ /
+character are forward searches, the commands beginning with a
+question mark
+.PQ ?
+are backward searches.
+.CO Vi
+prompts with the leading character on the last line of the screen
+for a string.
+It then searches forward or backward in the file for the next
+occurrence of the string, which is interpreted as a Basic Regular
+Expression.
+.sp
+The
+.CO /
+and
+.CO ?
+commands are absolute movements.
+They may be used as the motion components of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the search started and ended on
+the first column of a line, in which case it is line oriented.
+In addition, forward searches ending at the first character of a line,
+and backward searches beginning at the first character in the line,
+are corrected to begin or end at the last character of the previous line.
+(Note, forward and backward searches can occur for both
+.CO /
+and
+.CO ?
+commands, if the
+.OP wrapscan
+option is set.)
+.sp
+If an offset from the matched line is specified (i.e. a trailing
+.QT /
+or
+.QT ?
+character is followed by a signed offset), the buffer will always
+be line oriented (e.g.
+.QT /string/+0
+will always guarantee a line orientation).
+.sp
+The
+.CO N
+command repeats the previous search, but in the reverse direction.
+The
+.CO n
+command repeats the previous search.
+If either the
+.CO N
+or
+.CO n
+commands are used as motion components for the
+.CO !
+command, you will not be prompted for the text of the bang command,
+instead the previous bang command will be executed.
+.sp
+Missing RE's (e.g.
+.QT //<carriage-return> ,
+.QT /<carriage-return> ,
+.QT ??<carriage-return> ,
+or
+.QT ?<carriage-return>
+search for the last search RE, in the indicated direction.
+.sp
+Searches may be interrupted using the
+.LI <interrupt>
+character.
+.sp
+Multiple search patterns may be grouped together by delimiting
+them with semicolons and zero or more whitespace characters, e.g.
+.LI "/foo/ ; ?bar?"
+searches forward for
+.LI foo
+and then, from that location, backwards for
+.LI bar .
+When search patterns are grouped together in this manner,
+the search patterns are evaluated left to right with the
+final cursor position determined by the last search pattern.
+.sp
+It is also permissible to append a
+.CO z
+command to the search strings, e.g.
+.LI "/foo/ z."
+searches forward for the next occurrence of
+.LI foo ,
+and then positions that line in the middle of screen.
+.SS
+.SP Line:
+Set to the line in which the match occurred.
+.SP Column:
+Set to the first character of the matched string.
+.SP Options:
+Affected by the
+.OP edcompatible ,
+.OP extended ,
+.OP ignorecase ,
+.OP magic ,
+and
+.OP wrapscan
+options.
+.SE
+.KY 0
+.IP "0"
+Move to the first character in the current line.
+It is not an error to use the
+.CO 0
+command when the cursor is on the first character in the line,
+.sp
+The
+.CO 0
+command may be used as the motion component of other
+.CO vi
+commands,
+in which case it is an error if the cursor is on the first character
+in the line,
+and any text copied into a buffer is character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first character in the line.
+.SP Options:
+None.
+.SE
+.KY :
+.IP ":"
+Execute an
+.CO ex
+command.
+.CO Vi
+prompts for an
+.CO ex
+command on the last line of the screen, using a colon
+.PQ :
+character.
+The command is terminated by a
+.LI <carriage-return> ,
+.LI <newline>
+or
+.LI <escape>
+character; all of these characters may be escaped by using a
+.LI "<literal-next>"
+character.
+The command is then executed.
+.sp
+If the
+.CO ex
+command writes to the screen,
+.CO vi
+will prompt the user for a
+.LI <carriage-return>
+before continuing
+when the
+.CO ex
+command finishes.
+Large amounts of output from the
+.CO ex
+command will be paged for the user, and the user prompted for a
+.LI <carriage-return>
+or
+.LI <space>
+key to continue.
+In some cases, a quit (normally a
+.QQ q
+character) or
+.LI <interrupt>
+may be entered to interrupt the
+.CO ex
+command.
+.sp
+When the
+.CO ex
+command finishes, and the user is prompted to resume visual mode,
+it is also possible to enter another
+.QT :
+character followed by another
+.CO ex
+command.
+.SS
+.SP Line:
+The current line is set as described for the
+.CO ex
+command.
+.SP Column:
+The current column is set as described for the
+.CO ex
+command.
+.SP Options:
+Affected as described for the
+.CO ex
+command.
+.SE
+.KY ;
+.IP "[count] ;"
+Repeat the last character find
+.LI count
+times.
+The last character find is one of the
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+commands.
+It is an error if a
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+command has not been performed yet.
+.sp
+The
+.CO ;
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character for the
+.CO F
+and
+.CO f
+commands,
+before the character for the
+.CO t
+command
+and after the character for the
+.CO T
+command.
+.SP Options:
+None.
+.SE
+.KY <
+.IP "[count] < motion"
+.KY >
+.Ip "[count] > motion"
+Shift lines left or right.
+Shift the number of lines in the region specified by the
+.LI count
+and
+.LI motion
+left (for the
+.CO <
+command) or right (for the
+.CO >
+command) by the number of columns specified by the
+.OP shiftwidth
+option.
+Only whitespace characters are deleted when shifting left.
+Once the first character in the line no longer contains a whitespace
+character, the command will succeed,
+but the line will not be modified.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP shiftwidth
+option.
+.SE
+.KY @
+.IP "@ buffer"
+Execute a named buffer.
+Execute the named buffer as
+.CO vi
+commands.
+The buffer may include
+.CO ex
+commands, too, but they must be expressed as a
+.CO :
+command.
+If the buffer is line oriented,
+.LI <newline>
+characters are logically appended to each line of the buffer.
+If the buffer is character oriented,
+.LI <newline>
+characters are logically appended to all but the last line in the buffer.
+.sp
+If the buffer name is
+.QT @ ,
+or
+.QT * ,
+then the last buffer executed shall be used.
+It is an error to specify
+.QT @@
+or
+.QT @*
+if there were no previous buffer executions.
+The text of a buffer may contain a
+.CO @
+command,
+and it is possible to create infinite loops in this manner.
+(The
+.LI <interrupt>
+character may be used to interrupt the loop.)
+.SS
+.SP Line:
+The current line is set as described for the command(s).
+.SP Column:
+The current column is set as described for the command(s).
+.SP Options:
+None.
+.SE
+.KY A
+.IP "[count] A"
+Enter input mode, appending the text after the end of the line.
+If
+.LI count
+is specified, the text is repeatedly input
+.LI "count - 1"
+more times after input mode is exited.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY B
+.IP "[count] B"
+Move backward
+.LI count
+bigwords.
+Move the cursor backward to the beginning of a bigword by repeating the
+following algorithm: if the current position is at the beginning of a
+bigword or the character at the current position cannot be part of a bigword,
+move to the first character of the preceding bigword.
+Otherwise, move to the first character of the bigword at the current position.
+If no preceding bigword exists on the current line, move to the first
+character of the last bigword on the first preceding line that contains a
+bigword.
+.sp
+The
+.CO B
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY C
+.IP "[buffer] [count] C"
+Change text from the current position to the end-of-line.
+If
+.LI count
+is specified, the input text replaces from the current position to
+the end-of-line, plus
+.LI "count - 1"
+subsequent lines.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY D
+.IP "[buffer] D"
+Delete text from the current position to the end-of-line.
+.sp
+It is not an error to execute the
+.CO D
+command on an empty line.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character before the current character, or, column 1 if
+the cursor was on column 1.
+.SP Options:
+None.
+.SE
+.KY E
+.IP "[count] E"
+Move forward
+.LI count
+end-of-bigwords.
+Move the cursor forward to the end of a bigword by repeating the
+following algorithm: if the current position is the end of a
+bigword or the character at that position cannot be part of a bigword,
+move to the last character of the following bigword.
+Otherwise, move to the last character of the bigword at the current
+position.
+If no succeeding bigword exists on the current line,
+move to the last character of the first bigword on the next following
+line that contains a bigword.
+.sp
+The
+.CO E
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the last character of the word selected.
+.SP Options:
+None.
+.SE
+.KY F
+.IP "[count] F <character>"
+Search
+.LI count
+times backward through the current line for
+.LI <character> .
+.sp
+The
+.CO F
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY G
+.IP "[count] G"
+Move to line
+.LI count ,
+or the last line of the file if
+.LI count
+not specified.
+.sp
+The
+.CO G
+command is an absolute movement.
+The
+.CO \&G
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to
+.LI count ,
+if specified, otherwise, the last line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY H
+.IP "[count] H"
+Move to the screen line
+.LI "count - 1"
+lines below the top of the screen.
+.sp
+The
+.CO H
+command is an absolute movement.
+The
+.CO H
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the line
+.LI "count - 1"
+lines below the top of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY I
+.IP "[count] I"
+Enter input mode, inserting the text at the beginning of the line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+None.
+.SE
+.KY J
+.IP "[count] J"
+Join lines.
+If
+.LI count
+is specified,
+.LI count
+lines are joined; a minimum of two lines are always joined,
+regardless of the value of
+.LI count .
+.sp
+If the current line ends with a whitespace character, all whitespace
+is stripped from the next line.
+Otherwise, if the next line starts with a open parenthesis
+.PQ (
+do nothing.
+Otherwise, if the current line ends with a question mark
+.PQ ? ,
+period
+.PQ \&.
+or exclamation point
+.PQ ! ,
+insert two spaces.
+Otherwise, insert a single space.
+.sp
+It is not an error to join lines past the end of the file,
+i.e. lines that do not exist.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character after the last character of the next-to-last
+joined line.
+.SP Options:
+None.
+.SE
+.KY L
+.IP "[count] L"
+Move to the screen line
+.LI "count - 1"
+lines above the bottom of the screen.
+.sp
+The
+.CO L
+command is an absolute movement.
+The
+.CO L
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the line
+.LI "count - 1"
+lines above the bottom of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY M
+.IP " M"
+Move to the screen line in the middle of the screen.
+.sp
+The
+.CO M
+command is an absolute movement.
+The
+.CO M
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO M
+command was ignored.
+.SS
+.SP Line:
+Set to the line in the middle of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY O
+.IP "[count] O"
+Enter input mode, appending text in a new line above the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO O
+command was ignored.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY P
+.IP "[buffer] P"
+Insert text from a buffer.
+Text from the buffer (the unnamed buffer by default) is inserted
+before the current column or, if the buffer is line oriented,
+before the current line.
+.SS
+.SP Line:
+Set to the lowest numbered line insert,
+if the buffer is line oriented, otherwise unchanged.
+.SP Column:
+Set to the first nonblank character of the appended text,
+if the buffer is line oriented, otherwise, the last character
+of the appended text.
+.SP Options:
+None.
+.SE
+.KY Q
+.IP "Q"
+Exit
+.CO vi
+(or visual) mode and switch to
+.CO ex
+mode.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+No longer relevant.
+.SP Options:
+None.
+.SE
+.KY R
+.IP "[count] R"
+Enter input mode, replacing the characters in the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+If the end of the current line is reached, no more characters are
+replaced and any further characters input are appended to the line.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY S
+.IP "[buffer] [count] S"
+Substitute
+.LI count
+lines.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY T
+.IP "[count] T <character>"
+Search backward,
+.LI count
+times,
+through the current line for the character
+.i after
+the specified
+.LI <character> .
+.sp
+The
+.CO T
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character
+.i after
+the searched-for character.
+.SP Options:
+None.
+.SE
+.KY U
+.IP "U"
+Restore the current line to its state before the cursor last
+moved to it.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+The first character in the line.
+.SP Options:
+None.
+.SE
+.KY W
+.IP "[count] W"
+Move forward
+.LI count
+bigwords.
+Move the cursor forward to the beginning of a bigword by repeating the
+following algorithm: if the current position is within a bigword or the
+character at that position cannot be part of a bigword, move to the first
+character of the next bigword.
+If no subsequent bigword exists on the current line,
+move to the first character of the first bigword on the first following
+line that contains a bigword.
+.sp
+The
+.CO W
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+The line containing the word selected.
+.SP Column:
+The first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY X
+.IP "[buffer] [count] X"
+Delete
+.LI count
+characters before the cursor.
+If the number of characters to be deleted is greater than or equal to
+the number of characters to the beginning of the line, all of the
+characters before the current cursor position, to the beginning of the
+line, are deleted.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the current character minus
+.LI count ,
+or the first character if count is greater than the number of
+characters in the line before the cursor.
+.SP Options:
+None.
+.SE
+.KY Y
+.IP "[buffer] [count] Y"
+Copy (or
+.QQ yank )
+.LI count
+lines into the specified buffer.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY ZZ
+.IP "ZZ"
+Write the file and exit
+.CO vi .
+The file is only written if it has been modified since the last
+complete write of the file to any file.
+.sp
+The
+.CO ZZ
+command will exit the editor after writing the file,
+if there are no further files to edit.
+Entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY [[
+.IP "[count] [["
+Back up
+.LI count
+section boundaries.
+.sp
+The
+.CO [[
+command is an absolute movement.
+The
+.CO [[
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting position is column 0,
+in which case it is line oriented.
+.sp
+It is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Set to the previous line that is
+.LI count
+section boundaries back,
+or the first line of the file if no more section boundaries exist
+preceding the current line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP sections
+option.
+.SE
+.KY ]]
+.IP "[count] ]]"
+Move forward
+.LI count
+section boundaries.
+.sp
+The
+.CO ]]
+command is an absolute movement.
+The
+.CO ]]
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting position is column 0,
+in which case it is line oriented.
+.sp
+It is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Set to the line that is
+.LI count
+section boundaries forward,
+or to the last line of the file if no more section
+boundaries exist following the current line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP sections
+option.
+.SE
+.KY ^
+.IP "\&^"
+Move to first nonblank character on the current line.
+.sp
+The
+.CO ^
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first nonblank character of the current line.
+.SP Options:
+None.
+.SE
+.KY _
+.IP "[count] _"
+Move down
+.LI "count - 1"
+lines, to the first nonblank character.
+The
+.CO _
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.sp
+It is not an error to execute the
+.CO _
+command when the cursor is on the first character in the line.
+.SS
+.SP Line:
+The current line plus
+.LI "count - 1" .
+.SP Column:
+The first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY a
+.IP "[count] a"
+Enter input mode, appending the text after the cursor.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY b
+.IP "[count] b"
+Move backward
+.LI count
+words.
+Move the cursor backward to the beginning of a word by repeating the
+following algorithm: if the current position is at the beginning of a word,
+move to the first character of the preceding word.
+Otherwise, the current position moves to the first character of the word
+at the current position.
+If no preceding word exists on the current line, move to the first
+character of the last word on the first preceding line that contains
+a word.
+.sp
+The
+.CO b
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY c
+.IP "[buffer] [count] c motion"
+Change the region of text specified by the
+.LI count
+and
+.LI motion .
+If only part of a single line is affected, then the last character
+being changed is marked with a
+.QT $ .
+Otherwise, the region of text is deleted, and input mode is entered.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY d
+.IP "[buffer] [count] d motion"
+Delete the region of text specified by the
+.LI count
+and
+.LI motion .
+.SS
+.SP Line:
+Set to the line where the region starts.
+.SP Column:
+Set to the first character in the line after the last character in the
+region.
+If no such character exists, set to the last character before the region.
+.SP Options:
+None.
+.SE
+.KY e
+.IP "[count] e"
+Move forward
+.LI count
+end-of-words.
+Move the cursor forward to the end of a word by repeating the following
+algorithm: if the current position is the end of a word,
+move to the last character of the following word.
+Otherwise, move to the last character of the word at the current position.
+If no succeeding word exists on the current line, move to the last character
+of the first word on the next following line that contains a word.
+.sp
+The
+.CO e
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the last character of the word selected.
+.SP Options:
+None.
+.SE
+.KY f
+.IP "[count] f <character>"
+Search forward,
+.LI count
+times, through the rest of the current line for
+.LI <character> .
+.sp
+The
+.CO f
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY i
+.IP "[count] i"
+Enter input mode, inserting the text before the cursor.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY m
+.IP "m <character>"
+Save the current context (line and column) as
+.LI <character> .
+The exact position is referred to by
+.QT `<character> .
+The line is referred to by
+.QT '<character> .
+.sp
+Historically,
+.LI <character>
+was restricted to lower-case letters.
+.CO Nvi
+permits the use of any character.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY o
+.IP "[count] o"
+Enter input mode, appending text in a new line under the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO o
+command was ignored.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY p
+.IP "[buffer] p"
+Append text from a buffer.
+Text from the buffer (the unnamed buffer by default) is appended
+after the current column or, if the buffer is line oriented,
+after the current line.
+.SS
+.SP Line:
+Set to the first line appended, if the buffer is line oriented,
+otherwise unchanged.
+.SP Column:
+Set to the first nonblank character of the appended text if the buffer
+is line oriented, otherwise, the last character of the appended text.
+.SP Options:
+None.
+.SE
+.KY r
+.IP "[count] r <character>"
+Replace characters.
+The next
+.LI count
+characters in the line are replaced with
+.LI <character> .
+Replacing characters with
+.LI <newline>
+characters results in creating new, empty lines into the file.
+.sp
+If
+.LI <character>
+is
+.LI <escape> ,
+the command is cancelled.
+.SS
+.SP Line:
+Unchanged unless the replacement character is a
+.LI <newline> ,
+in which case it is set to the current line plus
+.LI "count - 1" .
+.SP Column:
+Set to the last character replaced,
+unless the replacement character is a
+.LI <newline> ,
+in which case the cursor is in column 1 of the last line inserted.
+.SP Options:
+None.
+.SE
+.KY s
+.IP "[buffer] [count] s"
+Substitute
+.LI count
+characters in the current line starting with the current character.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY t
+.IP "[count] t <character>"
+Search forward,
+.LI count
+times, through the current line for the character immediately
+.i before
+.LI <character> .
+.sp
+The
+.CO t
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character
+.i before
+the searched-for character.
+.SP Options:
+None.
+.SE
+.KY u
+.IP "u"
+Undo the last change made to the file.
+If repeated, the
+.CO u
+command alternates between these two states, and is its own inverse.
+When used after an insert that inserted text on more than one line,
+the lines are saved in the numeric buffers.
+.sp
+The
+.CO \&.
+command, when used immediately after the
+.CO u
+command, causes the change log to be rolled forward or backward,
+depending on the action of the
+.CO u
+command.
+.SS
+.SP Line:
+Set to the position of the first line changed, if the reversal affects
+only one line or represents an addition or change; otherwise, the line
+preceding the deleted text.
+.SP Column:
+Set to the cursor position before the change was made.
+.SP Options:
+None.
+.SE
+.KY w
+.IP "[count] w"
+Move forward
+.LI count
+words.
+Move the cursor forward to the beginning of a word by repeating the
+following algorithm: if the current position is at the
+beginning of a word, move to the first character of the next word.
+If no subsequent word exists on the current line, move to the first
+character of the first word on the first following line that contains
+a word.
+.sp
+The
+.CO w
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY x
+.IP "[buffer] [count] x"
+Delete
+.LI count
+characters.
+The deletion is at the current character position.
+If the number of characters to be deleted is greater than or equal to
+the number of characters to the end of the line, all of the characters
+from the current cursor position to the end of the line are deleted.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged unless the last character in the line is deleted and the cursor
+is not already on the first character in the line, in which case it is
+set to the previous character.
+.SP Options:
+None.
+.SE
+.KY y
+.IP "[buffer] [count] y motion"
+Copy (or
+.QQ yank )
+the text region specified by the
+.LI count
+and
+.LI motion ,
+into a buffer.
+.SS
+.SP Line:
+Unchanged, unless the region covers more than a single line,
+in which case it is set to the line where the region starts.
+.SP Column:
+Unchanged, unless the region covers more than a single line,
+in which case it is set to the character were the region starts.
+.SP Options:
+None.
+.SE
+.KY z
+.IP "[count1] z [count2] type"
+Redraw the screen with a window
+.LI count2
+lines long, with line
+.LI count1
+placed as specified by the
+.LI type
+character.
+If
+.LI count1
+is not specified, it defaults to the current line.
+If
+.LI count2
+is not specified, it defaults to the current window size.
+.sp
+The following
+.LI type
+characters may be used:
+.SS
+.SP +
+If
+.LI count1
+is specified, place the line
+.LI count1
+at the top of the screen.
+Otherwise, display the screen after the current screen, similarly to the
+.CO <control-F>
+command.
+.SP <carriage-return>
+Place the line
+.LI count1
+at the top of the screen.
+.SP \&.
+Place the line
+.LI count1
+in the center of the screen.
+.SP \-
+Place the line
+.LI count1
+at the bottom of the screen.
+.SP ^
+If
+.LI count1
+is specified, place the line that is at the top of the screen
+when
+.LI count1
+is at the bottom of the screen, at the bottom of the screen,
+i.e. display the screen before the screen before
+.LI count1 .
+Otherwise, display the screen before the current screen, similarly to the
+.CO <control-B>
+command.
+.SE
+.SS
+.SP Line:
+Set to
+.LI count1
+unless
+.LI count1
+is not specified and the
+.LI type
+character was either
+.QT ^
+or
+.QT + ,
+in which case it is set to the line before the first line on the
+previous screen or the line after the last line on the previous
+screen, respectively.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY {
+.IP "[count] {"
+Move backward
+.LI count
+paragraphs.
+.sp
+The
+.CO {
+command is an absolute movement.
+The
+.CO {
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting character is the first
+character on its line, in which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the beginning of the previous paragraph.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP paragraph
+option.
+.SE
+.KY |
+.IP "[count] |"
+Move to a specific
+.i column
+position on the current line.
+.sp
+The
+.CO |
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+It is an error to use the
+.CO |
+command as a motion component and for the cursor not to move.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character occupying the column position identified by
+.LI count ,
+if the position exists in the line.
+If the column length of the current line is less than
+.LI count ,
+the cursor is moved to the last character in the line.
+.SP Options:
+None.
+.SE
+.KY }
+.IP "[count] }"
+Move forward
+.LI count
+paragraphs.
+.sp
+The
+.CO }
+command is an absolute movement.
+The
+.CO }
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting character is at or
+before any nonblank characters in its line,
+in which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the beginning of the next paragraph.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP paragraph
+option.
+.SE
+.KY ~
+.IP "[count] ~"
+Reverse the case of the next
+.LI count
+character(s).
+This is the historic semantic for the
+.CO ~
+command and it is only in effect if the
+.OP tildeop
+option is not set.
+.sp
+Lowercase alphabetic characters are changed to uppercase,
+and uppercase characters are changed to lowercase.
+No other characters are affected.
+.sp
+Historically, the
+.CO ~
+command did not take an associated count, nor did it move past the
+end of the current line.
+As it had no associated motion it was difficult to change the case
+of large blocks of text.
+In
+.CO nvi ,
+if the cursor is on the last character of a line, and there are
+more lines in the file, the cursor moves to the next line.
+.sp
+It is not an error to specify a count larger than the number of
+characters between the cursor and the end of the file.
+.SS
+.SP Line:
+Set to the line of the character after
+.LI count
+characters, or, end of file.
+.SP Column:
+Set to the character after
+.LI count
+characters, or, end-of-file.
+.SP Options:
+Affected by the
+.OP tildeop
+option.
+.SE
+.KY ~
+.IP "[count] ~ motion"
+Reverse the case of the characters in a text region specified by the
+.LI count
+and
+.LI motion .
+Only in effect if the
+.OP tildeop
+option is set.
+.sp
+Lowercase characters are changed to uppercase,
+and uppercase characters are changed to lowercase.
+No other characters are affected.
+.SS
+.SP Line:
+Set to the line of the character after the last character in the region.
+.SP Column:
+Set to the character after the last character in the region.
+.SP Options:
+Affected by the
+.OP tildeop
+option.
+.SE
+.KY <interrupt>
+.IP "<interrupt>"
+Interrupt the current operation.
+Many of the potentially long-running
+.CO vi
+commands may be interrupted using the terminal interrupt character.
+These operations include searches, file reading and writing, filter
+operations and map character expansion.
+Interrupts are also enabled when running commands outside of
+.CO vi .
+.sp
+If the
+.LI <interrupt>
+character is used to interrupt while entering an
+.CO ex
+command, the command is aborted, the cursor returns to its previous
+position, and
+.CO vi
+remains in command mode.
+.sp
+Generally, if the
+.LI <interrupt>
+character is used to interrupt any
+operation, any changes made before the interrupt are left in place.
+.SS
+.SP Line:
+Dependent on the operation being interrupted.
+.SP Column:
+Dependent on the operation being interrupted.
+.SP Options:
+None.
+.SH 1 "Vi Text Input Commands"
+.pp
+The following section describes the commands available in the text
+input mode of the
+.CO vi
+editor.
+.pp
+Historically,
+.CO vi
+implementations only permitted the characters inserted on the current
+line to be erased.
+In addition, only the
+.LI <control-D>
+erase character and the
+.QT 0<control-D>
+and
+.QT ^<control-D>
+erase strings could erase autoindent characters.
+(Autoindent characters include both the characters inserted automatically
+at the beginning of an input line as well as characters inserted using the
+.LI <control-T>
+command.)
+This implementation permits erasure to continue past the beginning
+of the current line, and back to where text input mode was entered.
+In addition, autoindent characters may be erased using the standard
+erase characters.
+For the line and word erase characters, reaching the autoindent
+characters forms a
+.QQ soft
+boundary, denoting the end of the current word or line erase.
+Repeating the word or line erase key will erase the autoindent characters.
+.pp
+Historically,
+.CO vi
+always used
+.LI <control-H>
+and
+.LI <control-W>
+as character and word erase characters, respectively, regardless of
+the current terminal settings.
+This implementation accepts, in addition to these two characters,
+the current terminal characters for those operations.
+.KY <nul>
+.IP "<nul>"
+If the first character of the input is a
+.LI <nul> ,
+the previous input is replayed, as if just entered.
+.KY <control-D>
+.IP "<control-D>"
+If the previous character on the line was an autoindent character,
+erase characters to move the cursor back to the column immediately
+after the previous (1-based) column which is a multiple of the
+.OP shiftwidth
+edit option.
+This may result in any number of
+.LI <tab>
+and
+.LI <space>
+characters preceding the cursor being changed.
+.sp
+Otherwise, if the
+.OP autoindent
+option is set and the user is entering the first character in the line,
+.LI <control-D>
+is ignored.
+Otherwise, a literal
+.LI <control-D>
+character is entered.
+.KY ^<control-D>
+.IP "^<control-D>"
+If the previous character on the line was an autoindent character,
+erase all of the autoindent characters on the line.
+In addition, the autoindent level is reset to 0.
+.KY 0<control-D>
+.IP "0<control-D>"
+If the previous character on the line was an autoindent character,
+erase all of the autoindent characters on the line.
+The autoindent level is not altered.
+.KY <control-T>
+.IP "<control-T>"
+Insert sufficient
+.LI <tab>
+and
+.LI <space>
+characters to move the cursor forward to the column immediately
+after the next (1-based) column which is a multiple of the
+.OP shiftwidth
+edit option.
+This may result in any number of
+.LI <tab>
+and
+.LI <space>
+characters preceding the cursor being changed.
+.sp
+Historically,
+.CO vi
+did not permit the
+.LI <control-T>
+command to be used unless the cursor was at the first column of a new
+line or it was preceded only by autoindent characters.
+.CO Nvi
+permits it to be used at any time during insert mode.
+.KY <erase>
+.IP <erase>
+.KY <control-H>
+.Ip <control-H>
+Erase the last character.
+.KY "<literal-next>"
+.IP "<literal-next>"
+Quote the next character.
+The next character will not be mapped (see the
+.CO map
+command for more information)
+or interpreted specially.
+A carat
+.PQ ^
+character will be displayed immediately as a placeholder,
+but will be replaced by the next character.
+.KY <escape>
+.IP <escape>
+If on the colon command line, and the
+.OP filec
+edit option is set, behave as described for that option.
+Otherwise, if on the colon command line,
+execute the command.
+Otherwise, if not on the colon command line,
+resolve all text input into the file, and return to command mode.
+.KY "<line erase>"
+.IP "<line erase>"
+Erase the current line.
+.KY "<control-W>"
+.IP "<control-W>"
+.KY "<word erase>"
+.Ip "<word erase>"
+Erase the last word.
+The definition of word is dependent on the
+.OP altwerase
+and
+.OP ttywerase
+options.
+.KY "<control-X>"
+.IP "<control-X>[0-9A-Fa-f]+"
+Insert a character with the specified hexadecimal value into the text.
+The value is delimited by any non-hexadecimal character or the input
+of the maximum number of characters that can be translated into a single
+character value.
+.KY <interrupt>
+.IP "<interrupt>"
+Interrupt text input mode, returning to command mode.
+If the
+.LI <interrupt>
+character is used to interrupt inserting text into the file,
+it is as if the
+.LI <escape>
+character was used; all text input up to the interruption is
+resolved into the file.
diff --git a/contrib/nvi/docs/USD.doc/vi.ref/vi.ref b/contrib/nvi/docs/USD.doc/vi.ref/vi.ref
new file mode 100644
index 000000000000..a880c1659e11
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vi.ref/vi.ref
@@ -0,0 +1,1840 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1994, 1995, 1996
+.\" Keith Bostic. All rights reserved.
+.\"
+.\" This document may not be republished without written permission from
+.\" Keith Bostic.
+.\"
+.\" See the LICENSE file for redistribution information.
+.\"
+.\" @(#)vi.ref 8.88 (Berkeley) 10/19/96
+.\"
+.\"
+.so ref.so
+.tp
+.(l C
+.ps 12
+.ft B
+Vi/Ex Reference Manual
+.ft
+.ps
+.sp
+.i "Keith Bostic"
+.sp
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, California 94720
+.sp 1
+\*(td
+.)l
+.sp 3
+.(l C
+.i Abstract
+.)l
+.(q
+.pp
+This document is the reference guide for the 4.4BSD
+implementations of
+.EV nex nvi ,
+which are implementations of the historic Berkeley
+.EV ex vi
+editors.
+.)q
+.sp 3
+.(l C
+.i Licensing
+.)l
+.sp
+.lp
+Copyright (c) 1991, 1992, 1993, 1994
+.ti +5
+The Regents of the University of California. All Rights Reserved.
+.lp
+Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996
+.ti +5
+Keith Bostic. All Rights Reserved.
+.sp
+.pp
+The vi program is freely redistributable. You are welcome to copy,
+modify and share it with others under the conditions listed in the
+LICENSE file. If any company (not individual!) finds vi sufficiently
+useful that you would have purchased it, or if any company wishes to
+redistribute it, contributions to the authors would be appreciated.
+.bp 2
+.(l C
+.i Acknowledgements
+.)l
+.sp
+.(q
+.pp
+Bruce Englar encouraged the early development of the historic
+.EV ex vi
+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
+.EV ex vi
+work on a large number of terminals and Unix systems.
+.pp
+.CO Nvi
+is originally derived from software contributed to the University of
+California, Berkeley by Steve Kirkendall, the author of the
+.CO vi
+clone
+.CO elvis .
+.pp
+IEEE Standard Portable Operating System Interface for Computer
+Environments (POSIX) 1003.2 style Regular Expression support was
+done by Henry Spencer.
+.pp
+The curses library was originally done by Ken Arnold.
+Scrolling and reworking for
+.CO nvi
+was done by Elan Amir.
+.pp
+George Neville-Neil added the Tcl interpreter,
+and Sven Verdoolaege added the Perl interpreter.
+.pp
+Rob Mayoff added Cscope support.
+.pp
+The Institute of Electrical and Electronics Engineers has
+given us permission to reprint portions of their documentation.
+Portions of this document are reprinted and reproduced from
+IEEE Std 1003.2-1992, IEEE Standard Portable Operating
+System Interface for Computer Environments (POSIX),
+copyright 1992 by the Institute of Electrical and Electronics
+Engineers, Inc.
+.pp
+The financial support of UUNET Communications Services is gratefully
+acknowledged.
+.)q
+.sy echo -n >index
+.oh 'Vi/Ex Reference''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference'
+.bp 4
+.SH 1 Description
+.pp
+.CO Vi
+is a screen oriented text editor.
+.CO Ex
+is a line-oriented text editor.
+.CO Ex
+and
+.CO vi
+are different interfaces to the same program,
+and it is possible to switch back and forth during an edit session.
+.CO View
+is the equivalent of using the
+.b \-R
+(read-only) option of
+.CO vi .
+.pp
+This reference manual is the one provided with the
+.EV nex nvi
+versions of the
+.EV ex vi
+text editors.
+.EV Nex nvi
+are intended as bug-for-bug compatible replacements for the original
+Fourth Berkeley Software Distribution (4BSD)
+.EV ex vi
+programs.
+This reference manual is accompanied by a traditional-style manual page.
+That manual page describes the functionality found in
+.EV ex vi
+in far less detail than the description here.
+In addition, it describes the system interface to
+.EV ex vi ,
+e.g. command line options, session recovery, signals,
+environmental variables, and similar things.
+.pp
+This reference is intended for users already familiar with
+.EV ex vi .
+Anyone else should almost certainly read a good tutorial on the
+editor first.
+If you are in an unfamiliar environment,
+and you absolutely have to get work done immediately,
+see the section entitled
+.QB "Fast Startup"
+in the manual page.
+It is probably enough to get you started.
+.pp
+There are a few features in
+.EV nex nvi
+that are not found in historic versions of
+.EV ex vi .
+Some of the more interesting of those features are briefly described
+in the next section, entitled
+.QB "Additional Features" .
+For the rest of this document,
+.EV nex nvi
+is used only when it is necessary to distinguish it from the historic
+implementations of
+.EV ex vi .
+.pp
+Future versions of this software will be periodically made available
+by anonymous ftp, and can be retrieved from
+.LI ftp.cs.berkeley.edu ,
+in the directory
+.LI ucb/4bsd .
+.SH 1 "Additional Features in Nex/Nvi"
+.pp
+There are a few features in
+.EV nex nvi
+that are not found in historic versions of
+.EV ex vi .
+Some of the more interesting of these are as follows:
+.IP "8-bit clean data, large lines, files"
+.EV Nex nvi
+will edit any format file.
+Line lengths are limited by available memory,
+and file sizes are limited by available disk space.
+The
+.CO vi
+text input mode command
+.CO <control-X>
+can insert any possible character value into the text.
+.IP "Background and foreground screens"
+The
+.CO bg
+command backgrounds the current screen, and the
+.CO fg
+command foregrounds backgrounded screens.
+The
+.CO display
+command can be used to list the background screens.
+.IP "Command Editing"
+You can enter a normal editing window on the collected commands that
+you've entered on the
+.CO vi
+colon command-line,
+and then modify and/or execute the commands.
+See the
+.OP cedit
+edit option for more information.
+.IP "Displays"
+The
+.CO display
+command can be used to display the current buffers, the backgrounded
+screens, and the tags stack.
+.IP "Extended Regular Expressions"
+The
+.CO extended
+option causes Regular Expressions to be interpreted as as Extended
+Regular Expressions, (i.e. \fIegrep\fP(1) style Regular Expressions).
+.IP "File Name Completion"
+It is possible to do file name completion and file name displays when
+entering commands on the
+.CO vi
+colon command-line.
+See the
+.OP filec
+option for more information.
+.IP "Infinite undo"
+Changes made during an edit session may be rolled backward and forward.
+A
+.CO \&.
+command immediately after a
+.CO u
+command continues either forward or backward depending on whether the
+.CO u
+command was an undo or a redo.
+.IP "Left-right scrolling"
+The
+.CO leftright
+option causes
+.CO nvi
+to do left-right screen scrolling, instead of the traditional
+.CO vi
+line wrapping.
+.IP "Message Catalogs"
+It is possible to display informational and error messages in different
+languages by providing a catalog of messages.
+See the
+.OP msgcat
+option and the file
+.LI "catalog/README"
+for more information.
+.IP "Incrementing numbers"
+The
+.CO \&#
+command increments or decrements the number referenced by the cursor.
+.IP "Previous file"
+The
+.CO previous
+command edits the previous file from the argument list.
+.IP "Scripting languages"
+The
+.CO ":pe[rl] cmd" ,
+.CO ":perld[o] cmd"
+and
+.CO ":tc[l] cmd"
+commands execute Perl and Tcl/Tk commands, respectively,
+on lines from the edit buffer.
+See the
+.QB "Scripting Languages"
+section and the specific commands for more information.
+.\".IP "Shell screens"
+.\"The
+.\".CO ":sc[ript] [file ...]"
+.\"command runs a shell in the screen.
+.\"Editing is unchanged, with the exception that a \fC<carriage-return>\fP
+.\"enters the current line (stripped of any prompt) as input to the
+.\"shell.
+.IP "Split screens"
+The
+.CO Edit ,
+.CO Ex ,
+.CO Next ,
+.CO Previous ,
+.CO Tag
+and
+.CO Visual
+(in
+.CO vi
+mode) commands divide the screen into multiple editing regions and
+then perform their normal function in a new screen area.
+The
+.CO <control-W>
+command rotates between the foreground screens.
+The
+.CO resize
+command can be used to grow or shrink a particular screen.
+.IP "Tag stacks"
+Tags are now maintained in a stack.
+The
+.CO <control-T>
+command returns to the previous tag location.
+The
+.CO tagpop
+command returns to the most recent tag location by default, or,
+optionally to a specific tag number in the tag stack,
+or the most recent tag from a specified file.
+The
+.CO display
+command can be used to list the tags stack.
+The
+.CO tagtop
+command returns to the top of the tag stack.
+.IP "Usage information"
+The
+.CO exusage
+and
+.CO viusage
+commands provide usage information for all of the
+.CO ex
+and
+.CO vi
+commands by default, or, optionally, for a specific command or key.
+.IP "Word search"
+The
+.CO <control-A>
+command searches for the word referenced by the cursor.
+.SH 1 "Startup Information"
+.pp
+.EV Ex vi
+interprets one of two possible environmental variables and reads up to
+three of five possible files during startup.
+The variables and files are expected to contain
+.CO ex
+commands, not
+.CO vi
+commands.
+In addition, they are interpreted
+.i before
+the file to be edited is read, and therefore many
+.CO ex
+commands may not be used.
+Generally, any command that requires output to the screen or that
+needs a file upon which to operate, will cause an error if included
+in a startup file or environmental variable.
+.pp
+Because the
+.CO ex
+command set supported by
+.EV nex nvi
+is a superset of the command set supported by historical implementations of
+.CO ex ,
+.EV nex nvi
+can use the startup files created for the historical implementations,
+but the converse may not be true.
+.pp
+If the
+.b \-s
+(the historic \- option)
+is specified, or if standard input is redirected from a file,
+all environmental variables and startup files are ignored.
+.pp
+Otherwise, startup files and environmental variables are handled
+in the following order:
+.np
+The file
+.LI /etc/vi.exrc
+is read,
+as long as it is owned by root or the effective user ID of the user.
+.np
+The environmental variable
+.LI NEXINIT
+(or the variable
+.LI EXINIT ,
+if
+.LI NEXINIT
+is not set) is interpreted.
+.np
+If neither
+.LI NEXINIT
+or
+.LI EXINIT
+was set, and the
+.LI HOME
+environmental variable is set, the file
+.LI $HOME/.nexrc
+(or the file
+.LI $HOME/.exrc ,
+if
+.LI $HOME/.nexrc
+does not exist) is read,
+as long as the effective user ID of the user is root or is the same as
+the owner of the file.
+.sp
+When the $HOME directory is being used for both
+.EV nex nvi
+and an historic implementation of
+.EV ex vi ,
+a possible solution is to put
+.EV nex nvi
+specific commands in the
+.LI \&.nexrc
+file, along with a
+.CO ":source $HOME/.exrc"
+command to read in the commands common to both implementations.
+.np
+If the
+.OP exrc
+option was turned on by one of the previous startup information
+sources, the file
+.LI \&.nexrc
+(or the file
+.LI \&.exrc ,
+if
+.LI \&.nexrc
+does not exist) is read, as long as the effective user ID of the user
+is the same as the owner of the file.
+.pp
+No startup file is read if it is writable by anyone other than its owner.
+.pp
+It is not an error for any of the startup environmental variables or files
+not to exist.
+.pp
+Once all environmental variables are interpreted,
+and all startup files are read,
+the first file to be edited is read in (or a temporary file is created).
+Then, any commands specified using the
+.b \-c
+option are executed, in the context of that file.
+.SH 1 "Recovery"
+.pp
+There is no recovery program for
+.EV nex nvi ,
+nor does
+.EV nex nvi
+run setuid.
+Recovery files are created readable and writable by the owner only.
+Users may recover any file which they can read,
+and the superuser may recover any edit session.
+.pp
+Edit sessions are backed by files in the directory named by the
+.OP recdir
+option (the directory
+.LI /var/tmp/vi.recover
+by default), and are named
+.QC vi.XXXXXX ,
+where
+.QC XXXXXX
+is a number related to the process ID.
+When a file is first modified,
+a second recovery file containing an email message for the user is created,
+and is named
+.QC recover.XXXXXX ,
+where, again,
+.QC XXXXXX
+is associated with the process ID.
+Both files are removed at the end of a normal edit session,
+but will remain if the edit session is abnormally terminated
+or the user runs the
+.CO ex
+.CO preserve
+command.
+.pp
+The
+.OP recdir
+option may be set in either the user's or system's startup information,
+changing the recovery directory.
+(Note, however, that if a memory based file system is used as the backup
+directory, each system reboot will delete all of the recovery files!
+The same caution applies to directories such as
+.LI /tmp
+which are cleared of their contents by a system reboot, or
+.LI /usr/tmp
+which is periodically cleared of old files on many systems.)
+.pp
+The recovery directory should be owned by root, or at least by a pseudo-user.
+In addition, if directory
+.QQ sticky-bit
+semantics are available, the directory should have the sticky-bit
+set so that files may only be removed by their owners.
+The recovery directory must be read, write, and executable by any user,
+i.e. mode 1777.
+.pp
+If the recovery directory does not exist,
+.EV ex vi
+will attempt to create it.
+This can result in the recovery directory being owned by a normal user,
+which means that that user will be able to remove other user's recovery
+and backup files.
+This is annoying, but is not a security issue as the user cannot
+otherwise access or modify the files.
+.pp
+The recovery file has all of the necessary information in it to enable the
+user to recover the edit session.
+In addition, it has all of the necessary email headers for
+.XR sendmail 8 .
+When the system is rebooted, all of the files in
+.LI /var/tmp/vi.recover
+named
+.QC recover.XXXXXX
+should be sent to their owners, by email, using the
+.b \-t
+option of
+.CO sendmail
+(or a similar mechanism in other mailers).
+If
+.EV ex vi
+receives a hangup (SIGHUP) signal, or the user executes the
+.CO ex
+.CO preserve
+command,
+.EV ex vi
+will automatically email the recovery information to the user.
+.pp
+If your system does not have the
+.CO sendmail
+utility (or a mailer program which supports its interface)
+the source file
+.LI nvi/common/recover.c
+will have to be modified to use your local mail delivery programs.
+Note, if
+.EV nex nvi
+is changed to use another mailer,
+it is important to remember that the owner of the file given to
+the mailer is the
+.EV nex nvi
+user, so nothing in the file should be trusted as it may have been
+modified in an effort to compromise the system.
+.pp
+Finally, the owner execute bit is set on backup files when they are
+created, and unset when they are first modified, e.g. backup files
+that have no associated email recovery file will have this bit set.
+(There is also a small window where empty files can be created and
+not yet have this bit set.
+This is due to the method in which the files are created.)
+Such files should be deleted when the system reboots.
+.pp
+A simple way to do this cleanup is to run the Bourne shell script
+.CO recover ,
+from your
+.LI /etc/rc.local
+(or other system startup) file.
+The script should work with the historic Bourne shell,
+a POSIX 1003.2 shell or the Korn shell.
+The
+.CO recover
+script is installed as part of the
+.EV nex nvi
+installation process.
+.pp
+Consult the manual page for details on recovering preserved or
+aborted editing sessions.
+.SH 1 "Sizing the Screen"
+.pp
+The size of the screen can be set in a number of ways.
+.EV Ex vi
+takes the following steps until values are obtained for both the
+number of rows and number of columns in the screen.
+.np
+If the environmental variable
+.LI LINES
+exists,
+it is used to specify the number of rows in the screen.
+.np
+If the environmental variable
+.LI COLUMNS
+exists,
+it is used to specify the number of columns in the screen.
+.np
+The TIOCGWINSZ
+.XR ioctl 2
+is attempted on the standard error file descriptor.
+.np
+The termcap entry (or terminfo entry on System V machines)
+is checked for the
+.QQ li
+entry (rows) and the
+.QQ co
+entry (columns).
+.np
+The number of rows is set to 24, and the number of columns is set to 80.
+.pp
+If a window change size signal (SIGWINCH) is received,
+the new window size is retrieved using the TIOCGWINSZ
+.XR ioctl 2
+call, and all other information is ignored.
+.SH 1 "Character Display"
+.pp
+In both
+.CO ex
+and
+.CO vi
+printable characters as defined by
+.XR isprint 3
+are displayed using the local character set.
+.pp
+Non-printable characters, for which
+.XR iscntrl 3
+returns true, and which are less than octal \e040,
+are displayed as the string
+.QT ^<character> ,
+where
+.LI <character>
+is the character that is the original character's value offset from the
+.QT @
+character.
+For example, the octal character \e001 is displayed as
+.QT ^A .
+If
+.XR iscntrl 3
+returns true for the octal character \e177,
+it is displayed as the string
+.QT ^? .
+All other characters are displayed as either hexadecimal values,
+in the form
+.QT "0x<high-halfbyte> ... 0x<low-halfbyte>" ,
+or as octal values, in the form
+.QT "\e<high-one-or-two-bits> ... \e<low-three-bits>" .
+The display of unknown characters is based on the value of the
+.OP octal
+option.
+.pp
+In
+.CO vi
+command mode, the cursor is always positioned on the last column of
+characters which take up more than one column on the screen.
+In
+.CO vi
+text input mode, the cursor is positioned on the first column of
+characters which take up more than one column on the screen.
+.SH 1 "Multiple Screens"
+.pp
+.CO Nvi
+supports multiple screens by dividing the window into regions.
+It also supports stacks of screens by permitting the user to change
+the set of screens that are currently displayed.
+.pp
+The
+.CO Edit ,
+.CO Ex ,
+.CO Fg ,
+.CO Next ,
+.CO Previous ,
+.CO Tag
+and
+.CO Visual
+(in
+.CO vi
+mode)
+commands divide the current screen into two regions of approximately
+equal size and then perform their usual action in a new screen area.
+If the cursor is in the lower half of the screen, the screen will split
+up, i.e. the new screen will be above the old one.
+If the cursor is in the upper half of the screen, the new screen will be
+below the old one.
+.pp
+When more than one screen is editing a file, changes in any screen are
+reflected in all other screens editing the same file.
+Exiting a screen without saving any changes (or explicitly discarding
+them) is permitted until the last screen editing the file is exited,
+at which time the changes must be saved or discarded.
+.pp
+The
+.CO resize
+command permits resizing of individual screens.
+Screens may be grown, shrunk or set to an absolute number of rows.
+.pp
+The
+.CO ^W
+command is used to switch between screens.
+Each
+.CO ^W
+moves to the next lower screen in the window, or to the first screen
+in the window if there are no lower screens.
+.pp
+The
+.CO bg
+command
+.QQ backgrounds
+the current screen.
+The screen disappears from the window,
+and the rows it occupied are taken over by a neighboring screen.
+It is an error to attempt to background the only screen in the window.
+.pp
+The
+.CO "display screens"
+command displays the names of the files associated with the current
+backgrounded screens in the window.
+.pp
+The
+.CO "fg [file]"
+command moves the specified screen from the list of backgrounded screens
+to the foreground.
+If no file argument is specified, the first screen on the list is
+foregrounded.
+By default,
+foregrounding consists of backgrounding the current screen,
+and replacing its space in the window with the foregrounded screen.
+.pp
+Capitalizing the first letter of the command, i.e.
+.CO Fg ,
+will foreground the backgrounded screen in a new screen instead of
+swapping it with the current screen.
+.pp
+If the last foregrounded screen in the window is exited,
+and there are backgrounded screens,
+the first screen on the list of backgrounded screens takes over the window.
+.SH 1 "Tags, Tag Stacks, and Cscope"
+.pp
+.CO Nvi
+supports the historic
+.CO vi
+tag command
+.CO <control-]> ,
+and the historic
+.CO ex
+tag command
+.CO tag .
+These commands change the current file context to a new location,
+based on information found in the
+.LI tags
+files.
+If you are unfamiliar with these commands,
+you should review their description in the
+.CO ex
+and
+.CO vi
+commands section of this manual.
+For additional information on tags files,
+see the discussion of the
+.OP tags
+edit option and the system
+.XR ctags 1
+manual page.
+.pp
+In addition,
+.CO nvi
+supports the notion of
+.QQ "tags stacks" ,
+using the
+.CO <control-T>
+command.
+The
+.CO <control-T>
+command returns the user to the previous context, i.e.,
+the last place from which a
+.CO <control-]>
+or
+.CO "tag"
+command was entered.
+These three commands provide the basic functionality which allows you
+to use
+.CO vi
+to review source code in a structured manner.
+.pp
+.CO Nvi
+also provides two other basic
+.CO ex
+commands for tag support:
+.CO tagpop
+and
+.CO tagtop .
+The
+.CO tagpop
+command is identical to the
+.CO <control-T>
+command,
+with the additional functionality that you may specify that modifications
+to the current file are to be discarded.
+This cannot be done using the
+.CO <control-T>
+command.
+The
+.CO tagtop
+command discards all of the contexts that have been pushed onto the tag
+stack, returning to the context from which the first
+.CO <control-]>
+or
+.CO tag
+command was entered.
+.pp
+The historic
+.XR ctags 1
+tags file format supports only a single location per tag,
+normally the function declaration or structure or string definition.
+More sophisticated source code tools often provide multiple locations
+per tag, e.g.,
+a list of the places from which a function is called or a string
+definition is used.
+An example of this functionality is the System V source code tool,
+.CO cscope .
+.sp
+.CO Cscope
+creates a database of information on source code files,
+and supports a query language for that information as described in the
+.XR cscope 1
+manual page.
+.CO Nvi
+contains an interface to the
+.CO cscope
+query language which permits you to query
+.CO cscope
+and then sequentially step through the locations in the sources files which
+.CO cscope
+returns.
+There are two
+.CO nvi
+commands which support this ability to step through multiple locations.
+They are the
+.CO ex
+commands
+.CO tagnext
+and
+.CO tagprev .
+The
+.CO tagnext
+command moves to the next location for the current tag.
+The
+.CO tagprev
+command moves to the previous location for the current tag.
+(See the
+.CO tagnext
+and
+.CO tagprev
+command discussion in the
+.CO ex
+commands section of this manual for more information.)
+At any time during this sequential walk,
+you may use the
+.CO <control-]> ,
+.CO tag
+or
+.CO cscope
+commands to move to a new tag context, and then use the
+.CO <control-T>
+or
+.CO tagpop
+commands to return and continue stepping through the locations for this
+tag.
+This is similar to the previous model of a simple tag stack,
+except that each entry in the tag stack may have more than one file context
+that is of interest.
+.pp
+Although there is no widely distributed version of
+.XR ctags 1
+that creates tags files with multiple locations per tag,
+.CO nvi
+has been written to understand the obvious extension to the historic
+tags file format, i.e., more than a single line in the tags file with
+the same initial tag name.
+If you wish to extend your
+.CO ctags
+implementation or other tool with which you build tags files,
+this extension should be simple and will require no changes to
+.CO nvi .
+.pp
+The
+.CO nvi
+and
+.CO cscope
+interface is based on the new
+.CO ex
+command
+.CO cscope ,
+which has five subcommands:
+.CO add ,
+.CO find ,
+.CO help ,
+.CO kill
+and
+.CO reset .
+The subcommand
+.CO find
+itself has eight subcommands:
+.CO \&c ,
+.CO \&d ,
+.CO \&e ,
+.CO \&f ,
+.CO \&g ,
+.CO \&i ,
+.CO \&s
+and
+.CO \&t .
+.pp
+.IP "cs[cope] a[dd] file"
+The
+.CO add
+command attaches to the specified
+.CO cscope
+database.
+The file name is expanded using the standard filename expansions.
+If
+.CO file
+is a directory, the file
+.QQ cscope.out
+in that directory is used as the database.
+.pp
+After
+.CO nvi
+attaches to a new database,
+all subsequent
+.CO cscope
+queries will be asked of that database.
+The result of any single query is the collection of response to the query
+from all of the attached databases.
+.sp
+If the
+.QQ CSCOPE_DIRS
+environmental variable is set when
+.CO nvi
+is run,
+it is expected to be a <colon> or <blank>-separated list of
+.CO cscope
+databases or directories containing
+.CO cscope
+databases, to which the user wishes to attach.
+.IP ":cs[cope] f[ind] c|d|e|f|g|i|s|t buffer|pattern"
+The
+.CO find
+command is the
+.CO cscope
+query command for
+.CO nvi .
+For this command,
+.CO nvi
+queries all attached
+.CO cscope
+databases for the pattern.
+If the pattern is a double-quote character followed by a valid buffer
+name (e.g.,
+.LI """<character>" ),
+then the contents of the named buffer are used as the pattern.
+Otherwise, the pattern is a Regular Expression.
+.sp
+The
+.CO find
+command pushes the current location onto the tags stack,
+and switches to the first location resulting from the query,
+if the query returned at least one result.
+.sp
+File names returned by the
+.CO cscope
+query, if not absolute paths, are searched for relative to the directory
+where the
+.CO cscope
+database is located.
+In addition, if the file
+.QQ cscope.tpath
+appears in the same directory as the
+.CO cscope
+database,
+it is expected to contain a colon-separated list of directory names
+where files referenced by its associated
+.CO cscope
+database may be found.
+.sp
+The
+.CO find
+subcommand is one of the following:
+.SS
+.SP \&c
+Find callers of the name.
+.SP \&d
+Find all function calls made from name.
+.SP \&e
+Find pattern.
+.SP \&f
+Find files with name as substring.
+.SP \&g
+Find definition of name.
+.SP \&i
+Find files #including name.
+.SP \&s
+Find all uses of name.
+.SP \&t
+Find assignments to name.
+.SE
+.IP ":cs[cope] h[elp] [command]"
+List the
+.CO cscope
+commands,
+or optionally list usage help for any single
+.CO cscope
+command.
+.IP ":display c[onnections]"
+Display the list of
+.CO cscope
+databases to which
+.CO nvi
+is currently connected.
+.IP ":cs[cope] k[ill] #"
+Disconnect from a specific
+.CO cscope
+database.
+The connection number is the one displayed by the
+.CO ex
+.CO "display connections"
+command.
+.IP ":cs[cope] r[eset]"
+Disconnect from all attached
+.CO cscope
+databases.
+.pp
+Cscope is not freely redistributable software,
+but is fairly inexpensive and easily available.
+To purchase a copy of
+.CO cscope ,
+see http://www.att.com/ssg/products/toolchest.html.
+.SH 1 "Regular Expressions and Replacement Strings"
+.pp
+Regular expressions are used in line addresses,
+as the first part of the
+.CO ex
+.CO substitute ,
+.CO global ,
+and
+.CO v
+commands, and in search patterns.
+.pp
+The regular expressions supported by
+.EV ex vi
+are, by default, the Basic Regular Expressions (BRE's) described in the
+IEEE POSIX Standard 1003.2.
+The
+.OP extended
+option causes all regular expressions to be interpreted as the Extended
+Regular Expressions (ERE's) described by the same standard.
+(See
+.XR re_format 7
+for more information.)
+Generally speaking, BRE's are the Regular Expressions found in
+.XR ed 1
+and
+.XR grep 1 ,
+and ERE's are the Regular Expressions found in
+.XR egrep 1 .
+.pp
+The following is not intended to provide a description of Regular
+Expressions.
+The information here only describes strings and characters which
+have special meanings in the
+.EV ex vi
+version of RE's,
+or options which change the meanings of characters that normally
+have special meanings in RE's.
+.np
+An empty RE (e.g.
+.QT //
+or
+.QT ??
+is equivalent to the last RE used.
+.np
+The construct
+.QT \e<
+matches the beginning of a word.
+.np
+The construct
+.QT \e>
+matches the end of a word.
+.np
+The character
+.QT ~
+matches the replacement part of the last
+.CO substitute
+command.
+.pp
+When the
+.OP magic
+option is
+.i not
+set, the only characters with special meanings are a
+.QT ^
+character at the beginning of an RE, a
+.QT $
+character at the end of an RE, and the escaping character
+.QT \e .
+The characters
+.QT \&. ,
+.QT * ,
+.QT [
+and
+.QT ~
+are treated as ordinary characters unless preceded by a
+.QT \e ;
+when preceded by a
+.QT \e
+they regain their special meaning.
+.pp
+Replacement strings are the second part of a
+.CO substitute
+command.
+.pp
+The character
+.QT &
+(or
+.QT \e&
+if the
+.OP magic
+option is
+.i not
+set) in the replacement string stands for the text matched by the RE
+that is being replaced.
+The character
+.QT ~
+(or
+.QT \e~
+if the
+.OP magic
+option is
+.i not
+set) stands for the replacement part of the previous
+.CO substitute
+command.
+It is only valid after a
+.CO substitute
+command has been performed.
+.pp
+The string
+.QT \e# ,
+where
+.QT #
+is an integer value from 1 to 9, stands for the text matched by
+the portion of the RE enclosed in the
+.QT # 'th
+set of escaped parentheses, e.g.
+.QT \e(
+and
+.QT \e) .
+For example,
+.QT "s/abc\e(.*\e)def/\e1/"
+deletes the strings
+.QT abc
+and
+.QT def
+from the matched pattern.
+.pp
+The strings
+.QT \el ,
+.QT \eu ,
+.QT \eL
+and
+.QT \eU
+can be used to modify the case of elements in the replacement string.
+The string
+.QT \el
+causes the next character to be converted to lowercase;
+the string
+.QT \eu
+behaves similarly, but converts to uppercase
+(e.g.
+.LI s/abc/\eU&/
+replaces the string
+.LI abc
+with
+.LI ABC ).
+The string
+.QT \eL
+causes characters up to the end of the string or the next occurrence
+of the strings
+.QT \ee
+or
+.QT \eE
+to be converted to lowercase;
+the string
+.QT \eU
+behaves similarly, but converts to uppercase.
+.pp
+If the entire replacement pattern is
+.QT % ,
+then the last replacement pattern is used again.
+.pp
+In
+.CO vi ,
+inserting a
+.LI <control-M>
+into the replacement string will cause
+the matched line to be split into two lines at that point.
+(The
+.LI <control-M>
+will be discarded.)
+.SH 1 "Scripting Languages"
+.pp
+The
+.CO nvi
+editor currently supports two scripting languages, Tcl/Tk and Perl.
+(Note that Perl4 isn't sufficient, and that the Perl5 used must be
+version 5.002 or later.
+See the
+.QB "Building Nvi"
+section for more information.
+.pp
+The scripting language interface is still being worked on,
+therefore the following information is probably incomplete,
+probably wrong in cases, and likely to change.
+See the
+.LI perl_api
+and
+.LI tcl_api
+source directories for more information.
+As a quick reference, the following function calls are provided for
+both the Perl and Tcl interfaces.
+The Perl interface uses a slightly different naming convention,
+e.g. ``viFindScreen'' is named ``VI::FindScreen''.
+.IP "viFindScreen file"
+Return the
+.LI "screenId" associated with
+.LI file .
+.IP "viAppendLine screenId lineNumber text"
+Append
+.LI text
+as a new line after line number
+.LI lineNumber ,
+in the screen
+.LI screenId .
+.IP "viDelLine screenId lineNum"
+Delete the line
+.LI lineNumber
+from the screen
+.LI screenId .
+.IP "viGetLine screenId lineNumber"
+Return the line
+.LI lineNumber
+from the screen
+.LI screenId .
+.IP "viInsertLine screenId lineNumber text"
+Insert
+.LI text
+as a new line before line number
+.LI lineNumber
+in the screen
+.LI screenId .
+.IP "viLastLine screenId"
+Return the line number of the last line in the screen
+.LI screenId .
+.IP "viSetLine screenId lineNumber text"
+Change the line
+.LI lineNumber
+in the screen
+.LI screenId
+to match the specified
+.LI text .
+.IP "viGetMark screenId mark"
+Return the current line and column for the specified
+.LI mark
+from the screen
+.LI screenId .
+.IP "viSetMark screenId mark line column"
+Set the specified
+.LI mark
+to be at line
+.LI line ,
+column
+.LI column ,
+in the screen
+.LI screenId .
+.IP "viGetCursor screenId"
+Return the current line and column for the cursor in the screen
+.LI screenId .
+.IP "viSetCursor screenId line column"
+Set the cursor in the screen
+.LI screenId
+to the specified
+.LI line
+and
+.LI column .
+.IP "viMsg screenId text"
+Display the specified
+.LI text
+as a vi message in the screen
+.LI screenId .
+.IP "viNewScreen screenId [file]"
+Create a new screen.
+.IP "viEndScreen screenId"
+Exit the screen
+.LI screenId .
+.IP "viSwitchScreen screenId screenId"
+Switch from the screen
+.LI screenId
+to the screen
+.LI screenId .
+.IP "viMapKey screenId key tclproc"
+Map the specified
+.LI key
+in the screen
+.LI screenId
+to the Tcl procedure
+.LI tclproc .
+.IP "viUnmMapKey screenId key"
+Unmap the specified
+.LI key
+in the screen
+.LI screenId
+.IP "viGetOpt screenId option"
+Return the value of the specified
+.LI option
+from the screen
+.LI screenId .
+.IP "viSetOpt screenId command"
+Set one or more options in the screen
+.LI screenId .
+.SH 1 "General Editor Description"
+.pp
+When
+.CO ex
+or
+.CO vi
+are executed,
+the text of a file is read (or a temporary file is created),
+and then all editing changes happen within the context of the
+copy of the file.
+.i "No changes affect the actual file until the file is written out" ,
+either using a write command or another command which is affected by the
+.OP autowrite
+option.
+.pp
+All files are locked (using the
+.XR flock 2
+or
+.XR fcntl 2
+interfaces) during the edit session,
+to avoid inadvertently making modifications to multiple copies of the file.
+If a lock cannot be obtained for a file because it is locked by another
+process, the edit session is read-only (as if the
+.OP readonly
+option or the
+.b \-R
+flag had been specified).
+If a lock cannot be obtained for other reasons, the edit session will
+continue, but the file status information
+(see the
+.CO <control-G>
+command) will reflect this fact.
+.pp
+Both
+.CO ex
+and
+.CO vi
+are modeful editors, i.e. they have two modes,
+.QQ command
+mode and
+.QQ "text input"
+mode.
+The former is intended to permit you to enter commands which modifies
+already existing text.
+The latter is intended to permit you to enter new text.
+When
+.CO ex
+first starts running, it is in command mode, and usually displays a prompt
+(see the
+.OP prompt
+option for more information).
+The prompt is a single colon
+.PQ :
+character.
+There are three commands that switch
+.CO ex
+into text input mode:
+.CO append ,
+.CO change
+and
+.CO insert .
+Once in input mode, entering a line containing only a single period
+.PQ \&.
+ends text input mode and returns to command mode,
+where the prompt is redisplayed.
+.pp
+When
+.CO vi
+first starts running, it is in command mode as well.
+There are eleven commands that switch
+.CO vi
+into text input mode:
+.CO A ,
+.CO a ,
+.CO C ,
+.CO c ,
+.CO I ,
+.CO i ,
+.CO O ,
+.CO o ,
+.CO R ,
+.CO S
+and
+.CO s .
+Once in input mode, entering an
+.LI <escape>
+character ends text input mode and returns to command mode.
+.pp
+.EV Ex vi
+present three different interfaces to editing a file.
+.CO Ex
+presents a line oriented interface.
+.CO Vi
+presents a full screen display oriented interface,
+also known as
+.QQ "visual mode" .
+In addition, there is a third mode,
+.QQ "open mode" ,
+which is line oriented,
+but supports cursor movement and editing within the displayed line,
+similarly to visual mode.
+Open mode is not yet implemented in
+.CO nvi .
+.pp
+The following words have special meanings in both the
+.CO ex
+and
+.CO vi
+command descriptions:
+.KY <interrupt>
+.IP <interrupt>
+The interrupt character is used to interrupt the current operation.
+Normally
+.LI <control-C> ,
+whatever character is set for the current terminal is used.
+.KY "<literal-next>"
+.IP "<literal-next>"
+The literal next character is used to escape the subsequent character
+from any special meaning.
+This character is always
+.LI <control-V> .
+If the terminal is not set up to do XON/XOFF flow control,
+then
+.LI <control-Q>
+is used to mean literal next as well.
+.KY "current pathname"
+.IP "current pathname"
+The pathname of the file currently being edited by vi.
+When the percent character
+.PQ %
+appears in a file name entered as part of an
+.CO ex
+command argument, it is replaced by the current pathname.
+(The
+.QT %
+character can be escaped by preceding it with a backslash.)
+.KY "alternate pathname"
+.IP "alternate pathname"
+The name of the last file name mentioned in an
+.CO ex
+command, or,
+the previous current pathname if the last file mentioned
+becomes the current file.
+When the hash mark character
+.PQ #
+appears in a file name entered as part of an
+.CO ex
+command argument, it is replaced by the alternate pathname.
+(The
+.QT #
+character can be escaped by preceding it with a backslash.)
+.KY buffer
+.IP buffer
+One of a number of named areas for saving copies of text.
+Commands that change or delete text can save the changed or deleted
+text into a specific buffer, for later use, if the command allows
+it (i.e. the
+.CO ex
+.CO change
+command cannot save the changed text in a named buffer).
+Buffers are named with a single character, preceded by a double quote,
+e.g.
+.LI """<character>"
+in
+.CO vi
+and
+without the double quote, e.g.
+.LI <character> ,
+in
+.CO ex .
+(The double quote isn't necessary for
+.CO ex
+because buffers names are denoted by their position in the command line.)
+Historic implementations of
+.EV ex vi
+limited
+.LI <character>
+to the alphanumeric characters;
+.EV nex nvi
+permits the use of any character without another meaning in the position
+where a buffer name is expected.
+.sp
+Buffers named by uppercase characters are the same as buffers
+named by lowercase characters, e.g. the buffer named by the
+English character
+.QT A
+is the same as the buffer named by the character
+.QT a ,
+with the exception that, if the buffer contents are being changed (as
+with a text deletion or
+.CO vi
+.CO change
+command), the text is
+.i appended
+to the buffer, instead of replacing the current contents.
+.sp
+The buffers named by the numeric characters (in English,
+.QT 1
+through
+.QT 9 ),
+are special.
+If a region of text including characters from more than one line,
+or a single line of text specified by using a line-oriented motion,
+is changed or deleted in the file using the
+.CO vi
+.CO change
+or
+.CO delete
+commands, a copy of the text is placed into the numeric buffer
+.QT 1 ,
+regardless of the user specifying another buffer in which to save it.
+In addition, there are a few commands which, when used as a
+.LI motion
+with the
+.CO vi
+.CO change
+and
+.CO delete
+commands,
+.i always
+copy the specified region of text into the numeric buffers regardless
+of the region including characters from more than one line.
+These commands are:
+.sp
+.ne 3v
+.ft C
+.TS
+r r r r.
+<control-A> % ( )
+`<character> / ? N
+n { }
+.TE
+.ft R
+.sp
+Before this copy is done, the previous contents of buffer
+.QT 1
+are moved into buffer
+.QT 2 ,
+.QT 2
+into buffer
+.QT 3 ,
+and so on.
+The contents of buffer
+.QT 9
+are discarded.
+In
+.CO vi ,
+text may be explicitly stored into the numeric buffers.
+In this case, the buffer rotation described above occurs before the
+replacement of the buffer's contents.
+The numeric buffers are only available in
+.LI visual
+and
+.LI open
+modes,
+and are not accessible by
+.CO ex
+in any way, although changed and deleted text is still stored there
+while in
+.CO ex
+mode.
+.sp
+When a
+.CO vi
+command synopsis shows both a
+.LI [buffer]
+and a
+.LI [count] ,
+they may be presented in any order.
+.sp
+Finally, all buffers are either
+.QQ line
+or
+.QQ character
+oriented.
+All
+.CO ex
+commands which store text into buffers are line oriented.
+Some
+.CO vi
+commands which store text into buffers are line oriented,
+and some are character oriented; the description for each applicable
+.CO vi
+command notes whether text copied into buffers using the command
+is line or character oriented.
+In addition, the
+.CO vi
+command
+.CO "display buffers"
+displays the current orientation for each buffer.
+Generally, the only importance attached to this orientation is that
+if the buffer is subsequently inserted into the text, line oriented
+buffers create new lines for each of the lines they contain, and
+character oriented buffers create new lines for any lines
+.i other
+than the first and last lines they contain.
+The first and last lines are inserted into the text at the current
+cursor position, becoming part of the current line.
+If there is more than one line in the buffer, however, the current
+line itself will be split.
+.KY "unnamed buffer"
+.IP "unnamed buffer"
+The unnamed buffer is a text storage area which is used by commands
+that use or operate on a buffer when no buffer is specified by the user.
+If the command stores text into a buffer,
+the text is stored into the unnamed buffer even if a buffer is also
+specified by the user.
+It is not possible to append text to the unnamed buffer.
+If text is appended to a named buffer,
+the named buffer contains both the old and new text,
+while the unnamed buffer contains only the new text.
+There is no way to explicitly reference the unnamed buffer.
+.sp
+Historically, the contents of the unnamed buffer were discarded by many
+different commands, even ones that didn't store text into it.
+.EV Nex nvi
+never discards the contents of the unnamed buffer until new text
+replaces them.
+.KY whitespace
+.IP whitespace
+The characters <tab> and <space>.
+.KY "<carriage-return>"
+.IP "<carriage-return>"
+The character represented by an ASCII
+.LI <control-M> .
+This character is almost always treated identically to a
+.LI <newline>
+character, but differs in that it can be escaped into the file text or
+into a command.
+.KY <newline>
+.IP <newline>
+The character represented by an ASCII
+.LI <control-J> .
+This character is almost always treated identically to a
+.LI <control-M>
+character, but differs in that it cannot be escaped into the file text or
+into a command.
+.oh 'Vi/Ex Reference (Vi Commands)''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference (Vi Commands)'
+.so vi.cmd.roff
+.oh 'Vi/Ex Reference''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference'
+.SH 1 "Ex Addressing"
+.pp
+Addressing in
+.CO ex
+(and when
+.CO ex
+commands are executed from
+.CO vi )
+relates to the current line.
+In general, the current line is the last line affected by a command.
+The exact effect on the current line is discussed under the description
+of each command.
+When the file contains no lines, the current line is zero.
+.pp
+Addresses are constructed by one or more of the following methods:
+.np
+The address
+.QT \&.
+refers to the current line.
+.np
+The address
+.QT $
+refers to the last line of the file.
+.np
+The address
+.QT N ,
+where
+.LI N
+is a positive number, refers to the N-th line of the file.
+.np
+The address
+.QT '<character>
+or
+.QT `<character>
+refers to the line marked with the name
+.LI <character> .
+(See the
+.CO k
+or
+.CO m
+commands for more information on how to mark lines.)
+.np
+A regular expression (RE) enclosed by slashes
+.PQ /
+is an address,
+and it refers to the first line found by searching forward from the line
+.i after
+the current line toward the end of the file, and stopping at the
+first line containing a string matching the RE.
+(The trailing slash can be omitted at the end of the command line.)
+.sp
+If no RE is specified, i.e. the pattern is
+.QT // ,
+the last RE used in any command is used in the search.
+.sp
+If the
+.OP extended
+option is set, the RE is handled as an extended RE, not a basic RE.
+If the
+.OP wrapscan
+option is set, the search wraps around to the beginning of the file
+and continues up to and including the current line, so that the entire
+file is searched.
+.sp
+The form
+.QT \e/
+is accepted for historic reasons,
+and is identical to
+.QT // .
+.np
+An RE enclosed in question marks
+.PQ ?
+addresses the first line found by searching backward from the line
+.i preceding
+the current line, toward the beginning of the file and stopping at the
+first line containing a string matching the RE.
+(The trailing question mark can be omitted at the end of a command line.)
+.sp
+If no RE is specified, i.e. the pattern is
+.QT ?? ,
+the last RE used in any command is used in the search.
+.sp
+If the
+.OP extended
+option is set, the RE is handled as an extended RE, not a basic RE.
+If the
+.OP wrapscan
+option is set, the search wraps around from the beginning of the file to
+the end of the file and continues up to and including the current line,
+so that the entire file is searched.
+.sp
+The form
+.QT \e?
+is accepted for historic reasons, and is identical to
+.QT ?? .
+.np
+An address followed by a plus sign
+.PQ +
+or a minus sign
+.PQ -
+followed by a number is an offset address and refers to the address
+plus (or minus) the indicated number of lines.
+If the address is omitted, the addition or subtraction is done with
+respect to the current line.
+.np
+An address of
+.QT +
+or
+.QT \-
+followed by a number is an offset from the current line.
+For example,
+.QT \-5
+is the same as
+.QT \&.\-5 .
+.np
+An address ending with
+.QT +
+or
+.QT -
+has 1 added to or subtracted from the address, respectively.
+As a consequence of this rule and of the previous rule, the address
+.QT \-
+refers to the line preceding the current line.
+Moreover, trailing
+.QT +
+and
+.QT \-
+characters have a cumulative effect.
+For example,
+.QT ++\-++
+refers to the current line plus 3.
+.np
+A percent sign
+.PQ %
+is equivalent to the address range
+.QT 1,$ .
+.pp
+.CO Ex
+commands require zero, one, or two addresses.
+It is an error to specify an address to a command which requires zero
+addresses.
+.pp
+If the user provides more than the expected number of addresses to any
+.CO ex
+command, the first addresses specified are discarded.
+For example,
+.QT 1,2,3,5 print
+prints lines 3 through 5, because the
+.CO print
+command only takes two addresses.
+.pp
+The addresses in a range are separated from each other by a comma
+.PQ ,
+or a semicolon
+.PQ ; .
+In the latter case, the current line
+.PQ \&.
+is set to the first address, and only then is the second address calculated.
+This feature can be used to determine the starting line for forward and
+backward searches (see rules (5) and (6) above).
+The second address of any two-address sequence corresponds to a line that
+follows, in the file, the line corresponding to the first address.
+The first address must be less than or equal to the second address.
+The first address must be greater than or equal to the first line of the
+file, and the last address must be less than or equal to the last line
+of the file.
+.oh 'Vi/Ex Reference (Ex Commands)''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference (Ex Commands)'
+.so ex.cmd.roff
+.oh 'Vi/Ex Reference (Options)''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference (Options)'
+.so set.opt.roff
+.oh 'Vi/Ex Reference''USD:13-%'
+.eh 'USD:13-%''Vi/Ex Reference'
+.bp
+.SH 1 Index
+.lp
+.2c +0.5i 3
+.ta \n($luR
+.nf
+.so index.so
+.fi
+.\" Force the TOC to an odd page, in case it's a duplex printer.
+.if o .bp
+.bp 3
+.1c
+.ce 1
+\fB\s+2Table of Contents\s0\fP
+.sp
+.xp
diff --git a/contrib/nvi/docs/USD.doc/vitut/Makefile b/contrib/nvi/docs/USD.doc/vitut/Makefile
new file mode 100644
index 000000000000..3d4aca0a64e6
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vitut/Makefile
@@ -0,0 +1,22 @@
+# @(#)Makefile 8.7 (Berkeley) 8/18/96
+
+MACROS= -ms
+ROFF= groff
+TBL= tbl
+
+all: vitut.ps summary.ps viapwh.ps
+
+vitut.ps: vi.in vi.chars
+ ${TBL} vi.in vi.chars | ${ROFF} ${MACROS} > $@
+ chmod 444 $@
+
+summary.ps: vi.summary
+ ${TBL} vi.summary | ${ROFF} ${MACROS} > $@
+ chmod 444 $@
+
+viapwh.ps: vi.apwh.ms
+ ${TBL} vi.apwh.ms | ${ROFF} ${MACROS} > $@
+ chmod 444 $@
+
+clean:
+ rm -f vitut.ps summary.ps viapwh.ps
diff --git a/contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms b/contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms
new file mode 100644
index 000000000000..6b0763055ca9
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms
@@ -0,0 +1,1081 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.apwh.ms 8.2 (Berkeley) 8/18/96
+.\"
+.nr LL 6.5i
+.nr FL 6.5i
+.TL
+Vi Command & Function Reference
+.AU CB 2675
+Alan P.W. Hewett
+.sp
+Revised for version 2.12 by Mark Horton
+.CB
+.NH 1
+Author's Disclaimer
+.LP
+This document does not claim to be 100% complete. There are a
+few commands listed in the original document that I was unable
+to test either because I do not speak \fBlisp\fR, because they
+required programs we don't have, or because I wasn't able to make
+them work. In these cases I left the command out. The commands
+listed in this document have been tried and are known to work.
+It is expected that prospective users of this document will read
+it once to get the flavor of everything that \fBvi\fR can do
+and then use it as a reference document. Experimentation is
+recommended. If you don't understand a command, try it and
+see what happens.
+.LP
+[Note: In revising this document, I have attempted to make it
+completely reflect version 2.12 of
+.B vi .
+It does not attempt to document the VAX version (version 3),
+but with one or two exceptions (wrapmargin, arrow keys)
+everything said about 2.12 should apply to 3.1.
+.I "Mark Horton" ]
+.NH 1
+Notation
+.LP
+\fB[option]\fR is used to denote optional parts of a command.
+Many \fBvi\fR commands have an optional count. \fB[cnt]\fR
+means that an optional number may precede the command to
+multiply or iterate the command.
+\fB{variable item}\fR is used to denote parts of the command
+which must appear, but can take a number of different values.
+\fB<character [-character]>\fR means that the character or
+one of the characters in the range described between the
+two angle brackets is to be typed.
+For example \fB<esc>\fR means
+the \fBescape\fR key is to be typed. \fB<a-z>\fR means that a
+lower case letter is to be typed. \fB^<character>\fR means that
+the character is to be typed as a \fBcontrol\fR character, that is,
+with the \fB<cntl>\fR key held down while simultaneously typing
+the specified character. In this document control characters will
+be denoted using the \fIupper case\fR character, but
+^<uppercase chr> and ^<lowercase chr> are equivalent. That is, for
+example, \fB<^D>\fR is equal to \fB<^d>\fR.
+The most common character abbreviations
+used in this list are as follows:
+.VL 8
+.IP <esc> 8
+escape, octal 033
+.IP <cr> 8
+carriage return, ^M, octal 015
+.IP <lf> 8
+linefeed ^J, octal 012
+.IP <nl> 8
+newline, ^J, octal 012 (same as linefeed)
+.IP <bs> 8
+backspace, ^H, octal 010
+.IP <tab> 8
+tab, ^I, octal 011
+.IP <bell> 8
+bell, ^G, octal 07
+.IP <ff> 8
+formfeed, ^L, octal 014
+.IP <sp> 8
+space, octal 040
+.IP <del> 8
+delete, octal 0177
+.LE
+.sp 1
+.NH 1
+Basics
+.LP
+To run \fBvi\fR the shell variable \fBTERM\fR must be defined and
+exported to your environment.
+How you do this depends on which shell you are using.
+You can tell which shell you have by the character it
+prompts you for commands with.
+The Bourne shell prompts with `$', and the C shell prompts with `%'.
+For these examples, we will suppose
+that you are using an HP 2621 terminal, whose termcap name is ``2621''.
+.NH 2
+Bourne Shell
+.LP
+To manually set your terminal type to 2621 you would type:
+.DS
+TERM=2621
+export TERM
+.DE
+.PP
+There are various ways of having this automatically or
+semi-automatically done when you log in.
+Suppose you usually dial in on a 2621.
+You want to tell this to the machine, but still have it
+work when you use a hardwired terminal.
+The recommended way, if you have the
+.B tset
+program, is to use the sequence
+.DS
+tset \-s \-d 2621 > tset$$
+\&. tset$$
+rm tset$$
+.DE
+in your .login (for csh) or the same thing using `.' instead of `source'
+in your .profile (for sh).
+The above line says that if you are dialing in you are on a 2621,
+but if you are on a hardwired terminal it figures out your terminal
+type from an on-line list.
+.NH 2
+The C Shell
+.LP
+To manually set your terminal type to 2621 you would type:
+.DS
+setenv TERM 2621
+.DE
+.PP
+There are various ways of having this automatically or
+semi-automatically done when you log in.
+Suppose you usually dial in on a 2621.
+You want to tell this to the machine, but still have it
+work when you use a hardwired terminal.
+The recommended way, if you have the
+.B tset
+program, is to use the sequence
+.DS
+tset \-s \-d 2621 > tset$$
+source tset$$
+rm tset$$
+.DE
+in your .login.*
+.FS
+* On a version 6 system
+without environments, the invocation of tset
+is simpler, just add the line ``tset \-d 2621''
+to your .login or .profile.
+.FE
+The above line says that if you are dialing in you are on a 2621,
+but if you are on a hardwired terminal it figures out your terminal
+type from an on-line list.
+.NH 1
+Normal Commands
+.LP
+\fBVi\fR is a visual editor with a window on the file. What
+you see on the screen is \fBvi\fR's current notion of
+what your file will contain,
+(at this point in the file),
+when it is written out.
+Most commands do not cause any change in the screen until the
+complete command is typed. Should you get confused while
+typing a command, you can abort the command by typing an
+<del> character. You will know you are back to command level
+when you hear a <bell>. Usually typing an <esc> will produce the
+same result. When \fBvi\fR gets an improperly formatted command
+it rings the <bell>.
+Following are the \fBvi\fR commands broken down by function.
+.NH 2
+Entry and Exit
+.LP
+To enter
+.B vi
+on a particular
+.I file ,
+type
+.DS
+\fBvi\fP \fIfile\fP
+.DE
+The file will be read in and the cursor will be placed at the beginning
+of the first line.
+The first screenfull of the file will be displayed on the terminal.
+.PP
+To get out of the editor, type
+.DS
+ZZ
+.DE
+If you are in some special mode, such as input mode
+or the middle of a multi-keystroke command, it may
+be necessary to type <esc> first.
+.NH 2
+Cursor and Page Motion
+.LP
+.VL 16
+.B NOTE:
+The arrow keys (see the next four commands)
+on certain kinds of terminals will not work with the
+PDP-11 version of vi. The control versions or the hjkl versions will
+work on any terminal. Experienced users prefer the hjkl keys because
+they are always right under their fingers. Beginners often prefer
+the arrow keys, since they do not require memorization of which hjkl
+key is which.
+The mnemonic value of hjkl is clear from looking at the keyboard of an adm3a.
+.sp
+.IP "[cnt]<bs> or [cnt]h or [cnt]\(<-" 16
+.br
+Move the cursor to the left one character. Cursor stops at the left
+margin of the page.
+If cnt is given, these commands move that many spaces.
+.IP "[cnt]^N or [cnt]j or [cnt]\(da or [cnt]<lf>" 16
+.br
+Move down one line.
+Moving off the screen scrolls the window to force a new line
+onto the screen.
+Mnemonic: \fBN\fRext
+.IP "[cnt]^P or [cnt]k or [cnt]\(ua" 16
+.br
+Move up one line.
+Moving off the top of the screen forces new text onto the screen.
+Mnemonic: \fBP\fRrevious
+.IP "[cnt]<sp> or [cnt]l or [cnt]\(->" 16
+.br
+Move to the right one character.
+Cursor will not go beyond the end of the line.
+.IP [cnt]- 16
+Move the cursor up the screen to the beginning of the next line.
+Scroll if necessary.
+.IP "[cnt]+ or [cnt]<cr>" 16
+.sp 1
+Move the cursor down the screen to the beginning of the next line.
+Scroll up if necessary.
+.IP "[cnt]$" 16
+Move the cursor to the end of the line.
+If there is a count, move to the end of the line "cnt" lines
+forward in the file.
+.IP "^" 16
+Move the cursor to the beginning of the first word on the line.
+.IP "0" 16
+Move the cursor to the left margin of the current line.
+.IP "[cnt]|" 16
+Move the cursor to the column specified by the count. The default is
+column zero.
+.IP "[cnt]w" 16
+Move the cursor to the beginning of the next word. If there
+is a count, then move forward that many words and
+position the cursor at the beginning of the word.
+Mnemonic: next-\fBw\fRord
+.IP "[cnt]W" 16
+Move the cursor to the beginning of the next word which follows
+a "white space" (<sp>,<tab>, or <nl>). Ignore other punctuation.
+.IP "[cnt]b" 16
+Move the cursor to the preceding word. Mnemonic: \fBb\fRackup-word
+.IP "[cnt]B" 16
+Move the cursor to the preceding word that is separated from the
+current word by a "white space" (<sp>,<tab>, or <nl>).
+.IP "[cnt]e" 16
+Move the cursor to the end of the current word or the end of the
+"cnt"'th word hence. Mnemonic: \fBe\fRnd-of-word
+.IP "[cnt]E" 16
+Move the cursor to the end of the current word which is delimited by
+"white space" (<sp>,<tab>, or <nl>).
+.IP "[line number]G" 16
+.br
+Move the cursor to the line specified. Of particular use are the
+sequences "1G" and "G", which move the cursor to the beginning and
+the end of the file respectively. Mnemonic: \fBG\fRo-to
+.LP
+.B NOTE:
+The next four commands (^D, ^U, ^F, ^B)
+are not true motion commands, in that they
+cannot be used as the object of commands such as delete or change.
+.IP "[cnt]^D" 16
+Move the cursor down in the file by "cnt" lines (or the last "cnt"
+if a new count isn't given. The initial default is half a page.) The
+screen is simultaneously scrolled up. Mnemonic: \fBD\fRown
+.IP "[cnt]^U" 16
+Move the cursor up in the file by "cnt" lines. The screen is simultaneously
+scrolled down. Mnemonic: \fBU\fRp
+.IP "[cnt]^F" 16
+Move the cursor to the next page. A count moves that many pages.
+Two lines of the previous page are kept on the screen for continuity if
+possible. Mnemonic: \fBF\fRorward-a-page
+.IP "[cnt]^B" 16
+Move the cursor to the previous page. Two lines of the current page
+are kept if possible. Mnemonic: \fBB\fRackup-a-page
+.IP "[cnt](" 16
+Move the cursor to the beginning of the next sentence.
+A sentence is defined as ending with a ".", "!", or "?"
+followed by two spaces or a <nl>.
+.IP "[cnt])" 16
+Move the cursor backwards to the beginning of a sentence.
+.IP "[cnt]}" 16
+Move the cursor to the beginning of the next paragraph. This command
+works best inside \fBnroff\fR documents. It understands two sets of
+\fBnroff\fR macros, \fB\-ms\fR and \fB\-mm\fR, for which the
+commands ".IP", ".LP", ".PP", ".QP", "P", as well as the nroff command ".bp"
+are considered to be paragraph delimiters.
+A blank line also delimits a paragraph.
+The \fBnroff\fR macros that it accepts as paragraph delimiters is
+adjustable. See \fBparagraphs\fR under the \fBSet Commands\fR section.
+.IP "[cnt]{" 16
+Move the cursor backwards to the beginning of a paragraph.
+.IP "]]" 16
+Move the cursor to the next "section", where a section is defined by
+two sets of \fBnroff\fR macros, \fB\-ms\fR and \fB\-mm\fR, in which
+".NH", ".SH", and ".H" delimit a section. A line beginning with a <ff><nl>
+sequence, or a line beginning with a "{" are also considered to
+be section delimiters. The last option makes it
+useful for finding the beginnings of C functions.
+The \fBnroff\fR macros that are used for section delimiters can be adjusted.
+See \fBsections\fR under the \fBSet Commands\fR section.
+.IP "[[" 16
+Move the cursor backwards to the beginning of a section.
+.IP "%" 16
+Move the cursor to the matching parenthesis
+or brace. This is very useful in C or lisp code. If the
+cursor is sitting on a \fB( ) {\fR or \fB}\fR the cursor
+is moved to the matching character at the other end of the
+section. If the cursor is not sitting on a brace or a
+parenthesis, \fBvi\fR searches forward until it finds one
+and then jumps to the match mate.
+.IP "[cnt]H" 16
+If there is no count move the cursor to the top left position on the screen.
+If there is a count, then move the cursor to the beginning of the line
+"cnt" lines from the top of the screen. Mnemonic: \fBH\fRome
+.IP "[cnt]L" 16
+If there is no count move the cursor to the beginning
+of the last line on the screen.
+If there is a count, then move the cursor to the beginning of the line
+"cnt" lines from the bottom of the screen. Mnemonic: \fBL\fRast
+.IP "M" 16
+Move the cursor to the beginning of the middle line on the screen.
+Mnemonic: \fBM\fRiddle
+.IP "m<a-z>" 16
+This command does not move the cursor, but it \fBmarks\fR the place
+in the file and the character "<a-z>" becomes the label for referring
+to this location in the file. See the next two commands. Mnemonic:
+\fBm\fRark
+.B NOTE:
+The mark command is not a motion, and cannot be used as the target
+of commands such as delete.
+.IP "\(aa<a-z>" 16
+Move the cursor to the beginning of the line that is marked with the label
+"<a-z>".
+.IP "\(ga<a-z>" 16
+Move the cursor to the exact position on the line that was marked with
+with the label "<a-z>".
+.IP "\(aa\(aa" 16
+Move the cursor back to the beginning of the line where it was before the
+last "non-relative" move. A "non-relative" move is something such as a
+search or a jump to a specific line in the file, rather than moving the
+cursor or scrolling the screen.
+.IP "\(ga\(ga" 16
+Move the cursor back to the exact spot on the line where it was located
+before the last "non-relative" move.
+.LE
+.NH 2
+Searches
+.LP
+The following commands allow you to search for items in a file.
+.VL 16
+.IP [cnt]f{chr} 16
+.sp 1
+Search forward on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBat\fR the character
+of interest. Mnemonic: \fBf\fRind character
+.IP [cnt]F{chr} 16
+.sp 1
+Search backwards on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBat\fR the character
+of interest.
+.IP [cnt]t{chr} 16
+.sp 1
+Search forward on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBjust preceding\fR
+the character of interest. Mnemonic: move cursor up \fBt\fRo character
+.IP [cnt]T{chr} 16
+.sp 1
+Search backwards on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBjust preceding\fR
+the character of interest.
+.IP "[cnt];" 16
+Repeat the last "f", "F", "t" or "T" command.
+.IP "[cnt]," 16
+Repeat the last "f", "F", "t" or "T" command, but in the opposite
+search direction. This is useful if you overshoot.
+.IP "[cnt]/[string]/<nl>" 16
+.br
+Search forward for the next occurrence of "string".
+Wrap around at the end of the file
+does occur.
+The final \fB</>\fR is not required.
+.IP "[cnt]?[string]?<nl>" 16
+.br
+Search backwards for the next occurrence of "string". If a count is
+specified, the count becomes the new window size. Wrap around at the beginning
+of the file does occur.
+The final \fB<?>\fR is not required.
+.IP n 16
+Repeat the last /[string]/ or ?[string]? search. Mnemonic: \fBn\fRext
+occurrence.
+.IP N 16
+Repeat the last /[string]/ or ?[string]? search, but in the reverse
+direction.
+.IP ":g/[string]/[editor command]<nl>" 16
+.sp 1
+Using the \fB:\fR syntax it is possible to do global searches ala the
+standard UNIX "ed" editor.
+.LE
+.NH 2
+Text Insertion
+.LP
+The following commands allow for the insertion of text. All multicharacter
+text insertions are terminated with an <esc> character.
+The last change
+can always be \fBundone\fR by typing a \fBu\fR.
+The text insert in insertion mode can contain newlines.
+.VL 16
+.IP a{text}<esc> 16
+Insert text immediately following the cursor position.
+Mnemonic: \fBa\fRppend
+.IP A{text}<esc> 16
+Insert text at the end of the current line.
+Mnemonic: \fBA\fRppend
+.IP i{text}<esc> 16
+Insert text immediately preceding the cursor position.
+Mnemonic: \fBi\fRnsert
+.IP I{text}<esc> 16
+Insert text at the beginning of the current line.
+.IP o{text}<esc> 16
+Insert a new line after the line on which the cursor appears and
+insert text there. Mnemonic: \fBo\fRpen new line
+.IP O{text}<esc> 16
+Insert a new line preceding the line on which the cursor appears
+and insert text there.
+.LE
+.NH 2
+Text Deletion
+.LP
+The following commands allow the user to delete text in various ways.
+All changes can always be \fBundone\fR by typing the \fBu\fR command.
+.VL 16
+.IP "[cnt]x" 16
+Delete the character or characters starting at the cursor position.
+.IP "[cnt]X" 16
+Delete the character or characters starting at the character preceding
+the cursor position.
+.IP "D" 16
+Deletes the remainder of the line starting at the cursor.
+Mnemonic: \fBD\fRelete the rest of line
+.IP "[cnt]d{motion}" 16
+.br
+Deletes one or more occurrences of the specified motion.
+Any motion from sections 4.1 and 4.2 can be used here.
+The d can be stuttered (e.g. [cnt]dd) to delete cnt lines.
+.LE
+.NH 2
+Text Replacement
+.LP
+The following commands allow the user to simultaneously delete and
+insert new text. All such actions can be \fBundone\fR by typing
+\fBu\fR following the command.
+.VL 16
+.IP "r<chr>" 16
+Replaces the character at the current cursor position with <chr>. This
+is a one character replacement. No <esc> is required for termination.
+Mnemonic: \fBr\fReplace character
+.IP "R{text}<esc>" 16
+Starts overlaying the characters on the screen with whatever you type.
+It does not stop until an <esc> is typed.
+.IP "[cnt]s{text}<esc>" 16
+Substitute for "cnt" characters beginning at the current cursor
+position. A "$" will appear at the position in the text where the
+"cnt"'th character appears so you will know how much you are erasing.
+Mnemonic: \fBs\fRubstitute
+.IP "[cnt]S{text}<esc>" 16
+Substitute for the entire current line (or lines). If no count is given,
+a "$" appears at the end of the current line. If a count of more than
+1 is given, all the lines to be replaced are deleted before the insertion
+begins.
+.IP "[cnt]c{motion}{text}<esc>" 16
+.br
+Change the specified "motion" by replacing it with the
+insertion text. A "$" will appear at the end of the last item
+that is being deleted unless the deletion involves whole lines.
+Motion's can be any motion from sections 4.1 or 4.2.
+Stuttering the c (e.g. [cnt]cc) changes cnt lines.
+.LE
+.NH 2
+Moving Text
+.LP
+\fBVi\fR provides a number of ways of moving chunks of text around.
+There are nine buffers into which each piece of text which is deleted
+or "yanked" is put in addition to the "undo" buffer.
+The most recent deletion or yank is in the "undo" buffer and also
+usually in buffer
+1, the next most recent in buffer 2, and so forth. Each new deletion
+pushes down all the older deletions. Deletions older than 9
+disappear. There is also
+a set of named registers, a-z, into which text can optionally
+be placed. If any delete or replacement type command is preceded
+by \fB"<a-z>\fR, that named buffer will contain the text deleted
+after the command is executed. For example, \fB"a3dd\fR will delete
+three lines starting at the current line and put them in buffer \fB"a\fR.*
+.FS
+* Referring to an upper case letter as a buffer name (A-Z) is the
+same as referring to the lower case letter, except that text placed
+in such a buffer is appended to it instead of replacing it.
+.FE
+There are two more basic commands and
+some variations useful in getting and putting text into a file.
+.VL 16
+.IP ["<a-z>][cnt]y{motion} 16
+.sp 1
+Yank the specified item or "cnt" items and put in the "undo" buffer or
+the specified buffer. The variety of "items" that can be yanked
+is the same as those that can be deleted with the "d" command or
+changed with the "c" command. In the same way that "dd" means
+delete the current line and "cc" means replace the current line,
+"yy" means yank the current line.
+.IP ["<a-z>][cnt]Y 16
+Yank the current line or the "cnt" lines starting from the current
+line. If no buffer is specified, they will go into the "undo" buffer,
+like any delete would. It is equivalent to "yy".
+Mnemonic: \fBY\fRank
+.IP ["<a-z>]p 16
+Put "undo" buffer or the specified buffer down \fBafter\fR the cursor.
+If whole lines were yanked or deleted into the buffer, then they will be
+put down on the line following the line the cursor is on. If
+something else was deleted, like a word or sentence, then it will
+be inserted immediately following the cursor.
+Mnemonic: \fBp\fRut buffer
+.IP
+It should be noted that text in the named buffers remains there when you
+start editing a new file with the \fB:e file<esc>\fR command. Since
+this is so, it is possible to copy or delete text from one file and
+carry it over to another file in the buffers.
+However, the undo buffer and the ability to undo are lost when
+changing files.
+.IP ["<a-z>]P 16
+Put "undo" buffer or the specified buffer down \fBbefore\fR the cursor.
+If whole lines where yanked or deleted into the buffer, then they will be
+put down on the line preceding the line the cursor is on. If
+something else was deleted, like a word or sentence, then it will
+be inserted immediately preceding the cursor.
+.IP [cnt]>{motion} 16
+The shift operator will right shift all the text from the line on which
+the cursor is located to the line where the \fBmotion\fR is located.
+The text is shifted by one \fBshiftwidth\fR. (See section 6.)
+\fB>>\fR means right shift the current line or lines.
+.IP [cnt]<{motion} 16
+The shift operator will left shift all the text from the line on which
+the cursor is located to the line where the \fBitem\fR is located.
+The text is shifted by one \fBshiftwidth\fR. (See section 6.)
+\fB<<\fR means left shift the current line or lines.
+Once the line has reached the left margin it is not further affected.
+.IP [cnt]={motion} 16
+Prettyprints the indicated area according to
+.B lisp
+conventions.
+The area should be a lisp s-expression.
+.LE
+.NH 2
+Miscellaneous Commands
+.LP
+\fBVi\fR has a number of miscellaneous commands that are very
+useful. They are:
+.VL 16
+.IP ZZ 16
+This is the normal way to exit from vi.
+If any changes have been made, the file is written out.
+Then you are returned to the shell.
+.IP ^L 16
+Redraw the current screen. This is useful if someone "write"s you
+while you are in "vi" or if for any reason garbage gets onto the
+screen.
+.IP ^R 16
+On dumb terminals, those not having the "delete line" function
+(the vt100 is such a terminal), \fBvi\fR saves redrawing the
+screen when you delete a line by just marking the line with an
+"@" at the beginning and blanking the line. If you want to
+actually get rid of the lines marked with "@" and see what the
+page looks like, typing a ^R will do this.
+.IP \s+4.\s0 16
+"Dot" is a particularly useful command. It repeats the last
+text modifying command. Therefore you can type a command once and
+then to another place and repeat it by just typing ".".
+.IP u 16
+Perhaps the most important command in the editor,
+u undoes the last command that changed the buffer.
+Mnemonic: \fBu\fRndo
+.IP U 16
+Undo all the text modifying commands performed on the current line
+since the last time you moved onto it.
+.IP [cnt]J 16
+Join the current line and the following line. The <nl> is deleted
+and the two lines joined, usually with a space between the
+end of the first line and the beginning of what was the second
+line. If the first line ended with a "period", then two spaces
+are inserted.
+A count joins the next cnt lines.
+Mnemonic: \fBJ\fRoin lines
+.IP Q 16
+Switch to \fBex\fR editing mode.
+In this mode \fBvi\fR will behave very much like \fBed\fR.
+The editor in this mode will operate on single lines normally and
+will not attempt to keep the "window" up to date.
+Once in this mode it is also possible to switch to the \fBopen\fR
+mode of editing. By entering the command \fB[line number]open<nl>\fR
+you enter this mode. It is similar to the normal visual mode
+except the window is only \fBone\fR line long.
+Mnemonic: \fBQ\fRuit visual mode
+.IP ^] 16
+An abbreviation for a tag command.
+The cursor should be positioned at the beginning of a word.
+That word is taken as a tag name, and the tag with that
+name is found as if it had been typed in a :tag command.
+.IP [cnt]!{motion}{UNIX\ cmd}<nl> 16
+.br
+Any UNIX filter
+(e.g. command that reads the standard input and outputs something
+to the standard output) can be sent a section of the current file and
+have the output of the command replace the original text. Useful
+examples are programs like \fBcb\fR, \fBsort\fR, and
+\fBnroff\fR. For instance, using \fBsort\fR it would be possible to
+sort a section of the current file into a new list.
+Using \fB!!\fR means take a line or lines starting at the line the
+cursor is currently on and pass them to the UNIX command.
+.B NOTE:
+To just escape to the shell for one command,
+use :!{cmd}<nl>, see section 5.
+.IP z{cnt}<nl> 16
+This resets the current window size to "cnt" lines and redraws the screen.
+.LE
+.NH 2
+Special Insert Characters
+.LP
+There are some characters that have special meanings during
+insert modes. They are:
+.VL 16
+.IP ^V 16
+During inserts, typing a ^V allows you to quote control characters
+into the file. Any character typed after the ^V will be inserted
+into the file.
+.IP [^]^D\ or\ [0]^D 16
+<^D> without any argument backs up one \fBshiftwidth\fR. This is necessary
+to remove indentation that was inserted by the \fBautoindent\fR feature.
+^<^D> temporarily removes all the autoindentation, thus placing the cursor
+at the left margin. On the next line, the previous indent level will be
+restored. This is useful for putting "labels" at the left margin.
+0<^D> says remove all autoindents and stay that way. Thus the cursor
+moves to the left margin and stays there on successive lines until
+<tab>'s are typed. As with the <tab>, the <^D> is only effective before
+any other "non-autoindent" controlling characters are typed.
+Mnemonic: \fBD\fRelete a shiftwidth
+.IP ^W 16
+If the cursor is sitting on a word, <^W> moves the cursor back to the beginning
+of the word, thus erasing the word from the insert.
+Mnemonic: erase \fBW\fRord
+.IP <bs> 16
+The backspace always serves as an erase during insert modes in addition
+to your normal "erase" character. To insert a <bs> into your file, use
+the <^V> to quote it.
+.LE
+.NH 1
+\fB:\fR Commands
+.LP
+Typing a ":" during command mode causes \fBvi\fR to put the cursor at
+the bottom on the screen in preparation for a command. In the
+":" mode, \fBvi\fR can be given most \fBed\fR commands. It is
+also from this mode that you exit from \fBvi\fR or switch to different
+files. All commands of this variety are terminated by a <nl>, <cr>,
+or <esc>.
+.VL 16
+.IP ":w[!] [file]" 16
+Causes \fBvi\fR to write out the current text to the disk. It is
+written to the file you are editing unless "file" is supplied. If
+"file" is supplied, the write is directed to that file instead. If
+that file already exists, \fBvi\fR will not perform the write unless
+the "!" is supplied indicating you
+.I really
+want to destroy the older copy of the file.
+.IP :q[!] 16
+Causes \fBvi\fR to exit. If you have modified the file you are
+looking at currently and haven't written it out, \fBvi\fR will
+refuse to exit unless the "!" is supplied.
+.IP ":e[!] [+[cmd]] [file]" 16
+.sp 1
+Start editing a new file called "file" or start editing the current
+file over again. The command ":e!" says "ignore the changes I've made
+to this file and start over from the beginning". It is useful if
+you really mess up the file. The optional "+" says instead of starting
+at the beginning, start at the "end", or,
+if "cmd" is supplied, execute "cmd" first.
+Useful cases of this are where cmd is "n" (any integer) which starts
+at line number n,
+and "/text", which searches for "text" and starts at the line where
+it is found.
+.IP "^^" 16
+Switch back to the place you were before your last tag command.
+If your last tag command stayed within the file, ^^ returns to that tag.
+If you have no recent tag command, it will return to the
+same place in the previous file that it was showing when you switched
+to the current file.
+.IP ":n[!]" 16
+Start editing the next file in the argument list. Since \fBvi\fR
+can be called with multiple file names, the ":n" command tells it to
+stop work on the current file and switch to the next file. If the
+current file was modifies, it has to be written out before the ":n"
+will work or else the "!" must be supplied, which says discard the
+changes I made to the current file.
+.IP ":n[!] file [file file ...]" 16
+.sp
+Replace the current argument list with a new list of files and start
+editing the first file in this new list.
+.IP ":r file" 16
+Read in a copy of "file" on the line after the cursor.
+.IP ":r !cmd" 16
+Execute the "cmd" and take its output and put it into the file after
+the current line.
+.IP ":!cmd" 16
+Execute any UNIX shell command.
+.IP ":ta[!] tag" 16
+.B Vi
+looks in the file named
+.B tags
+in the current directory.
+.B Tags
+is a file of lines in the format:
+.sp 1
+.ti +8
+tag filename \fBvi\fR-search-command
+.sp 1
+If \fBvi\fR finds the tag you specified in the \fB:ta\fR command,
+it stops editing the current file if necessary and if the current file is
+up to date on the disk and switches to the file specified and uses the
+search pattern specified to find the "tagged" item of interest. This
+is particularly useful when editing multi-file C programs such as the
+operating system. There is a program called \fBctags\fR which will
+generate an appropriate \fBtags\fR file for C and f77
+programs so that by saying
+\fB:ta function<nl>\fR you will be switched to that function.
+It could also be useful when editing multi-file documents, though the
+\fBtags\fR file would have to be generated manually.
+.LE
+.NH 1
+Special Arrangements for Startup
+.PP
+\fBVi\fR takes the value of \fB$TERM\fR and looks up the characteristics
+of that terminal in the file \fB/etc/termcap\fR.
+If you don't know \fBvi\fR's name for the terminal you are working
+on, look in \fB/etc/termcap\fR.
+.PP
+When \fBvi\fR starts, it attempts to read the variable EXINIT
+from your environment.*
+If that exists, it takes the values in it as the default values
+for certain of its internal constants. See the section on "Set Values"
+for further details.
+If EXINIT doesn't exist you will get all the normal defaults.
+.FS
+* On version 6 systems
+Instead of EXINIT, put the startup commands in the file .exrc
+in your home directory.
+.FE
+.PP
+Should you inadvertently hang up the phone while inside
+.B vi ,
+or should the computer crash,
+all may not be lost.
+Upon returning to the system, type:
+.DS
+vi \-r file
+.DE
+This will normally recover the file. If there is more than one
+temporary file for a specific file name, \fBvi\fR recovers the
+newest one. You can get an older version by recovering the
+file more than once.
+The command "vi -r" without a file name gives you the list of files
+that were saved in the last system crash
+(but
+.I not
+the file just saved when the phone was hung up).
+.NH 1
+Set Commands
+.LP
+\fBVi\fR has a number of internal variables and switches which can be
+set to achieve special affects.
+These options come in three forms, those that are switches, which toggle
+from off to on and back, those that require a numeric value, and those
+that require an alphanumeric string value.
+The toggle options are set by a command of the form:
+.DS
+:set option<nl>
+.DE
+and turned off with the command:
+.DS
+:set nooption<nl>
+.DE
+Commands requiring a value are set with a command of the form:
+.DS
+:set option=value<nl>
+.DE
+To display the value of a specific option type:
+.DS
+:set option?<nl>
+.DE
+To display only those that you have changed type:
+.DS
+:set<nl>
+.DE
+and to display the long table of all the settable parameters and
+their current values type:
+.DS
+:set all<nl>
+.DE
+.PP
+Most of the options have a long form and an abbreviation. Both are
+listed in the following table as well as the normal default value.
+.PP
+To arrange to have values other than the default used every time you
+enter
+.B vi ,
+place the appropriate
+.B set
+command in EXINIT in your environment, e.g.
+.DS
+EXINIT='set ai aw terse sh=/bin/csh'
+export EXINIT
+.DE
+or
+.DS
+setenv EXINIT 'set ai aw terse sh=/bin/csh'
+.DE
+for
+.B sh
+and
+.B csh ,
+respectively.
+These are usually placed in your .profile or .login.
+If you are running a system without environments (such as version 6)
+you can place the set command in the file .exrc in your home
+directory.
+.VL 16
+.IP autoindent\ ai 16
+Default: noai Type: toggle
+.br
+When in autoindent mode, vi helps you indent code by starting each
+line in the same column as the preceding line.
+Tabbing to the right with <tab> or <^T> will move this boundary to
+the right, and it can be moved to the left with <^D>.
+.IP autoprint\ ap 16
+Default: ap Type: toggle
+.br
+Causes the current line to be printed after each ex text modifying command.
+This is not of much interest in the normal \fBvi\fR visual mode.
+.IP autowrite\ aw 16
+Default: noaw type: toggle
+.br
+Autowrite causes an automatic write to be done if there are unsaved
+changes before certain commands which change files or otherwise
+interact with the outside world.
+These commands are :!, :tag, :next, :rewind, ^^, and ^].
+.IP beautify\ bf 16
+Default: nobf Type: toggle
+.br
+Causes all control characters except <tab>, <nl>, and <ff> to be discarded.
+.IP directory\ dir 16
+Default: dir=/tmp Type: string
+.br
+This is the directory in which \fBvi\fR puts its temporary file.
+.IP errorbells\ eb 16
+Default: noeb Type: toggle
+.br
+Error messages are preceded by a <bell>.
+.IP hardtabs\ ht 16
+Default: hardtabs=8 Type: numeric
+.br
+This option contains the value of hardware tabs in your terminal, or
+of software tabs expanded by the Unix system.
+.IP ignorecase\ ic 16
+Default: noic Type: toggle
+.br
+All upper case characters are mapped to lower case in regular expression
+matching.
+.IP lisp 16
+Default: nolisp Type: toggle
+.br
+Autoindent for \fBlisp\fR code. The commands \fB( ) [[\fR and \fB]]\fR
+are modified appropriately to affect s-expressions and functions.
+.IP list 16
+Default: nolist Type: toggle
+.br
+All printed lines have the <tab> and <nl> characters displayed visually.
+.IP magic 16
+Default: magic Type: toggle
+.br
+Enable the metacharacters for matching. These include \fB. * < > [string]
+[^string]\fR and \fB[<chr>-<chr>]\fR.
+.IP number\ nu 16
+Default: nonu Type: toggle
+.br
+Each line is displayed with its line number.
+.IP open 16
+Default: open Type: toggle
+.br
+When set, prevents entering open or visual modes from ex or edit.
+Not of interest from vi.
+.IP optimize\ opt 16
+Default: opt Type: toggle
+.br
+Basically of use only when using the \fBex\fR capabilities. This
+option prevents automatic <cr>s from taking place,
+and speeds up output of indented lines,
+at the expense of losing typeahead on some versions of UNIX.
+.IP paragraphs\ para 16
+Default: para=IPLPPPQPP\ bp Type: string
+.br
+Each pair of characters in the string indicate \fBnroff\fR macros
+which are to be treated as the beginning of a paragraph for the
+\fB{\fR and \fB}\fR commands. The default string is for the \fB-ms\fR
+and \fB-mm\fR macros.
+To indicate one letter \fBnroff\fR macros, such as \fB.P\fR or \fB.H\fR,
+quote a space in for the second character position. For example:
+.sp 1
+.ti +8
+:set paragraphs=P\e bp<nl>
+.sp 1
+would cause \fBvi\fR to consider \fB.P\fR and \fB.bp\fR as paragraph
+delimiters.
+.IP prompt 16
+Default: prompt Type: toggle
+.br
+In
+.B ex
+command mode the prompt character \fB:\fR will be printed when
+\fBex\fR is waiting for a command. This is not of interest from vi.
+.IP redraw 16
+Default: noredraw Type: toggle
+.br
+On dumb terminals, force the screen to always be up to date,
+by sending great amounts of output. Useful only at high speeds.
+.IP report 16
+Default: report=5 Type: numeric
+.br
+This sets the threshold for the number of lines modified. When
+more than this number of lines are modified, removed, or yanked,
+\fBvi\fR will report the number of lines changed at the bottom of
+the screen.
+.IP scroll 16
+Default: scroll={1/2 window} Type: numeric
+.br
+This is the number of lines that the screen scrolls up or down when
+using the <^U> and <^D> commands.
+.IP sections 16
+Default: sections=SHNHH HU Type: string
+.br
+Each two character pair of this string specify \fBnroff\fR macro names
+which are to be treated as the beginning of a section by the
+\fB]]\fR and \fB[[\fR commands. The default string is for the \fB-ms\fR
+and \fB-mm\fR macros.
+To enter one letter \fBnroff\fR macros, use a quoted space as the
+second character.
+See \fBparagraphs\fR for a fuller explanation.
+.IP shell\ sh 16
+Default: sh=from environment SHELL or /bin/sh Type: string
+.br
+This is the name of the \fBsh\fR to be used for "escaped" commands.
+.IP shiftwidth\ sw 16
+Default: sw=8 Type: numeric
+.br
+This is the number of spaces that a <^T> or <^D> will move over for
+indenting, and the amount < and > shift by.
+.IP showmatch\ sm 16
+Default: nosm Type: toggle
+.br
+When a \fB)\fR or \fB}\fR is typed, show the matching \fB(\fR or \fB{\fR
+by moving the cursor to it for one second if it is on the current screen.
+.IP slowopen\ slow 16
+Default: terminal dependent Type: toggle
+.br
+On terminals that are slow and unintelligent, this option prevents the
+updating of the screen some of the time to improve speed.
+.IP tabstop\ ts 16
+Default: ts=8 Type: numeric
+.br
+<tab>s are expanded to boundaries that are multiples of this value.
+.IP taglength\ tl 16
+Default: tl=0 Type: numeric
+.br
+If nonzero, tag names are only significant to this many characters.
+.IP term 16
+Default: (from environment \fBTERM\fP, else dumb) Type: string
+.br
+This is the terminal and controls the visual displays. It cannot be
+changed when in "visual" mode,
+you have to Q to command mode, type a
+set term command, and do ``vi.'' to get back into visual.
+Or exit vi, fix $TERM, and reenter.
+The definitions that drive a particular
+terminal type are found in the file \fB/etc/termcap\fR.
+.IP terse 16
+Default: terse Type: toggle
+.br
+When set, the error diagnostics are short.
+.IP warn 16
+Default: warn Type: toggle
+.br
+The user is warned if she/he tries to escape to
+the shell without writing out the current changes.
+.IP window 16
+Default: window={8 at 600 baud or less, 16 at 1200 baud, and screen
+size \- 1 at 2400 baud or more} Type: numeric
+.br
+This is the number of lines in the window whenever \fBvi\fR must redraw
+an entire screen. It is useful to make this size smaller if you are
+on a slow line.
+.IP w300,\ w1200,\ w9600
+.br
+These set window, but only within the corresponding speed ranges.
+They are useful in an EXINIT to fine tune window sizes.
+For example,
+.DS
+set w300=4 w1200=12
+.DE
+causes a 4 lines window at speed up to 600 baud, a 12 line window at 1200
+baud, and a full screen (the default) at over 1200 baud.
+.IP wrapscan\ ws 16
+Default: ws Type: toggle
+.br
+Searches will wrap around the end of the file when is option is set. When
+it is off, the search will terminate when it reaches the end or the
+beginning of the file.
+.IP wrapmargin\ wm 16
+Default: wm=0 Type: numeric
+.br
+\fBVi\fR will automatically insert a <nl> when it finds a natural
+break point (usually a <sp> between words) that occurs within
+"wm" spaces of the right margin.
+Therefore with "wm=0" the option is off. Setting it to 10 would
+mean that any time you are within 10 spaces of the right margin
+\fBvi\fR would be looking for a <sp> or <tab> which it could
+replace with a <nl>. This is convenient for people who forget
+to look at the screen while they type.
+(In version 3, wrapmargin behaves more like nroff, in that the
+boundary specified by the distance from the right edge of the screen
+is taken as the rightmost edge of the area where a break is allowed,
+instead of the leftmost edge.)
+.IP writeany\ wa 16
+Default: nowa Type: toggle
+.br
+\fBVi\fR normally makes a number of checks before it writes out a file.
+This prevents the user from inadvertently destroying a file. When the
+"writeany" option is enabled, \fBvi\fR no longer makes these checks.
+.LE
diff --git a/contrib/nvi/docs/USD.doc/vitut/vi.chars b/contrib/nvi/docs/USD.doc/vitut/vi.chars
new file mode 100644
index 000000000000..7941065d1d0f
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vitut/vi.chars
@@ -0,0 +1,645 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.chars 8.3 (Berkeley) 6/27/96
+.\"
+.bd S 3
+.pn 21
+.de iP
+.IP "\fB\\$1\fR" \\$2
+..
+.SH
+Appendix: character functions
+.PP
+This appendix gives the uses the editor makes of each character. The
+characters are presented in their order in the \s-2ASCII\s0 character
+set: Control characters come first, then most special characters, then
+the digits, upper and then lower case characters.
+.PP
+For each character we tell a meaning it has as a command and any meaning it
+has during an insert.
+If it has only meaning as a command, then only this is discussed.
+Section numbers in parentheses indicate where the character is discussed;
+a `f' after the section number means that the character is mentioned
+in a footnote.
+.iP "^@" 15
+Not a command character.
+If typed as the first character of an insertion it is replaced with the
+last text inserted, and the insert terminates. Only 128 characters are
+saved from the last insert; if more characters were inserted the mechanism
+is not available.
+A \fB^@\fR cannot be part of the file due to the editor implementation
+(7.5f).
+.iP "^A" 15
+Unused.
+.iP "^B" 15
+Backward window.
+A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.iP "^C" 15
+Unused.
+.iP "^D" 15
+As a command, scrolls down a half-window of text.
+A count gives the number of (logical) lines to scroll, and is remembered
+for future \fB^D\fR and \fB^U\fR commands (2.1, 7.2).
+During an insert, backtabs over \fIautoindent\fR white space at the beginning
+of a line (6.6, 7.5); this white space cannot be backspaced over.
+.iP "^E" 15
+Exposes one more line below the current screen in the file, leaving
+the cursor where it is if possible.
+(Version 3 only.)
+.iP "^F" 15
+Forward window. A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.iP "^G" 15
+Equivalent to \fB:f\fR\s-2CR\s0, printing the current file, whether
+it has been modified, the current line number and the number of lines
+in the file, and the percentage of the way through the file that you
+are.
+.iP "^H (\fR\s-2BS\s0\fP)" 15
+Same as
+.B "left arrow" .
+(See
+.B h ).
+During an insert, eliminates the last input character, backing over it
+but not erasing it; it remains so you can see what you typed if you
+wish to type something only slightly different (3.1, 7.5).
+.iP "^I\ (\fR\s-2TAB\s0\fP)" 15
+Not a command character.
+When inserted it prints as some
+number of spaces.
+When the cursor is at a tab character it rests at the last of the spaces
+which represent the tab.
+The spacing of tabstops is controlled by the \fItabstop\fR option (4.1, 6.6).
+.iP "^J\ (\fR\s-2LF\s0\fP)" 15
+Same as
+.B "down arrow"
+(see
+.B j ).
+.iP "^K" 15
+Unused.
+.iP "^L" 15
+The \s-2ASCII\s0 formfeed character, this causes the screen to be cleared
+and redrawn. This is useful after a transmission error, if characters
+typed by a program other than the editor scramble the screen,
+or after output is stopped by an interrupt (5.4, 7.2f).
+.ne 1i
+.iP "^M\ (\fR\s-2CR\s0\fP)" 15
+A carriage return advances to the next line, at the first non-white position
+in the line. Given a count, it advances that many lines (2.3).
+During an insert, a \s-2CR\s0 causes the insert to continue onto
+another line (3.1).
+.iP "^N" 15
+Same as
+.B "down arrow"
+(see
+.B j ).
+.iP "^O" 15
+Unused.
+.iP "^P" 15
+Same as
+.B "up arrow"
+(see
+.B k ).
+.iP "^Q" 15
+Not a command character.
+In input mode,
+.B ^Q
+quotes the next character, the same as
+.B ^V ,
+except that some teletype drivers will eat the
+.B ^Q
+so that the editor never sees it.
+.iP "^R" 15
+Redraws the current screen, eliminating logical lines not corresponding
+to physical lines (lines with only a single @ character on them).
+On hardcopy terminals in \fIopen\fR mode, retypes the current line
+(5.4, 7.2, 7.8).
+.iP "^S" 15
+Unused. Some teletype drivers use
+.B ^S
+to suspend output until
+.B ^Q is pressed.
+.iP "^T" 15
+Not a command character.
+During an insert, with \fIautoindent\fR set and at the beginning of the
+line, inserts \fIshiftwidth\fR white space.
+.iP "^U" 15
+Scrolls the screen up, inverting \fB^D\fR which scrolls down. Counts work as
+they do for \fB^D\fR, and the previous scroll amount is common to both.
+On a dumb terminal, \fB^U\fR will often necessitate clearing and redrawing
+the screen further back in the file (2.1, 7.2).
+.iP "^V" 15
+Not a command character.
+In input mode, quotes the next character so that it is possible
+to insert non-printing and special characters into the file (4.2, 7.5).
+.iP "^W" 15
+Not a command character.
+During an insert, backs up as \fBb\fR would in command mode; the deleted
+characters remain on the display (see \fB^H\fR) (7.5).
+.iP "^X" 15
+Unused.
+.iP "^Y" 15
+Exposes one more line above the current screen, leaving the cursor where
+it is if possible. (No mnemonic value for this key; however, it is next
+to \fB^U\fR which scrolls up a bunch.)
+(Version 3 only.)
+.iP "^Z" 15
+If supported by the Unix system,
+stops the editor, exiting to the top level shell.
+Same as \fB:stop\fP\s-2CR\s0.
+Otherwise, unused.
+.iP "^[\ (\fR\s-2ESC\s0\fP)" 15
+Cancels a partially formed command, such as a \fBz\fR when no following
+character has yet been given; terminates inputs on the last line (read
+by commands such as \fB: /\fR and \fB?\fR); ends insertions of new text
+into the buffer.
+If an \s-2ESC\s0 is given when quiescent in command state, the editor
+rings the bell or flashes the screen. You can thus hit \s-2ESC\s0 if
+you don't know what is happening till the editor rings the bell.
+If you don't know if you are in insert mode you can type \s-2ESC\s0\fBa\fR,
+and then material to be input; the material will be inserted correctly
+whether or not you were in insert mode when you started (1.5, 3.1, 7.5).
+.iP "^\e" 15
+Unused.
+.iP "^]" 15
+Searches for the word which is after the cursor as a tag. Equivalent
+to typing \fB:ta\fR, this word, and then a \s-2CR\s0.
+Mnemonically, this command is ``go right to'' (7.3).
+.iP "^\(ua" 15
+Equivalent to \fB:e #\fR\s-2CR\s0, returning to the previous position
+in the last edited file, or editing a file which you specified if you
+got a `No write since last change diagnostic' and do not want to have
+to type the file name again (7.3).
+(You have to do a \fB:w\fR before \fB^\(ua\fR
+will work in this case. If you do not wish to write the file you should
+do \fB:e!\ #\fR\s-2CR\s0 instead.)
+.iP "^_" 15
+Unused.
+Reserved as the command character for the
+Tektronix 4025 and 4027 terminal.
+.iP "\fR\s-2SPACE\s0\fP" 15
+Same as
+.B "right arrow"
+(see
+.B l ).
+.iP "!" 15
+An operator, which processes lines from the buffer with reformatting commands.
+Follow \fB!\fR with the object to be processed, and then the command name
+terminated by \s-2CR\s0. Doubling \fB!\fR and preceding it by a count
+causes count lines to be filtered; otherwise the count
+is passed on to the object after the \fB!\fR. Thus \fB2!}\fR\fIfmt\fR\s-2CR\s0
+reformats the next two paragraphs by running them through the program
+\fIfmt\fR. If you are working on \s-2LISP\s0,
+the command \fB!%\fR\fIgrind\fR\s-2CR\s0,*
+.FS
+*Both
+.I fmt
+and
+.I grind
+are Berkeley programs and may not be present at all installations.
+.FE
+given at the beginning of a
+function, will run the text of the function through the \s-2LISP\s0 grinder
+(6.7, 7.3).
+To read a file or the output of a command into the buffer use \fB:r\fR (7.3).
+To simply execute a command use \fB:!\fR (7.3).
+.tr "
+.iP  15
+Precedes a named buffer specification. There are named buffers \fB1\-9\fR
+used for saving deleted text and named buffers \fBa\-z\fR into which you can
+place text (4.3, 6.3)
+.tr 
+.iP "#" 15
+The macro character which, when followed by a number, will substitute
+for a function key on terminals without function keys (6.9).
+In input mode,
+if this is your erase character, it will delete the last character
+you typed in input mode, and must be preceded with a \fB\e\fR to insert
+it, since it normally backs over the last input character you gave.
+.iP "$" 15
+Moves to the end of the current line. If you \fB:se list\fR\s-2CR\s0,
+then the end of each line will be shown by printing a \fB$\fR after the
+end of the displayed text in the line. Given a count, advances to the
+count'th following end of line; thus \fB2$\fR advances to the end of the
+following line.
+.iP "%" 15
+Moves to the parenthesis or brace \fB{ }\fR which balances the parenthesis
+or brace at the current cursor position.
+.iP "&" 15
+A synonym for \fB:&\fR\s-2CR\s0, by analogy with the
+.I ex
+.B &
+command.
+.iP "\(aa" 15
+When followed by a \fB\(aa\fR returns to the previous context at the
+beginning of a line. The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the line which
+was marked with this letter with a \fBm\fR command, at the first non-white
+character in the line. (2.2, 5.3).
+When used with an operator such as \fBd\fR, the operation takes place
+over complete lines; if you use \fB\(ga\fR, the operation takes place
+from the exact marked place to the current cursor position within the
+line.
+.iP "(" 15
+Retreats to the beginning of a
+sentence, or to the beginning of a \s-2LISP\s0 s-expression
+if the \fIlisp\fR option is set.
+A sentence ends at a \fB. !\fR or \fB?\fR which is followed by either
+the end of a line or by two spaces. Any number of closing \fB) ] "\fR
+and \fB\(aa\fR characters may appear after the \fB. !\fR or \fB?\fR,
+and before the spaces or end of line. Sentences also begin
+at paragraph and section boundaries
+(see \fB{\fR and \fB[[\fR below).
+A count advances that many sentences (4.2, 6.8).
+.iP ")" 15
+Advances to the beginning of a sentence.
+A count repeats the effect.
+See \fB(\fR above for the definition of a sentence (4.2, 6.8).
+.iP "*" 15
+Unused.
+.iP "+" 15
+Same as \s-2CR\s0 when used as a command.
+.iP "," 15
+Reverse of the last \fBf F t\fR or \fBT\fR command, looking the other way
+in the current line. Especially useful after hitting too many \fB;\fR
+characters. A count repeats the search.
+.iP "\-" 15
+Retreats to the previous line at the first non-white character.
+This is the inverse of \fB+\fR and \s-2RETURN\s0.
+If the line moved to is not on the screen, the screen is scrolled, or
+cleared and redrawn if this is not possible.
+If a large amount of scrolling would be required the screen is also cleared
+and redrawn, with the current line at the center (2.3).
+.iP "\&." 15
+Repeats the last command which changed the buffer. Especially useful
+when deleting words or lines; you can delete some words/lines and then
+hit \fB.\fR to delete more and more words/lines.
+Given a count, it passes it on to the command being repeated. Thus after
+a \fB2dw\fR, \fB3.\fR deletes three words (3.3, 6.3, 7.2, 7.4).
+.iP "/" 15
+Reads a string from the last line on the screen, and scans forward for
+the next occurrence of this string. The normal input editing sequences may
+be used during the input on the bottom line; an returns to command state
+without ever searching.
+The search begins when you hit \s-2CR\s0 to terminate the pattern;
+the cursor moves to the beginning of the last line to indicate that the search
+is in progress; the search may then
+be terminated with a \s-2DEL\s0 or \s-2RUB\s0, or by backspacing when
+at the beginning of the bottom line, returning the cursor to
+its initial position.
+Searches normally wrap end-around to find a string
+anywhere in the buffer.
+.IP
+When used with an operator the enclosed region is normally affected.
+By mentioning an
+offset from the line matched by the pattern you can force whole lines
+to be affected. To do this give a pattern with a closing
+a closing \fB/\fR and then an offset \fB+\fR\fIn\fR or \fB\-\fR\fIn\fR.
+.IP
+To include the character \fB/\fR in the search string, you must escape
+it with a preceding \fB\e\fR.
+A \fB\(ua\fR at the beginning of the pattern forces the match to occur
+at the beginning of a line only; this speeds the search. A \fB$\fR at
+the end of the pattern forces the match to occur at the end of a line
+only.
+More extended pattern matching is available, see section 7.4;
+unless you set \fBnomagic\fR in your \fI\&.exrc\fR file you will have
+to preceed the characters \fB. [ *\fR and \fB~\fR in the search pattern
+with a \fB\e\fR to get them to work as you would naively expect (1.5, 2,2,
+6.1, 7.2, 7.4).
+.iP "0" 15
+Moves to the first character on the current line.
+Also used, in forming numbers, after an initial \fB1\fR\-\fB9\fR.
+.iP "1\-9" 15
+Used to form numeric arguments to commands (2.3, 7.2).
+.iP ":" 15
+A prefix to a set of commands for file and option manipulation and escapes
+to the system. Input is given on the bottom line and terminated with
+an \s-2CR\s0, and the command then executed. You can return to where
+you were by hitting \s-2DEL\s0 or \s-2RUB\s0 if you hit \fB:\fR accidentally
+(see primarily 6.2 and 7.3).
+.iP ";" 15
+Repeats the last single character find which used \fBf F t\fR or \fBT\fR.
+A count iterates the basic scan (4.1).
+.iP "<" 15
+An operator which shifts lines left one \fIshiftwidth\fR, normally 8
+spaces. Like all operators, affects lines when repeated, as in
+\fB<<\fR. Counts are passed through to the basic object, thus \fB3<<\fR
+shifts three lines (6.6, 7.2).
+.iP "=" 15
+Reindents line for \s-2LISP\s0, as though they were typed in with \fIlisp\fR
+and \fIautoindent\fR set (6.8).
+.iP ">" 15
+An operator which shifts lines right one \fIshiftwidth\fR, normally 8
+spaces. Affects lines when repeated as in \fB>>\fR. Counts repeat the
+basic object (6.6, 7.2).
+.iP "?" 15
+Scans backwards, the opposite of \fB/\fR. See the \fB/\fR description
+above for details on scanning (2.2, 6.1, 7.4).
+.iP "@" 15
+A macro character (6.9). If this is your kill character, you must escape it with a \e
+to type it in during input mode, as it normally backs over the input you
+have given on the current line (3.1, 3.4, 7.5).
+.iP "A" 15
+Appends at the end of line, a synonym for \fB$a\fR (7.2).
+.iP "B" 15
+Backs up a word, where words are composed of non-blank sequences, placing
+the cursor at the beginning of the word. A count repeats the effect
+(2.4).
+.iP "C" 15
+Changes the rest of the text on the current line; a synonym for \fBc$\fR.
+.iP "D" 15
+Deletes the rest of the text on the current line; a synonym for \fBd$\fR.
+.iP "E" 15
+Moves forward to the end of a word, defined as blanks and non-blanks,
+like \fBB\fR and \fBW\fR. A count repeats the effect.
+.iP "F" 15
+Finds a single following character, backwards in the current line.
+A count repeats this search that many times (4.1).
+.iP "G" 15
+Goes to the line number given as preceding argument, or the end of the
+file if no preceding count is given. The screen is redrawn with the
+new current line in the center if necessary (7.2).
+.iP "H" 15
+.B "Home arrow" .
+Homes the cursor to the top line on the screen. If a count is given,
+then the cursor is moved to the count'th line on the screen.
+In any case the cursor is moved to the first non-white character on the
+line. If used as the target of an operator, full lines are affected
+(2.3, 3.2).
+.iP "I" 15
+Inserts at the beginning of a line; a synonym for \fB\(uai\fR.
+.iP "J" 15
+Joins together lines, supplying appropriate white space: one space between
+words, two spaces after a \fB.\fR, and no spaces at all if the first
+character of the joined on line is \fB)\fR. A count causes that many
+lines to be joined rather than the default two (6.5, 7.1f).
+.iP "K" 15
+Unused.
+.iP "L" 15
+Moves the cursor to the first non-white character of the last line on
+the screen. With a count, to the first non-white of the count'th line
+from the bottom. Operators affect whole lines when used with \fBL\fR
+(2.3).
+.iP "M" 15
+Moves the cursor to the middle line on the screen, at the first non-white
+position on the line (2.3).
+.iP "N" 15
+Scans for the next match of the last pattern given to
+\fB/\fR or \fB?\fR, but in the reverse direction; this is the reverse
+of \fBn\fR.
+.iP "O" 15
+Opens a new line above the current line and inputs text there up to an
+\s-2ESC\s0. A count can be used on dumb terminals to specify a number
+of lines to be opened; this is generally obsolete, as the \fIslowopen\fR
+option works better (3.1).
+.iP "P" 15
+Puts the last deleted text back before/above the cursor. The text goes
+back as whole lines above the cursor if it was deleted as whole lines.
+Otherwise the text is inserted between the characters before and at the
+cursor. May be preceded by a named buffer specification \fB"\fR\fIx\fR
+to retrieve the contents of the buffer; buffers \fB1\fR\-\fB9\fR contain
+deleted material, buffers \fBa\fR\-\fBz\fR are available for general
+use (6.3).
+.iP "Q" 15
+Quits from \fIvi\fR to \fIex\fR command mode. In this mode, whole lines
+form commands, ending with a \s-2RETURN\s0. You can give all the \fB:\fR
+commands; the editor supplies the \fB:\fR as a prompt (7.7).
+.iP "R" 15
+Replaces characters on the screen with characters you type (overlay fashion).
+Terminates with an \s-2ESC\s0.
+.iP "S" 15
+Changes whole lines, a synonym for \fBcc\fR. A count substitutes for
+that many lines. The lines are saved in the numeric buffers, and erased
+on the screen before the substitution begins.
+.iP "T" 15
+Takes a single following character, locates the character before the
+cursor in the current line, and places the cursor just after that character.
+A count repeats the effect. Most useful with operators such as \fBd\fR
+(4.1).
+.iP "U" 15
+Restores the current line to its state before you started changing it
+(3.5).
+.iP "V" 15
+Unused.
+.iP "W" 15
+Moves forward to the beginning of a word in the current line,
+where words are defined as sequences of blank/non-blank characters.
+A count repeats the effect (2.4).
+.iP "X" 15
+Deletes the character before the cursor. A count repeats the effect,
+but only characters on the current line are deleted.
+.iP "Y" 15
+Yanks a copy of the current line into the unnamed buffer, to be put back
+by a later \fBp\fR or \fBP\fR; a very useful synonym for \fByy\fR.
+A count yanks that many lines. May be preceded by a buffer name to put
+lines in that buffer (7.4).
+.iP "ZZ" 15
+Exits the editor.
+(Same as \fB:x\fP\s-2CR\s0.)
+If any changes have been made, the buffer is written out to the current file.
+Then the editor quits.
+.iP "[[" 15
+Backs up to the previous section boundary. A section begins at each
+macro in the \fIsections\fR option,
+normally a `.NH' or `.SH' and also at lines which which start
+with a formfeed \fB^L\fR. Lines beginning with \fB{\fR also stop \fB[[\fR;
+this makes it useful for looking backwards, a function at a time, in C
+programs. If the option \fIlisp\fR is set, stops at each \fB(\fR at the
+beginning of a line, and is thus useful for moving backwards at the top
+level \s-2LISP\s0 objects. (4.2, 6.1, 6.6, 7.2).
+.iP "\e" 15
+Unused.
+.iP "]]" 15
+Forward to a section boundary, see \fB[[\fR for a definition (4.2, 6.1,
+6.6, 7.2).
+.iP "\(ua" 15
+Moves to the first non-white position on the current line (4.4).
+.iP "_" 15
+Unused.
+.iP "\(ga" 15
+When followed by a \fB\(ga\fR returns to the previous context.
+The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the position which
+was marked with this letter with a \fBm\fR command.
+When used with an operator such as \fBd\fR, the operation takes place
+from the exact marked place to the current position within the line;
+if you use \fB\(aa\fR, the operation takes place over complete lines
+(2.2, 5.3).
+.iP "a" 15
+Appends arbitrary text after the current cursor position; the insert
+can continue onto multiple lines by using \s-2RETURN\s0 within the insert.
+A count causes the inserted text to be replicated, but only if the inserted
+text is all on one line.
+The insertion terminates with an \s-2ESC\s0 (3.1, 7.2).
+.iP "b" 15
+Backs up to the beginning of a word in the current line. A word is a
+sequence of alphanumerics, or a sequence of special characters.
+A count repeats the effect (2.4).
+.iP "c" 15
+An operator which changes the following object, replacing it with the
+following input text up to an \s-2ESC\s0. If more than part of a single
+line is affected, the text which is changed away is saved in the numeric named
+buffers. If only part of the current line is affected, then the last
+character to be changed away is marked with a \fB$\fR.
+A count causes that many objects to be affected, thus both
+\fB3c)\fR and \fBc3)\fR change the following three sentences (7.4).
+.iP "d" 15
+An operator which deletes the following object. If more than part of
+a line is affected, the text is saved in the numeric buffers.
+A count causes that many objects to be affected; thus \fB3dw\fR is the
+same as \fBd3w\fR (3.3, 3.4, 4.1, 7.4).
+.iP "e" 15
+Advances to the end of the next word, defined as for \fBb\fR and \fBw\fR.
+A count repeats the effect (2.4, 3.1).
+.iP "f" 15
+Finds the first instance of the next character following the cursor on
+the current line. A count repeats the find (4.1).
+.iP "g" 15
+Unused.
+.sp
+Arrow keys
+.B h ,
+.B j ,
+.B k ,
+.B l ,
+and
+.B H .
+.iP "h" 15
+.B "Left arrow" .
+Moves the cursor one character to the left.
+Like the other arrow keys, either
+.B h ,
+the
+.B "left arrow"
+key, or one of the synonyms (\fB^H\fP) has the same effect.
+On v2 editors, arrow keys on certain kinds of terminals
+(those which send escape sequences, such as vt52, c100, or hp)
+cannot be used.
+A count repeats the effect (3.1, 7.5).
+.iP "i" 15
+Inserts text before the cursor, otherwise like \fBa\fR (7.2).
+.iP "j" 15
+.B "Down arrow" .
+Moves the cursor one line down in the same column.
+If the position does not exist,
+.I vi
+comes as close as possible to the same column.
+Synonyms include
+.B ^J
+(linefeed) and
+.B ^N .
+.iP "k" 15
+.B "Up arrow" .
+Moves the cursor one line up.
+.B ^P
+is a synonym.
+.iP "l" 15
+.B "Right arrow" .
+Moves the cursor one character to the right.
+\s-2SPACE\s0 is a synonym.
+.iP "m" 15
+Marks the current position of the cursor in the mark register which is
+specified by the next character \fBa\fR\-\fBz\fR. Return to this position
+or use with an operator using \fB\(ga\fR or \fB\(aa\fR (5.3).
+.iP "n" 15
+Repeats the last \fB/\fR or \fB?\fR scanning commands (2.2).
+.iP "o" 15
+Opens new lines below the current line; otherwise like \fBO\fR (3.1).
+.iP "p" 15
+Puts text after/below the cursor; otherwise like \fBP\fR (6.3).
+.iP "q" 15
+Unused.
+.iP "r" 15
+Replaces the single character at the cursor with a single character you
+type. The new character may be a \s-2RETURN\s0; this is the easiest
+way to split lines. A count replaces each of the following count characters
+with the single character given; see \fBR\fR above which is the more
+usually useful iteration of \fBr\fR (3.2).
+.iP "s" 15
+Changes the single character under the cursor to the text which follows
+up to an \s-2ESC\s0; given a count, that many characters from the current
+line are changed. The last character to be changed is marked with \fB$\fR
+as in \fBc\fR (3.2).
+.iP "t" 15
+Advances the cursor upto the character before the next character typed.
+Most useful with operators such as \fBd\fR and \fBc\fR to delete the
+characters up to a following character. You can use \fB.\fR to delete
+more if this doesn't delete enough the first time (4.1).
+.iP "u" 15
+Undoes the last change made to the current buffer. If repeated, will
+alternate between these two states, thus is its own inverse. When used
+after an insert which inserted text on more than one line, the lines are
+saved in the numeric named buffers (3.5).
+.iP "v" 15
+Unused.
+.iP "w" 15
+Advances to the beginning of the next word, as defined by \fBb\fR (2.4).
+.iP "x" 15
+Deletes the single character under the cursor. With a count deletes
+deletes that many characters forward from the cursor position, but only
+on the current line (6.5).
+.iP "y" 15
+An operator, yanks the following object into the unnamed temporary buffer.
+If preceded by a named buffer specification, \fB"\fR\fIx\fR, the text
+is placed in that buffer also. Text can be recovered by a later \fBp\fR
+or \fBP\fR (7.4).
+.iP "z" 15
+Redraws the screen with the current line placed as specified by the following
+character: \s-2RETURN\s0 specifies the top of the screen, \fB.\fR the
+center of the screen, and \fB\-\fR at the bottom of the screen.
+A count may be given after the \fBz\fR and before the following character
+to specify the new screen size for the redraw.
+A count before the \fBz\fR gives the number of the line to place in the
+center of the screen instead of the default current line. (5.4)
+.iP "{" 15
+Retreats to the beginning of the beginning of the preceding paragraph.
+A paragraph begins at each macro in the \fIparagraphs\fR option, normally
+`.IP', `.LP', `.PP', `.QP' and `.bp'.
+A paragraph also begins after a completely
+empty line, and at each section boundary (see \fB[[\fR above) (4.2, 6.8,
+7.6).
+.iP "|" 15
+Places the cursor on the character in the column specified
+by the count (7.1, 7.2).
+.iP "}" 15
+Advances to the beginning of the next paragraph. See \fB{\fR for the
+definition of paragraph (4.2, 6.8, 7.6).
+.iP "~" 15
+Unused.
+.iP "^?\ (\s-2\fRDEL\fP\s0)" 15
+Interrupts the editor, returning it to command accepting state (1.5,
+7.5)
+.bp
+\&.
diff --git a/contrib/nvi/docs/USD.doc/vitut/vi.in b/contrib/nvi/docs/USD.doc/vitut/vi.in
new file mode 100644
index 000000000000..c36ebe41743e
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vitut/vi.in
@@ -0,0 +1,2074 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.in 8.5 (Berkeley) 8/18/96
+.\"
+.nr LL 6.5i
+.nr FL 6.5i
+.EH 'USD:11-%''An Introduction to Display Editing with Vi'
+.OH 'An Introduction to Display Editing with Vi''USD:11-%'
+.bd S 3
+.if t .ds dg \(dg
+.if n .ds dg +
+.if t .ds dd \(dd
+.if n .ds dd ++
+.\".RP
+.TL
+An Introduction to Display Editing with Vi
+.AU
+William Joy
+.AU
+Mark Horton
+.AI
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, Ca. 94720
+.AB
+.PP
+.I Vi
+(visual) is a display oriented interactive text editor.
+When using
+.I vi
+the screen of your terminal acts as a window into the file which you
+are editing. Changes which you make to the file are reflected
+in what you see.
+.PP
+Using
+.I vi
+you can insert new text any place in the file quite easily.
+Most of the commands to
+.I vi
+move the cursor around in the file.
+There are commands to move the cursor
+forward and backward in units of characters, words,
+sentences and paragraphs.
+A small set of operators, like
+.B d
+for delete and
+.B c
+for change, are combined with the motion commands to form operations
+such as delete word or change paragraph, in a simple and natural way.
+This regularity and the mnemonic assignment of commands to keys makes the
+editor command set easy to remember and to use.
+.PP
+.I Vi
+will work on a large number of display terminals,
+and new terminals are easily driven after editing a terminal description file.
+While it is advantageous to have an intelligent terminal which can locally
+insert and delete lines and characters from the display, the editor will
+function quite well on dumb terminals over slow phone lines.
+The editor makes allowance for the low bandwidth in these situations
+and uses smaller window sizes and
+different display updating algorithms to make best use of the
+limited speed available.
+.PP
+It is also possible to use the command set of
+.I vi
+on hardcopy terminals, storage tubes and ``glass tty's'' using a one line
+editing window; thus
+.I vi's
+command set is available on all terminals.
+The full command set of the more traditional, line
+oriented editor
+.I ex
+is available within
+.I vi;
+it is quite simple to switch between the two modes of editing.
+.AE
+.NH 1
+Getting started
+.PP
+.FS
+The financial support of an \s-2IBM\s0 Graduate Fellowship and the
+National Science Foundation under grants MCS74-07644-A03 and MCS78-07291
+is gratefully acknowledged.
+.FE
+This document provides a quick introduction to
+.I vi.
+(Pronounced \fIvee-eye\fP.)
+You should be running
+.I vi
+on a file you are familiar with while you are reading this.
+The first part of this document (sections 1 through 5)
+describes the basics of using
+.I vi.
+Some topics of special interest are presented in section 6, and
+some nitty-gritty details of how the editor functions are saved for section
+7 to avoid cluttering the presentation here.
+.PP
+There is also a short appendix here, which gives for each character the
+special meanings which this character has in \fIvi\fR. Attached to
+this document should be a quick reference card.
+This card summarizes the commands of
+.I vi
+in a very compact format. You should have the card handy while you are
+learning
+.I vi.
+.NH 2
+Specifying terminal type
+.PP
+Before you can start
+.I vi
+you must tell the system what kind of terminal you are using.
+Here is a (necessarily incomplete) list of terminal type codes.
+If your terminal does not appear here, you should consult with one of
+the staff members on your system to find out the code for your terminal.
+If your terminal does not have a code, one can be assigned and a description
+for the terminal can be created.
+.LP
+.TS
+center;
+ab ab ab
+a a a.
+Code Full name Type
+_
+2621 Hewlett-Packard 2621A/P Intelligent
+2645 Hewlett-Packard 264x Intelligent
+act4 Microterm ACT-IV Dumb
+act5 Microterm ACT-V Dumb
+adm3a Lear Siegler ADM-3a Dumb
+adm31 Lear Siegler ADM-31 Intelligent
+c100 Human Design Concept 100 Intelligent
+dm1520 Datamedia 1520 Dumb
+dm2500 Datamedia 2500 Intelligent
+dm3025 Datamedia 3025 Intelligent
+fox Perkin-Elmer Fox Dumb
+h1500 Hazeltine 1500 Intelligent
+h19 Heathkit h19 Intelligent
+i100 Infoton 100 Intelligent
+mime Imitating a smart act4 Intelligent
+t1061 Teleray 1061 Intelligent
+vt52 Dec VT-52 Dumb
+.TE
+.PP
+Suppose for example that you have a Hewlett-Packard HP2621A
+terminal. The code used by the system for this terminal is `2621'.
+In this case you can use one of the following commands to tell the system
+the type of your terminal:
+.DS
+% \fBsetenv TERM\fP 2621
+.DE
+This command works with the
+.I csh
+shell.
+If you are using the standard Bourne shell
+.I sh
+then you should give the commands
+.DS
+$ \fBTERM=\fP2621
+$ \fBexport TERM\fP
+.DE
+.PP
+If you want to arrange to have your terminal type set up automatically
+when you log in, you can use the
+.I tset
+program.
+If you dial in on a
+.I mime ,
+but often use hardwired ports, a typical line for your
+.I .login
+file (if you use csh) would be
+.DS
+\fBsetenv TERM \(gatset\fP \- \-d mime\(ga
+.DE
+or for your
+.I .profile
+file (if you use sh)
+.DS
+\fBTERM=\(gatse\fPt \- \-d mime\(ga
+.DE
+.I Tset
+knows which terminals are hardwired to each port
+and needs only to be told that when you dial in you
+are probably on a
+.I mime .
+.I Tset
+is usually used to change the erase and kill characters, too.
+.NH 2
+Editing a file
+.PP
+After telling the system which kind of terminal you have, you should
+make a copy of a file you are familiar with, and run
+.I vi
+on this file, giving the command
+.DS
+% \fBvi\fR \fIname\fR
+.DE
+replacing \fIname\fR with the name of the copy file you just created.
+The screen should clear and the text of your file should appear on the
+screen. If something else happens refer to the footnote.\*(dd
+.FS
+\*(dd If you gave the system an incorrect terminal type code then the
+editor may have just made a mess out of your screen. This happens when
+it sends control codes for one kind of terminal to some other
+kind of terminal. In this case hit
+the keys \fB:q\fR (colon and the q key) and then hit the \s-2RETURN\s0 key.
+This should get you back to the command level interpreter.
+Figure out what you did wrong (ask someone else if necessary) and try again.
+ Another thing which can go wrong is that you typed the wrong file name and
+the editor just printed an error diagnostic. In this case you should
+follow the above procedure for getting out of the editor, and try again
+this time spelling the file name correctly.
+ If the editor doesn't seem to respond to the commands which you type
+here, try sending an interrupt to it by hitting the \s-2DEL\s0 or \s-2RUB\s0
+key on your terminal, and then hitting the \fB:q\fR command again followed
+by a carriage return.
+.sp
+.FE
+.NH 2
+The editor's copy: the buffer
+.PP
+The editor does not directly modify the file which you are editing.
+Rather, the editor makes a copy of this file, in a place called the
+.I buffer,
+and remembers the file's
+name. You do not affect the contents of the file unless and until you
+write the changes you make back into the original file.
+.NH 2
+Notational conventions
+.PP
+In our examples, input which must be typed as is will be presented in
+\fBbold face\fR. Text which should be replaced with appropriate input
+will be given in \fIitalics\fR. We will represent special characters
+in \s-2SMALL CAPITALS\s0.
+.NH 2
+Arrow keys
+.PP
+The editor command set is independent of the terminal
+you are using. On most terminals with cursor positioning keys, these keys
+will also work within the editor.
+If you don't have cursor positioning keys, or even if you do, you can use
+the \fBh j k\fR and \fBl\fR keys as cursor positioning
+keys (these are labelled with arrows on an
+.I adm3a).*
+.PP
+(Particular note for the HP2621: on this terminal the function keys
+must be \fIshifted\fR (ick) to send to the machine, otherwise they
+only act locally. Unshifted use will leave the cursor positioned
+incorrectly.)
+.FS
+* As we will see later,
+.I h
+moves back to the left (like control-h which is a backspace),
+.I j
+moves down (in the same column),
+.I k
+moves up (in the same column),
+and
+.I l
+moves to the right.
+.FE
+.NH 2
+Special characters: \s-2ESC\s0, \s-2CR\s0 and \s-2DEL\s0
+.PP
+Several of these special characters are very important, so be sure to
+find them right now. Look on your keyboard for a key labelled \s-2ESC\s0
+or \s-2ALT\s0. It should be near the upper left corner of your terminal.
+Try hitting this key a few times. The editor will ring the bell
+to indicate that it is in a quiescent state.\*(dd
+.FS
+\*(dd On smart terminals where it is possible, the editor will quietly
+flash the screen rather than ringing the bell.
+.FE
+Partially formed commands are cancelled by \s-2ESC\s0, and when you insert
+text in the file you end the text insertion
+with \s-2ESC\s0. This key is a fairly
+harmless one to hit, so you can just hit it if you don't know
+what is going on until the editor rings the bell.
+.PP
+The \s-2CR\s0 or \s-2RETURN\s0 key is important because it is used
+to terminate certain commands.
+It is usually at the right side of the keyboard,
+and is the same command used at the end of each shell command.
+.PP
+Another very useful key is the \s-2DEL\s0 or \s-2RUB\s0 key, which generates
+an interrupt, telling the editor to stop what it is doing.
+It is a forceful way of making the editor listen
+to you, or to return it to the quiescent state if you don't know or don't
+like what is going on. Try hitting the `/' key on your terminal. This
+key is used when you want to specify a string to be searched for. The
+cursor should now be positioned at the bottom line of the terminal after
+a `/' printed as a prompt. You can get the cursor back to the current
+position by hitting the \s-2DEL\s0 or \s-2RUB\s0 key; try this now.*
+.FS
+* Backspacing over the `/' will also cancel the search.
+.FE
+From now on we will simply refer to hitting the \s-2DEL\s0 or \s-2RUB\s0
+key as ``sending an interrupt.''**
+.FS
+** On some systems, this interruptibility comes at a price: you cannot type
+ahead when the editor is computing with the cursor on the bottom line.
+.FE
+.PP
+The editor often echoes your commands on the last line of the terminal.
+If the cursor is on the first position of this last line, then the editor
+is performing a computation, such as computing a new position in the
+file after a search or running a command to reformat part of the buffer.
+When this is happening you can stop the editor by
+sending an interrupt.
+.NH 2
+Getting out of the editor
+.PP
+After you have worked with this introduction for a while, and you wish
+to do something else, you can give the command \fBZZ\fP
+to the editor.
+This will write the contents of the editor's buffer back into
+the file you are editing, if you made any changes, and then quit from
+the editor. You can also end an editor
+session by giving the command \fB:q!\fR\s-2CR\s0;\*(dg
+.FS
+\*(dg All commands which read from the last display line can also be
+terminated with a \s-2ESC\s0 as well as an \s-2CR\s0.
+.FE
+this is a dangerous but occasionally essential
+command which ends the editor session and discards all your changes.
+You need to know about this command in case you change the editor's
+copy of a file you wish only to look at. Be very careful
+not to give this command when you really want to save
+the changes you have made.
+.NH 1
+Moving around in the file
+.NH 2
+Scrolling and paging
+.PP
+The editor has a number of commands for moving around in the file.
+The most useful of these is generated by hitting the control and D keys
+at the same time, a control-D or `^D'. We will use this two character
+notation for referring to these control keys from now on. You may have
+a key labelled `^' on your terminal. This key will be represented as `\(ua'
+in this document; `^' is exclusively used as part of the `^x' notation
+for control characters.\*(dd
+.FS
+\*(dd If you don't have a `^' key on your terminal
+then there is probably a key labelled `\(ua'; in any case these characters
+are one and the same.
+.FE
+.PP
+As you know now if you tried hitting \fB^D\fR, this command scrolls down in
+the file. The \fBD\fR thus stands for down. Many editor commands are mnemonic
+and this makes them much easier to remember. For instance the command
+to scroll up is \fB^U\fR. Many dumb terminals can't scroll up at all, in which
+case hitting \fB^U\fR clears the screen and refreshes it
+with a line which is farther back in the file at the top.
+.PP
+If you want to see more of the file below where you are, you can
+hit \fB^E\fR to expose one more line at the bottom of the screen,
+leaving the cursor where it is.
+The command \fB^Y\fR (which is hopelessly non-mnemonic, but next to \fB^U\fR
+on the keyboard) exposes one more line at the top of the screen.
+.PP
+There are other ways to move around in the file; the keys \fB^F\fR and \fB^B\fR
+move forward and backward a page,
+keeping a couple of lines of continuity between screens
+so that it is possible to read through a file using these rather than
+\fB^D\fR and \fB^U\fR if you wish.
+.PP
+Notice the difference between scrolling and paging. If you are trying
+to read the text in a file, hitting \fB^F\fR to move forward a page
+will leave you only a little context to look back at. Scrolling on the
+other hand leaves more context, and happens more smoothly. You can continue
+to read the text as scrolling is taking place.
+.NH 2
+Searching, goto, and previous context
+.PP
+Another way to position yourself in the file is by giving the editor a string
+to search for. Type the character \fB/\fR followed by a string of characters
+terminated by \s-2CR\s0. The editor will position the cursor
+at the next occurrence of this string.
+Try hitting \fBn\fR to then go to the next occurrence of this string.
+The character \fB?\fR will search backwards from where you are, and is
+otherwise like \fB/\fR.\*(dg
+.FS
+\*(dg These searches will normally wrap around the end of the file, and thus
+find the string even if it is not on a line in the direction you search
+provided it is anywhere else in the file. You can disable this wraparound
+in scans by giving the command \fB:se nowrapscan\fR\s-2CR\s0,
+or more briefly \fB:se nows\fR\s-2CR\s0.
+.FE
+.PP
+If the search string you give the editor is not present in the
+file the editor will print
+a diagnostic on the last line of the screen, and the cursor will be returned
+to its initial position.
+.PP
+If you wish the search to match only at the beginning of a line, begin
+the search string with an \fB\(ua\fR. To match only at the end of
+a line, end the search string with a \fB$\fR.
+Thus \fB/\(uasearch\fR\s-2CR\s0 will search for the word `search' at
+the beginning of a line, and \fB/last$\fR\s-2CR\s0 searches for the
+word `last' at the end of a line.*
+.FS
+*Actually, the string you give to search for here can be a
+.I "regular expression"
+in the sense of the editors
+.I ex (1)
+and
+.I ed (1).
+If you don't wish to learn about this yet, you can disable this more
+general facility by doing
+\fB:se\ nomagic\fR\s-2CR\s0;
+by putting this command in
+EXINIT
+in your environment, you can have this always be in effect (more
+about
+.I EXINIT
+later.)
+.FE
+.PP
+The command \fBG\fR, when preceded by a number will position the cursor
+at that line in the file.
+Thus \fB1G\fR will move the cursor to
+the first line of the file. If you give \fBG\fR no count, then it moves
+to the end of the file.
+.PP
+If you are near the end of the file, and the last line is not at the bottom
+of the screen, the editor will place only the character `~' on each remaining
+line. This indicates that the last line in the file is on the screen;
+that is, the `~' lines are past the end of the file.
+.PP
+You can find out the state of the file you are editing by typing a \fB^G\fR.
+The editor will show you the name of the file you are editing, the number
+of the current line, the number of lines in the buffer, and the percentage
+of the way through the buffer which you are.
+Try doing this now, and remember the number of the line you are on.
+Give a \fBG\fR command to get to the end and then another \fBG\fR command
+to get back where you were.
+.PP
+You can also get back to a previous position by using the command
+\fB\(ga\(ga\fR (two back quotes).
+This is often more convenient than \fBG\fR because it requires no advance
+preparation.
+Try giving a \fBG\fR or a search with \fB/\fR or \fB?\fR and then a
+\fB\(ga\(ga\fR to get back to where you were. If you accidentally hit
+\fBn\fR or any command which moves you far away from a context of interest, you
+can quickly get back by hitting \fB\(ga\(ga\fR.
+.NH 2
+Moving around on the screen
+.PP
+Now try just moving the cursor around on the screen.
+If your terminal has arrow keys (4 or 5 keys with arrows
+going in each direction) try them and convince yourself
+that they work.
+If you don't have working arrow keys, you can always use
+.B h ,
+.B j ,
+.B k ,
+and
+.B l .
+Experienced users of
+.I vi
+prefer these keys to arrow keys,
+because they are usually right underneath their fingers.
+.PP
+Hit the \fB+\fR key. Each time you do, notice that the cursor
+advances to the next line in the file, at the first non-white position
+on the line. The \fB\-\fR key is like \fB+\fR but goes the other way.
+.PP
+These are very common keys for moving up and down lines in the file.
+Notice that if you go off the bottom or top with these keys then the
+screen will scroll down (and up if possible) to bring a line at a time
+into view. The \s-2RETURN\s0 key has the same effect as the \fB+\fR
+key.
+.PP
+.I Vi
+also has commands to take you to the top, middle and bottom of the screen.
+\fBH\fR will take you to the top (home) line on the screen.
+Try preceding it with a
+number as in \fB3H\fR.
+This will take you to the third line on the screen.
+Many
+.I vi
+commands take preceding numbers and do interesting things with them.
+Try \fBM\fR,
+which takes you to the middle line on the screen,
+and \fBL\fR,
+which takes you to the last line on the screen.
+\fBL\fR also takes counts, thus
+\fB5L\fR will take you to the fifth line from the bottom.
+.NH 2
+Moving within a line
+.PP
+Now try picking a word on some line on the screen, not the
+first word on the line.
+move the cursor using \s-2RETURN\s0 and \fB\-\fR to be on the line where
+the word is.
+Try hitting the \fBw\fR key. This will advance the cursor to the
+next word on the line.
+Try hitting the \fBb\fR key to back up words
+in the line.
+Also try the \fBe\fR key which advances you to the end of the current
+word rather than to the beginning of the next word.
+Also try \s-2SPACE\s0 (the space bar) which moves right one character
+and the \s-2BS\s0 (backspace or \fB^H\fR) key which moves left one character.
+The key \fBh\fR works as \fB^H\fR does and is useful if you don't have
+a \s-2BS\s0 key.
+(Also, as noted just above, \fBl\fR will move to the right.)
+.PP
+If the line had punctuation in it you may have noticed that
+that the \fBw\fR and \fBb\fR
+keys stopped at each group of punctuation. You can also go back and
+forwards words without stopping at punctuation by using \fBW\fR and \fBB\fR
+rather than the lower case equivalents. Think of these as bigger words.
+Try these on a few lines with punctuation to see how they differ from
+the lower case \fBw\fR and \fBb\fR.
+.PP
+The word keys wrap around the end of line,
+rather than stopping at the end. Try moving to a word on a line below
+where you are by repeatedly hitting \fBw\fR.
+.NH 2
+Summary
+.IP
+.TS
+lw(.50i)b a.
+\fR\s-2SPACE\s0\fP advance the cursor one position
+^B backwards to previous page
+^D scrolls down in the file
+^E exposes another line at the bottom
+^F forward to next page
+^G tell what is going on
+^H backspace the cursor
+^N next line, same column
+^P previous line, same column
+^U scrolls up in the file
+^Y exposes another line at the top
++ next line, at the beginning
+\- previous line, at the beginning
+/ scan for a following string forwards
+? scan backwards
+B back a word, ignoring punctuation
+G go to specified line, last default
+H home screen line
+M middle screen line
+L last screen line
+W forward a word, ignoring punctuation
+b back a word
+e end of current word
+n scan for next instance of \fB/\fR or \fB?\fR pattern
+w word after this word
+.TE
+.NH 2
+View
+.PP
+If you want to use the editor to look at a file,
+rather than to make changes,
+invoke it as
+.I view
+instead of
+.I vi .
+This will set the
+.I readonly
+option which will prevent you from
+accidently overwriting the file.
+.sp
+.NH 1
+Making simple changes
+.NH 2
+Inserting
+.PP
+One of the most useful commands is the
+\fBi\fR (insert) command.
+After you type \fBi\fR, everything you type until you hit \s-2ESC\s0
+is inserted into the file.
+Try this now; position yourself to some word in the file and try inserting
+text before this word.
+If you are on an dumb terminal it will seem, for a minute,
+that some of the characters in your line have been overwritten, but they will
+reappear when you hit \s-2ESC\s0.
+.PP
+Now try finding a word which can, but does not, end in an `s'.
+Position yourself at this word and type \fBe\fR (move to end of word), then
+\fBa\fR for append and then `s\s-2ESC\s0' to terminate the textual insert.
+This sequence of commands can be used to easily pluralize a word.
+.PP
+Try inserting and appending a few times to make sure you understand how
+this works; \fBi\fR placing text to the left of the cursor, \fBa\fR to
+the right.
+.PP
+It is often the case that you want to add new lines to the file you are
+editing, before or after some specific line in the file. Find a line
+where this makes sense and then give the command \fBo\fR to create a
+new line after the line you are on, or the command \fBO\fR to create
+a new line before the line you are on. After you create a new line in
+this way, text you type up to an \s-2ESC\s0 is inserted on the new line.
+.PP
+Many related editor commands
+are invoked by the same letter key and differ only in that one is given
+by a lower
+case key and the other is given by
+an upper case key. In these cases, the
+upper case key often differs from the lower case key in its sense of
+direction, with
+the upper case key working backward and/or up, while the lower case
+key moves forward and/or down.
+.PP
+Whenever you are typing in text, you can give many lines of input or
+just a few characters.
+To type in more than one line of text,
+hit a \s-2RETURN\s0 at the middle of your input. A new line will be created
+for text, and you can continue to type. If you are on a slow
+and dumb terminal the editor may choose to wait to redraw the
+tail of the screen, and will let you type over the existing screen lines.
+This avoids the lengthy delay which would occur if the editor attempted
+to keep the tail of the screen always up to date. The tail of the screen will
+be fixed up, and the missing lines will reappear, when you hit \s-2ESC\s0.
+.PP
+While you are inserting new text, you can use the characters you normally use
+at the system command level (usually \fB^H\fR or \fB#\fR) to backspace
+over the last
+character which you typed, and the character which you use to kill input lines
+(usually \fB@\fR, \fB^X\fR, or \fB^U\fR)
+to erase the input you have typed on the current line.\*(dg
+.FS
+\*(dg In fact, the character \fB^H\fR (backspace) always works to erase the
+last input character here, regardless of what your erase character is.
+.FE
+The character \fB^W\fR
+will erase a whole word and leave you after the space after the previous
+word; it is useful for quickly backing up in an insert.
+.PP
+Notice that when you backspace during an insertion the characters you
+backspace over are not erased; the cursor moves backwards, and the characters
+remain on the display. This is often useful if you are planning to type
+in something similar. In any case the characters disappear when when
+you hit \s-2ESC\s0; if you want to get rid of them immediately, hit an
+\s-2ESC\s0 and then \fBa\fR again.
+.PP
+Notice also that you can't erase characters which you didn't insert, and that
+you can't backspace around the end of a line. If you need to back up
+to the previous line to make a correction, just hit \s-2ESC\s0 and move
+the cursor back to the previous line. After making the correction you
+can return to where you were and use the insert or append command again.
+.sp .5
+.NH 2
+Making small corrections
+.PP
+You can make small corrections in existing text quite easily.
+Find a single character which is wrong or just pick any character.
+Use the arrow keys to find the character, or
+get near the character with the word motion keys and then either
+backspace (hit the \s-2BS\s0 key or \fB^H\fR or even just \fBh\fR) or
+\s-2SPACE\s0 (using the space bar)
+until the cursor is on the character which is wrong.
+If the character is not needed then hit the \fBx\fP key; this deletes
+the character from the file. It is analogous to the way you \fBx\fP
+out characters when you make mistakes on a typewriter (except it's not
+as messy).
+.PP
+If the character
+is incorrect, you can replace it with the correct character by giving
+the command \fBr\fR\fIc\fR,
+where \fIc\fR is replaced by the correct character.
+Finally if the character which is incorrect should be replaced
+by more than one character, give the command \fBs\fR which substitutes
+a string of characters, ending with \s-2ESC\s0, for it.
+If there are a small number of characters
+which are wrong you can precede \fBs\fR with a count of the number of
+characters to be replaced. Counts are also useful with \fBx\fR to specify
+the number of characters to be deleted.
+.NH 2
+More corrections: operators
+.PP
+You already know almost enough to make changes at a higher level.
+All you need to know now is that the
+.B d
+key acts as a delete operator. Try the command
+.B dw
+to delete a word.
+Try hitting \fB.\fR a few times. Notice that this repeats the effect
+of the \fBdw\fR. The command \fB.\fR repeats the last command which
+made a change. You can remember it by analogy with an ellipsis `\fB...\fR'.
+.PP
+Now try
+\fBdb\fR.
+This deletes a word backwards, namely the preceding word.
+Try
+\fBd\fR\s-2SPACE\s0. This deletes a single character, and is equivalent
+to the \fBx\fR command.
+.PP
+Another very useful operator is
+.B c
+or change. The command
+.B cw
+thus changes the text of a single word.
+You follow it by the replacement text ending with an \s-2ESC\s0.
+Find a word which you can change to another, and try this
+now.
+Notice that the end of the text to be changed was marked with the character
+`$' so that you can see this as you are typing in the new material.
+.sp .5
+.NH 2
+Operating on lines
+.PP
+It is often the case that you want to operate on lines.
+Find a line which you want to delete, and type
+\fBdd\fR,
+the
+.B d
+operator twice. This will delete the line.
+If you are on a dumb terminal, the editor may just erase the line on
+the screen, replacing it with a line with only an @ on it. This line
+does not correspond to any line in your file, but only acts as a place
+holder. It helps to avoid a lengthy redraw of the rest of the screen
+which would be necessary to close up the hole created by the deletion
+on a terminal without a delete line capability.
+.PP
+Try repeating the
+.B c
+operator twice; this will change a whole line, erasing its previous contents and
+replacing them with text you type up to an \s-2ESC\s0.\*(dg
+.FS
+\*(dg The command \fBS\fR is a convenient synonym for for \fBcc\fR, by
+analogy with \fBs\fR. Think of \fBS\fR as a substitute on lines, while
+\fBs\fR is a substitute on characters.
+.FE
+.PP
+You can delete or change more than one line by preceding the
+.B dd
+or
+.B cc
+with a count, i.e. \fB5dd\fR deletes 5 lines.
+You can also give a command like \fBdL\fR to delete all the lines up to
+and including
+the last line on the screen, or \fBd3L\fR to delete through the third from
+the bottom line. Try some commands like this now.*
+.FS
+* One subtle point here involves using the \fB/\fR search after a \fBd\fR.
+This will normally delete characters from the current position to the
+point of the match. If what is desired is to delete whole lines
+including the two points, give the pattern as \fB/pat/+0\fR, a line address.
+.FE
+Notice that the editor lets you know when you change a large number of
+lines so that you can see the extent of the change.
+The editor will also always tell you when a change you make affects text which
+you cannot see.
+.NH 2
+Undoing
+.PP
+Now suppose that the last change which you made was incorrect;
+you could use the insert, delete and append commands to put the correct
+material back. However, since it is often the case that we regret a
+change or make a change incorrectly, the editor provides a
+.B u
+(undo) command to reverse the last change which you made.
+Try this a few times, and give it twice in a row to notice that an
+.B u
+also undoes a
+.B u.
+.PP
+The undo command lets you reverse only a single change. After you make
+a number of changes to a line, you may decide that you would rather have
+the original state of the line back. The
+.B U
+command restores the current line to the state before you started changing
+it.
+.PP
+You can recover text which you delete, even if
+undo will not bring it back; see the section on recovering lost text
+below.
+.NH 2
+Summary
+.IP
+.TS
+lw(.50i)b a.
+\fR\s-2SPACE\s0\fP advance the cursor one position
+^H backspace the cursor
+^W erase a word during an insert
+\fRerase\fP your erase (usually ^H or #), erases a character during an insert
+\fRkill\fP your kill (usually @, ^X, or ^U), kills the insert on this line
+\&\fB.\fP repeats the changing command
+O opens and inputs new lines, above the current
+U undoes the changes you made to the current line
+a appends text after the cursor
+c changes the object you specify to the following text
+d deletes the object you specify
+i inserts text before the cursor
+o opens and inputs new lines, below the current
+u undoes the last change
+.TE
+.NH 1
+Moving about; rearranging and duplicating text
+.NH 2
+Low level character motions
+.PP
+Now move the cursor to a line where there is a punctuation or a bracketing
+character such as a parenthesis or a comma or period. Try the command
+\fBf\fR\fIx\fR where \fIx\fR is this character. This command finds
+the next \fIx\fR character to the right of the cursor in the current
+line. Try then hitting a \fB;\fR, which finds the next instance of the
+same character. By using the \fBf\fR command and then a sequence of
+\fB;\fR's you can often
+get to a particular place in a line much faster than with a sequence
+of word motions or \s-2SPACE\s0s.
+There is also a \fBF\fR command, which is like \fBf\fR, but searches
+backward. The \fB;\fR command repeats \fBF\fR also.
+.PP
+When you are operating on the text in a line it is often desirable to
+deal with the characters up to, but not including, the first instance of
+a character. Try \fBdf\fR\fIx\fR for some \fIx\fR now and
+notice that the \fIx\fR character is deleted. Undo this with \fBu\fR
+and then try \fBdt\fR\fIx\fR; the \fBt\fR here stands for to, i.e.
+delete up to the next \fIx\fR, but not the \fIx\fR. The command \fBT\fR
+is the reverse of \fBt\fR.
+.PP
+When working with the text of a single line, an \fB\(ua\fR moves the
+cursor to the first non-white position on the line, and a
+\fB$\fR moves it to the end of the line. Thus \fB$a\fR will append new
+text at the end of the current line.
+.PP
+Your file may have tab (\fB^I\fR) characters in it. These
+characters are represented as a number of spaces expanding to a tab stop,
+where tab stops are every 8 positions.*
+.FS
+* This is settable by a command of the form \fB:se ts=\fR\fIx\fR\s-2CR\s0,
+where \fIx\fR is 4 to set tabstops every four columns. This has
+effect on the screen representation within the editor.
+.FE
+When the cursor is at a tab, it sits on the last of the several spaces
+which represent that tab. Try moving the cursor back and forth over
+tabs so you understand how this works.
+.PP
+On rare occasions, your file may have nonprinting characters in it.
+These characters are displayed in the same way they are represented in
+this document, that is with a two character code, the first character
+of which is `^'. On the screen non-printing characters resemble a `^'
+character adjacent to another, but spacing or backspacing over the character
+will reveal that the two characters are, like the spaces representing
+a tab character, a single character.
+.PP
+The editor sometimes discards control characters,
+depending on the character and the setting of the
+.I beautify
+option,
+if you attempt to insert them in your file.
+You can get a control character in the file by beginning
+an insert and then typing a \fB^V\fR before the control
+character. The
+\fB^V\fR quotes the following character, causing it to be
+inserted directly into the file.
+.PP
+.NH 2
+Higher level text objects
+.PP
+In working with a document it is often advantageous to work in terms
+of sentences, paragraphs, and sections. The operations \fB(\fR and \fB)\fR
+move to the beginning of the previous and next sentences respectively.
+Thus the command \fBd)\fR will delete the rest of the current sentence;
+likewise \fBd(\fR will delete the previous sentence if you are at the
+beginning of the current sentence, or the current sentence up to where
+you are if you are not at the beginning of the current sentence.
+.PP
+A sentence is defined to end at a `.', `!' or `?' which is followed by
+either the end of a line, or by two spaces. Any number of closing `)',
+`]', `"' and `\(aa' characters may appear after the `.', `!' or `?' before
+the spaces or end of line.
+.PP
+The operations \fB{\fR and \fB}\fR move over paragraphs and the operations
+\fB[[\fR and \fB]]\fR move over sections.\*(dg
+.FS
+\*(dg The \fB[[\fR and \fB]]\fR operations
+require the operation character to be doubled because they can move the
+cursor far from where it currently is. While it is easy to get back
+with the command \fB\(ga\(ga\fP,
+these commands would still be frustrating
+if they were easy to hit accidentally.
+.FE
+.PP
+A paragraph begins after each empty line, and also
+at each of a set of paragraph macros, specified by the pairs of characters
+in the definition of the string valued option \fIparagraphs\fR.
+The default setting for this option defines the paragraph macros of the
+\fI\-ms\fR and \fI\-mm\fR macro packages, i.e. the `.IP', `.LP', `.PP'
+and `.QP', `.P' and `.LI' macros.\*(dd
+.FS
+\*(dd You can easily change or extend this set of macros by assigning a
+different string to the \fIparagraphs\fR option in your EXINIT.
+See section 6.2 for details.
+The `.bp' directive is also considered to start a paragraph.
+.FE
+Each paragraph boundary is also a sentence boundary. The sentence
+and paragraph commands can
+be given counts to operate over groups of sentences and paragraphs.
+.PP
+Sections in the editor begin after each macro in the \fIsections\fR option,
+normally `.NH', `.SH', `.H' and `.HU', and each line with a formfeed \fB^L\fR
+in the first column.
+Section boundaries are always line and paragraph boundaries also.
+.PP
+Try experimenting with the sentence and paragraph commands until you are
+sure how they work. If you have a large document, try looking through
+it using the section commands.
+The section commands interpret a preceding count as a different window size in
+which to redraw the screen at the new location, and this window size
+is the base size for newly drawn windows until another size is specified.
+This is very useful
+if you are on a slow terminal and are looking for a particular section.
+You can give the first section command a small count to then see each successive
+section heading in a small window.
+.NH 2
+Rearranging and duplicating text
+.PP
+The editor has a single unnamed buffer where the last deleted or
+changed away text is saved, and a set of named buffers \fBa\fR\-\fBz\fR
+which you can use to save copies of text and to move text around in
+your file and between files.
+.PP
+The operator
+.B y
+yanks a copy of the object which follows into the unnamed buffer.
+If preceded by a buffer name, \fB"\fR\fIx\fR\|\fBy\fR, where
+\fIx\fR here is replaced by a letter \fBa\-z\fR, it places the text in the named
+buffer. The text can then be put back in the file with the commands
+.B p
+and
+.B P;
+\fBp\fR puts the text after or below the cursor, while \fBP\fR puts the text
+before or above the cursor.
+.PP
+If the text which you
+yank forms a part of a line, or is an object such as a sentence which
+partially spans more than one line, then when you put the text back,
+it will be placed after the cursor (or before if you
+use \fBP\fR). If the yanked text forms whole lines, they will be put
+back as whole lines, without changing the current line. In this case,
+the put acts much like a \fBo\fR or \fBO\fR command.
+.PP
+Try the command \fBYP\fR. This makes a copy of the current line and
+leaves you on this copy, which is placed before the current line.
+The command \fBY\fR is a convenient abbreviation for \fByy\fR.
+The command \fBYp\fR will also make a copy of the current line, and place
+it after the current line. You can give \fBY\fR a count of lines to
+yank, and thus duplicate several lines; try \fB3YP\fR.
+.PP
+To move text within the buffer, you need to delete it in one place, and
+put it back in another. You can precede a delete operation by the
+name of a buffer in which the text is to be stored as in \fB"a5dd\fR
+deleting 5 lines into the named buffer \fIa\fR. You can then move the
+cursor to the eventual resting place of the these lines and do a \fB"ap\fR
+or \fB"aP\fR to put them back.
+In fact, you can switch and edit another file before you put the lines
+back, by giving a command of the form \fB:e \fR\fIname\fR\s-2CR\s0 where
+\fIname\fR is the name of the other file you want to edit. You will
+have to write back the contents of the current editor buffer (or discard
+them) if you have made changes before the editor will let you switch
+to the other file.
+An ordinary delete command saves the text in the unnamed buffer,
+so that an ordinary put can move it elsewhere.
+However, the unnamed buffer is lost when you change files,
+so to move text from one file to another you should use an unnamed buffer.
+.NH 2
+Summary.
+.IP
+.TS
+lw(.50i)b a.
+\(ua first non-white on line
+$ end of line
+) forward sentence
+} forward paragraph
+]] forward section
+( backward sentence
+{ backward paragraph
+[[ backward section
+f\fIx\fR find \fIx\fR forward in line
+p put text back, after cursor or below current line
+y yank operator, for copies and moves
+t\fIx\fR up to \fIx\fR forward, for operators
+F\fIx\fR f backward in line
+P put text back, before cursor or above current line
+T\fIx\fR t backward in line
+.TE
+.ne 1i
+.NH 1
+High level commands
+.NH 2
+Writing, quitting, editing new files
+.PP
+So far we have seen how to enter
+.I vi
+and to write out our file using either
+\fBZZ\fR or \fB:w\fR\s-2CR\s0. The first exits from
+the editor,
+(writing if changes were made),
+the second writes and stays in the editor.
+.PP
+If you have changed the editor's copy of the file but do not wish to
+save your changes, either because you messed up the file or decided that the
+changes are not an improvement to the file, then you can give the command
+\fB:q!\fR\s-2CR\s0 to quit from the editor without writing the changes.
+You can also reedit the same file (starting over) by giving the command
+\fB:e!\fR\s-2CR\s0. These commands should be used only rarely, and with
+caution, as it is not possible to recover the changes you have made after
+you discard them in this manner.
+.PP
+You can edit a different file without leaving the editor by giving the
+command \fB:e\fR\ \fIname\fR\s-2CR\s0. If you have not written out
+your file before you try to do this, then the editor will tell you this,
+and delay editing the other file. You can then give the command
+\fB:w\fR\s-2CR\s0 to save your work and then the \fB:e\fR\ \fIname\fR\s-2CR\s0
+command again, or carefully give the command \fB:e!\fR\ \fIname\fR\s-2CR\s0,
+which edits the other file discarding the changes you have made to the
+current file.
+To have the editor automatically save changes,
+include
+.I "set autowrite"
+in your EXINIT,
+and use \fB:n\fP instead of \fB:e\fP.
+.NH 2
+Escaping to a shell
+.PP
+You can get to a shell to execute a single command by giving a
+.I vi
+command of the form \fB:!\fIcmd\fR\s-2CR\s0.
+The system will run the single command
+.I cmd
+and when the command finishes, the editor will ask you to hit a \s-2RETURN\s0
+to continue. When you have finished looking at the output on the screen,
+you should hit \s-2RETURN\s0 and the editor will clear the screen and
+redraw it. You can then continue editing.
+You can also give another \fB:\fR command when it asks you for a \s-2RETURN\s0;
+in this case the screen will not be redrawn.
+.PP
+If you wish to execute more than one command in the shell, then you can
+give the command \fB:sh\fR\s-2CR\s0.
+This will give you a new shell, and when you finish with the shell, ending
+it by typing a \fB^D\fR, the editor will clear the screen and continue.
+.PP
+On systems which support it, \fB^Z\fP will suspend the editor
+and return to the (top level) shell.
+When the editor is resumed, the screen will be redrawn.
+.NH 2
+Marking and returning
+.PP
+The command \fB\(ga\(ga\fR returned to the previous place
+after a motion of the cursor by a command such as \fB/\fR, \fB?\fR or
+\fBG\fR. You can also mark lines in the file with single letter tags
+and return to these marks later by naming the tags. Try marking the
+current line with the command \fBm\fR\fIx\fR, where you should pick some
+letter for \fIx\fR, say `a'. Then move the cursor to a different line
+(any way you like) and hit \fB\(gaa\fR. The cursor will return to the
+place which you marked.
+Marks last only until you edit another file.
+.PP
+When using operators such as
+.B d
+and referring to marked lines, it is often desirable to delete whole lines
+rather than deleting to the exact position in the line marked by \fBm\fR.
+In this case you can use the form \fB\(aa\fR\fIx\fR rather than
+\fB\(ga\fR\fIx\fR. Used without an operator, \fB\(aa\fR\fIx\fR will move to
+the first non-white character of the marked line; similarly \fB\(aa\(aa\fR
+moves to the first non-white character of the line containing the previous
+context mark \fB\(ga\(ga\fR.
+.NH 2
+Adjusting the screen
+.PP
+If the screen image is messed up because of a transmission error to your
+terminal, or because some program other than the editor wrote output
+to your terminal, you can hit a \fB^L\fR, the \s-2ASCII\s0 form-feed
+character, to cause the screen to be refreshed.
+.PP
+On a dumb terminal, if there are @ lines in the middle of the screen
+as a result of line deletion, you may get rid of these lines by typing
+\fB^R\fR to cause the editor to retype the screen, closing up these holes.
+.PP
+Finally, if you wish to place a certain line on the screen at the top
+middle or bottom of the screen, you can position the cursor to that line,
+and then give a \fBz\fR command.
+You should follow the \fBz\fR command with a \s-2RETURN\s0 if you want
+the line to appear at the top of the window, a \fB.\fR if you want it
+at the center, or a \fB\-\fR if you want it at the bottom.
+.NH 1
+Special topics
+.NH 2
+Editing on slow terminals
+.PP
+When you are on a slow terminal, it is important to limit the amount
+of output which is generated to your screen so that you will not suffer
+long delays, waiting for the screen to be refreshed. We have already
+pointed out how the editor optimizes the updating of the screen during
+insertions on dumb terminals to limit the delays, and how the editor erases
+lines to @ when they are deleted on dumb terminals.
+.PP
+The use of the slow terminal insertion mode is controlled by the
+.I slowopen
+option. You can force the editor to use this mode even on faster terminals
+by giving the command \fB:se slow\fR\s-2CR\s0. If your system is sluggish
+this helps lessen the amount of output coming to your terminal.
+You can disable this option by \fB:se noslow\fR\s-2CR\s0.
+.PP
+The editor can simulate an intelligent terminal on a dumb one. Try
+giving the command \fB:se redraw\fR\s-2CR\s0. This simulation generates
+a great deal of output and is generally tolerable only on lightly loaded
+systems and fast terminals. You can disable this by giving the command
+ \fB:se noredraw\fR\s-2CR\s0.
+.PP
+The editor also makes editing more pleasant at low speed by starting
+editing in a small window, and letting the window expand as you edit.
+This works particularly well on intelligent terminals. The editor can
+expand the window easily when you insert in the middle of the screen
+on these terminals. If possible, try the editor on an intelligent terminal
+to see how this works.
+.PP
+You can control the size of the window which is redrawn each time the
+screen is cleared by giving window sizes as argument to the commands
+which cause large screen motions:
+.DS
+.B ": / ? [[ ]] \(ga \(aa"
+.DE
+Thus if you are searching for a particular instance of a common string
+in a file you can precede the first search command by a small number,
+say 3, and the editor will draw three line windows around each instance
+of the string which it locates.
+.PP
+You can easily expand or contract the window, placing the current line
+as you choose, by giving a number on a \fBz\fR command, after the \fBz\fR
+and before the following \s-2RETURN\s0, \fB.\fR or \fB\-\fR. Thus the
+command \fBz5.\fR redraws the screen with the current line in the center
+of a five line window.\*(dg
+.FS
+\*(dg Note that the command \fB5z.\fR has an entirely different effect,
+placing line 5 in the center of a new window.
+.FE
+.PP
+If the editor is redrawing or otherwise updating large portions of the
+display, you can interrupt this updating by hitting a \s-2DEL\s0 or \s-2RUB\s0
+as usual. If you do this you may partially confuse the editor about
+what is displayed on the screen. You can still edit the text on
+the screen if you wish; clear up the confusion
+by hitting a \fB^L\fR; or move or search again, ignoring the
+current state of the display.
+.PP
+See section 7.8 on \fIopen\fR mode for another way to use the
+.I vi
+command set on slow terminals.
+.NH 2
+Options, set, and editor startup files
+.PP
+The editor has a set of options, some of which have been mentioned above.
+The most useful options are given in the following table.
+.PP
+The options are of three kinds: numeric options, string options, and
+toggle options. You can set numeric and string options by a statement
+of the form
+.DS
+\fBset\fR \fIopt\fR\fB=\fR\fIval\fR
+.DE
+and toggle options can be set or unset by statements of one of the forms
+.DS
+\fBset\fR \fIopt\fR
+\fBset\fR \fBno\fR\fIopt\fR
+.DE
+.KF
+.TS
+lb lb lb lb
+l l l a.
+Name Default Description
+_
+autoindent noai Supply indentation automatically
+autowrite noaw Automatic write before \fB:n\fR, \fB:ta\fR, \fB^\(ua\fR, \fB!\fR
+ignorecase noic Ignore case in searching
+lisp nolisp \fB( { ) }\fR commands deal with S-expressions
+list nolist Tabs print as ^I; end of lines marked with $
+magic nomagic The characters . [ and * are special in scans
+number nonu Lines are displayed prefixed with line numbers
+paragraphs para=IPLPPPQPbpP LI Macro names which start paragraphs
+redraw nore Simulate a smart terminal on a dumb one
+sections sect=NHSHH HU Macro names which start new sections
+shiftwidth sw=8 Shift distance for <, > and input \fB^D\fP and \fB^T\fR
+showmatch nosm Show matching \fB(\fP or \fB{\fP as \fB)\fP or \fB}\fR is typed
+slowopen slow Postpone display updates during inserts
+term dumb The kind of terminal you are using.
+.TE
+.KE
+These statements can be placed in your EXINIT in your environment,
+or given while you are running
+.I vi
+by preceding them with a \fB:\fR and following them with a \s-2CR\s0.
+.PP
+You can get a list of all options which you have changed by the
+command \fB:set\fR\s-2CR\s0, or the value of a single option by the
+command \fB:set\fR \fIopt\fR\fB?\fR\s-2CR\s0.
+A list of all possible options and their values is generated by
+\fB:set all\fP\s-2CR\s0.
+Set can be abbreviated \fBse\fP.
+Multiple options can be placed on one line, e.g.
+\fB:se ai aw nu\fP\s-2CR\s0.
+.PP
+Options set by the \fBset\fP command only last
+while you stay in the editor.
+It is common to want to have certain options set whenever you
+use the editor.
+This can be accomplished by creating a list of \fIex\fP commands\*(dg
+.FS
+\*(dg
+All commands which start with
+.B :
+are \fIex\fP commands.
+.FE
+which are to be run every time you start up \fIex\fP, \fIedit\fP,
+or \fIvi\fP.
+A typical list includes a \fBset\fP command, and possibly a few
+\fBmap\fP commands.
+Since it is advisable to get these commands on one line, they can
+be separated with the | character, for example:
+.DS
+\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x
+.DE
+which sets the options \fIautoindent\fP, \fIautowrite\fP, \fIterse\fP,
+(the
+.B set
+command),
+makes @ delete a line,
+(the first
+.B map ),
+and makes # delete a character,
+(the second
+.B map ).
+(See section 6.9 for a description of the \fBmap\fP command)
+This string should be placed in the variable EXINIT in your environment.
+If you use the shell \fIcsh\fP,
+put this line in the file
+.I .login
+in your home directory:
+.DS
+setenv EXINIT \(aa\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x\(aa
+.DE
+If you use the standard shell \fIsh\fP,
+put these lines in the file
+.I .profile
+in your home directory:
+.DS
+EXINIT=\(aa\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x\(aa
+export EXINIT
+.DE
+Of course, the particulars of the line would depend on which options
+you wanted to set.
+.NH 2
+Recovering lost lines
+.PP
+You might have a serious problem if you delete a number of lines and then
+regret that they were deleted. Despair not, the editor saves the last
+9 deleted blocks of text in a set of numbered registers 1\-9.
+You can get the \fIn\fR'th previous deleted text back in your file by
+the command
+"\fR\fIn\fR\|\fBp\fR.
+The "\fR here says that a buffer name is to follow,
+\fIn\fR is the number of the buffer you wish to try
+(use the number 1 for now),
+and
+.B p
+is the put command, which puts text in the buffer after the cursor.
+If this doesn't bring back the text you wanted, hit
+.B u
+to undo this and then
+\fB\&.\fR
+(period)
+to repeat the put command.
+In general the
+\fB\&.\fR
+command will repeat the last change you made.
+As a special case, when the last command refers to a numbered text buffer,
+the \fB.\fR command increments the number of the buffer before repeating
+the command. Thus a sequence of the form
+.DS
+\fB"1pu.u.u.\fR
+.DE
+will, if repeated long enough, show you all the deleted text which has
+been saved for you.
+You can omit the
+.B u
+commands here to gather up all this text in the buffer, or stop after any
+\fB\&.\fR command to keep just the then recovered text.
+The command
+.B P
+can also be used rather than
+.B p
+to put the recovered text before rather than after the cursor.
+.NH 2
+Recovering lost files
+.PP
+If the system crashes, you can recover the work you were doing
+to within a few changes. You will normally receive mail when you next
+login giving you the name of the file which has been saved for you.
+You should then change to the directory where you were when the system
+crashed and give a command of the form:
+.DS
+% \fBvi \-r\fR \fIname\fR
+.DE
+replacing \fIname\fR with the name of the file which you were editing.
+This will recover your work to a point near where you left off.\*(dg
+.FS
+\*(dg In rare cases, some of the lines of the file may be lost. The
+editor will give you the numbers of these lines and the text of the lines
+will be replaced by the string `LOST'. These lines will almost always
+be among the last few which you changed. You can either choose to discard
+the changes which you made (if they are easy to remake) or to replace
+the few lost lines by hand.
+.FE
+.PP
+You can get a listing of the files which are saved for you by giving
+the command:
+.DS
+% \fBvi \-r\fR
+.DE
+If there is more than one instance of a particular file saved, the editor
+gives you the newest instance each time you recover it. You can thus
+get an older saved copy back by first recovering the newer copies.
+.PP
+For this feature to work,
+.I vi
+must be correctly installed by a super user on your system,
+and the
+.I mail
+program must exist to receive mail.
+The invocation ``\fIvi -r\fP'' will not always list all saved files,
+but they can be recovered even if they are not listed.
+.NH 2
+Continuous text input
+.PP
+When you are typing in large amounts of text it is convenient to have
+lines broken near the right margin automatically. You can cause this
+to happen by giving the command
+\fB:se wm=10\fR\s-2CR\s0.
+This causes all lines to be broken at a space at least 10 columns
+from the right hand edge of the screen.
+.PP
+If the editor breaks an input line and you wish to put it back together
+you can tell it to join the lines with \fBJ\fR. You can give \fBJ\fR
+a count of the number of lines to be joined as in \fB3J\fR to join 3
+lines. The editor supplies white space, if appropriate,
+at the juncture of the joined
+lines, and leaves the cursor at this white space.
+You can kill the white space with \fBx\fR if you don't want it.
+.NH 2
+Features for editing programs
+.PP
+The editor has a number of commands for editing programs.
+The thing that most distinguishes editing of programs from editing of text
+is the desirability of maintaining an indented structure to the body of
+the program. The editor has a
+.I autoindent
+facility for helping you generate correctly indented programs.
+.PP
+To enable this facility you can give the command \fB:se ai\fR\s-2CR\s0.
+Now try opening a new line with \fBo\fR and type some characters on the
+line after a few tabs. If you now start another line, notice that the
+editor supplies white space at the beginning of the line to line it up
+with the previous line. You cannot backspace over this indentation,
+but you can use \fB^D\fR key to backtab over the supplied indentation.
+.PP
+Each time you type \fB^D\fR you back up one position, normally to an
+8 column boundary. This amount is settable; the editor has an option
+called
+.I shiftwidth
+which you can set to change this value.
+Try giving the command \fB:se sw=4\fR\s-2CR\s0
+and then experimenting with autoindent again.
+.PP
+For shifting lines in the program left and right, there are operators
+.B <
+and
+.B >.
+These shift the lines you specify right or left by one
+.I shiftwidth.
+Try
+.B <<
+and
+.B >>
+which shift one line left or right, and
+.B <L
+and
+.B >L
+shifting the rest of the display left and right.
+.PP
+If you have a complicated expression and wish to see how the parentheses
+match, put the cursor at a left or right parenthesis and hit \fB%\fR.
+This will show you the matching parenthesis.
+This works also for braces { and }, and brackets [ and ].
+.PP
+If you are editing C programs, you can use the \fB[[\fR and \fB]]\fR keys
+to advance or retreat to a line starting with a \fB{\fR, i.e. a function
+declaration at a time. When \fB]]\fR is used with an operator it stops
+after a line which starts with \fB}\fR; this is sometimes useful with
+\fBy]]\fR.
+.NH 2
+Filtering portions of the buffer
+.PP
+You can run system commands over portions of the buffer using the operator
+\fB!\fR.
+You can use this to sort lines in the buffer, or to reformat portions
+of the buffer with a pretty-printer.
+Try typing in a list of random words, one per line and ending them
+with a blank line. Back up to the beginning of the list, and then give
+the command \fB!}sort\fR\s-2CR\s0. This says to sort the next paragraph
+of material, and the blank line ends a paragraph.
+.NH 2
+Commands for editing \s-2LISP\s0
+.PP
+If you are editing a \s-2LISP\s0 program you should set the option
+.I lisp
+by doing
+\fB:se\ lisp\fR\s-2CR\s0.
+This changes the \fB(\fR and \fB)\fR commands to move backward and forward
+over s-expressions.
+The \fB{\fR and \fB}\fR commands are like \fB(\fR and \fB)\fR but don't
+stop at atoms. These can be used to skip to the next list, or through
+a comment quickly.
+.PP
+The
+.I autoindent
+option works differently for \s-2LISP\s0, supplying indent to align at
+the first argument to the last open list. If there is no such argument
+then the indent is two spaces more than the last level.
+.PP
+There is another option which is useful for typing in \s-2LISP\s0, the
+.I showmatch
+option.
+Try setting it with
+\fB:se sm\fR\s-2CR\s0
+and then try typing a `(' some words and then a `)'. Notice that the
+cursor shows the position of the `(' which matches the `)' briefly.
+This happens only if the matching `(' is on the screen, and the cursor
+stays there for at most one second.
+.PP
+The editor also has an operator to realign existing lines as though they
+had been typed in with
+.I lisp
+and
+.I autoindent
+set. This is the \fB=\fR operator.
+Try the command \fB=%\fR at the beginning of a function. This will realign
+all the lines of the function declaration.
+.PP
+When you are editing \s-2LISP\s0,, the \fB[[\fR and \fR]]\fR advance
+and retreat to lines beginning with a \fB(\fR, and are useful for dealing
+with entire function definitions.
+.NH 2
+Macros
+.PP
+.I Vi
+has a parameterless macro facility, which lets you set it up so that
+when you hit a single keystroke, the editor will act as though
+you had hit some longer sequence of keys. You can set this up if
+you find yourself typing the same sequence of commands repeatedly.
+.PP
+Briefly, there are two flavors of macros:
+.IP a)
+Ones where you put the macro body in a buffer register, say \fIx\fR.
+You can then type \fB@x\fR to invoke the macro. The \fB@\fR may be followed
+by another \fB@\fR to repeat the last macro.
+.IP b)
+You can use the
+.I map
+command from
+.I vi
+(typically in your
+.I EXINIT )
+with a command of the form:
+.DS
+:map \fIlhs\fR \fIrhs\fR\s-2CR
+.DE
+mapping
+.I lhs
+into
+.I rhs.
+There are restrictions:
+.I lhs
+should be one keystroke (either 1 character or one function key)
+since it must be entered within one second
+(unless
+.I notimeout
+is set, in which case you can type it as slowly as you wish,
+and
+.I vi
+will wait for you to finish it before it echoes anything).
+The
+.I lhs
+can be no longer than 10 characters, the
+.I rhs
+no longer than 100.
+To get a space, tab or newline into
+.I lhs
+or
+.I rhs
+you should escape them with a \fB^V\fR.
+(It may be necessary to double the \fB^V\fR if the map
+command is given inside
+.I vi,
+rather than in
+.I ex.)
+Spaces and tabs inside the
+.I rhs
+need not be escaped.
+.PP
+Thus to make the \fBq\fR key write and exit the editor, you can give
+the command
+.DS
+:map q :wq\fB^V^V\fP\s-2CR CR\s0
+.DE
+which means that whenever you type \fBq\fR, it will be as though you
+had typed the four characters \fB:wq\fR\s-2CR\s0.
+A \fB^V\fR's is needed because without it the \s-2CR\s0 would end the
+\fB:\fR command, rather than becoming part of the
+.I map
+definition.
+There are two
+.B ^V 's
+because from within
+.I vi ,
+two
+.B ^V 's
+must be typed to get one.
+The first \s-2CR\s0 is part of the
+.I rhs ,
+the second terminates the : command.
+.PP
+Macros can be deleted with
+.DS
+unmap lhs
+.DE
+.PP
+If the
+.I lhs
+of a macro is ``#0'' through ``#9'', this maps the particular function key
+instead of the 2 character ``#'' sequence. So that terminals without
+function keys can access such definitions, the form ``#x'' will mean function
+key
+.I x
+on all terminals (and need not be typed within one second.)
+The character ``#'' can be changed by using a macro in the usual way:
+.DS
+:map \fB^V^V^I\fP #
+.DE
+to use tab, for example. (This won't affect the
+.I map
+command, which still uses
+.B #,
+but just the invocation from visual mode.
+.PP
+The undo command reverses an entire macro call as a unit,
+if it made any changes.
+.PP
+Placing a `!' after the word
+.B map
+causes the mapping to apply
+to input mode, rather than command mode.
+Thus, to arrange for \fB^T\fP to be the same as 4 spaces in input mode,
+you can type:
+.DS
+:map \fB^T\fP \fB^V\fP\o'b/'\o'b/'\o'b/'\o'b/'
+.DE
+where
+.B \o'b/'
+is a blank.
+The \fB^V\fP is necessary to prevent the blanks from being taken as
+white space between the
+.I lhs
+and
+.I rhs .
+.NH
+Word Abbreviations
+.PP
+A feature similar to macros in input mode is word abbreviation.
+This allows you to type a short word and have it expanded into
+a longer word or words.
+The commands are
+.B :abbreviate
+and
+.B :unabbreviate
+(\fB:ab\fP
+and
+.B :una )
+and have the same syntax as
+.B :map .
+For example:
+.DS
+:ab eecs Electrical Engineering and Computer Sciences
+.DE
+causes the word `eecs' to always be changed into the
+phrase `Electrical Engineering and Computer Sciences'.
+Word abbreviation is different from macros in that
+only whole words are affected.
+If `eecs' were typed as part of a larger word, it would
+be left alone.
+Also, the partial word is echoed as it is typed.
+There is no need for an abbreviation to be a single keystroke,
+as it should be with a macro.
+.NH 2
+Abbreviations
+.PP
+The editor has a number of short
+commands which abbreviate longer commands which we
+have introduced here. You can find these commands easily
+on the quick reference card.
+They often save a bit of typing and you can learn them as convenient.
+.NH 1
+Nitty-gritty details
+.NH 2
+Line representation in the display
+.PP
+The editor folds long logical lines onto many physical lines in the display.
+Commands which advance lines advance logical lines and will skip
+over all the segments of a line in one motion. The command \fB|\fR moves
+the cursor to a specific column, and may be useful for getting near the
+middle of a long line to split it in half. Try \fB80|\fR on a line which
+is more than 80 columns long.\*(dg
+.FS
+\*(dg You can make long lines very easily by using \fBJ\fR to join together
+short lines.
+.FE
+.PP
+The editor only puts full lines on the display; if there is not enough
+room on the display to fit a logical line, the editor leaves the physical
+line empty, placing only an @ on the line as a place holder. When you
+delete lines on a dumb terminal, the editor will often just clear the
+lines to @ to save time (rather than rewriting the rest of the screen.)
+You can always maximize the information on the screen by giving the \fB^R\fR
+command.
+.PP
+If you wish, you can have the editor place line numbers before each line
+on the display. Give the command \fB:se nu\fR\s-2CR\s0 to enable
+this, and the command \fB:se nonu\fR\s-2CR\s0 to turn it off.
+You can have tabs represented as \fB^I\fR and the ends of lines indicated
+with `$' by giving the command \fB:se list\fR\s-2CR\s0;
+\fB:se nolist\fR\s-2CR\s0 turns this off.
+.PP
+Finally, lines consisting of only the character `~' are displayed when
+the last line in the file is in the middle of the screen. These represent
+physical lines which are past the logical end of file.
+.NH 2
+Counts
+.PP
+Most
+.I vi
+commands will use a preceding count to affect their behavior in some way.
+The following table gives the common ways in which the counts are used:
+.DS
+.TS
+l lb.
+new window size : / ? [[ ]] \` \'
+scroll amount ^D ^U
+line/column number z G |
+repeat effect \fRmost of the rest\fP
+.TE
+.DE
+.PP
+The editor maintains a notion of the current default window size.
+On terminals which run at speeds greater than 1200 baud
+the editor uses the full terminal screen.
+On terminals which are slower than 1200 baud
+(most dialup lines are in this group)
+the editor uses 8 lines as the default window size.
+At 1200 baud the default is 16 lines.
+.PP
+This size is the size used when the editor clears and refills the screen
+after a search or other motion moves far from the edge of the current window.
+The commands which take a new window size as count all often cause the
+screen to be redrawn. If you anticipate this, but do not need as large
+a window as you are currently using, you may wish to change the screen
+size by specifying the new size before these commands.
+In any case, the number of lines used on the screen will expand if you
+move off the top with a \fB\-\fR or similar command or off the bottom
+with a command such as \s-2RETURN\s0 or \fB^D\fR.
+The window will revert to the last specified size the next time it is
+cleared and refilled.\*(dg
+.FS
+\*(dg But not by a \fB^L\fR which just redraws the screen as it is.
+.FE
+.PP
+The scroll commands \fB^D\fR and \fB^U\fR likewise remember the amount
+of scroll last specified, using half the basic window size initially.
+The simple insert commands use a count to specify a repetition of the
+inserted text. Thus \fB10a+\-\-\-\-\fR\s-2ESC\s0 will insert a grid-like
+string of text.
+A few commands also use a preceding count as a line or column number.
+.PP
+Except for a few commands which ignore any counts (such as \fB^R\fR),
+the rest of the editor commands use a count to indicate a simple repetition
+of their effect. Thus \fB5w\fR advances five words on the current line,
+while \fB5\fR\s-2RETURN\s0 advances five lines. A very useful instance
+of a count as a repetition is a count given to the \fB.\fR command, which
+repeats the last changing command. If you do \fBdw\fR and then \fB3.\fR,
+you will delete first one and then three words. You can then delete
+two more words with \fB2.\fR.
+.NH 2
+More file manipulation commands
+.PP
+The following table lists the file manipulation commands which you can
+use when you are in
+.I vi.
+.KF
+.DS
+.TS
+lb l.
+:w write back changes
+:wq write and quit
+:x write (if necessary) and quit (same as ZZ).
+:e \fIname\fP edit file \fIname\fR
+:e! reedit, discarding changes
+:e + \fIname\fP edit, starting at end
+:e +\fIn\fP edit, starting at line \fIn\fP
+:e # edit alternate file
+:w \fIname\fP write file \fIname\fP
+:w! \fIname\fP overwrite file \fIname\fP
+:\fIx,y\fPw \fIname\fP write lines \fIx\fP through \fIy\fP to \fIname\fP
+:r \fIname\fP read file \fIname\fP into buffer
+:r !\fIcmd\fP read output of \fIcmd\fP into buffer
+:n edit next file in argument list
+:n! edit next file, discarding changes to current
+:n \fIargs\fP specify new argument list
+:ta \fItag\fP edit file containing tag \fItag\fP, at \fItag\fP
+.TE
+.DE
+.KE
+All of these commands are followed by a \s-2CR\s0 or \s-2ESC\s0.
+The most basic commands are \fB:w\fR and \fB:e\fR.
+A normal editing session on a single file will end with a \fBZZ\fR command.
+If you are editing for a long period of time you can give \fB:w\fR commands
+occasionally after major amounts of editing, and then finish
+with a \fBZZ\fR. When you edit more than one file, you can finish
+with one with a \fB:w\fR and start editing a new file by giving a \fB:e\fR
+command,
+or set
+.I autowrite
+and use \fB:n\fP <file>.
+.PP
+If you make changes to the editor's copy of a file, but do not wish to
+write them back, then you must give an \fB!\fR after the command you
+would otherwise use; this forces the editor to discard any changes
+you have made. Use this carefully.
+.ne 1i
+.PP
+The \fB:e\fR command can be given a \fB+\fR argument to start at the
+end of the file, or a \fB+\fR\fIn\fR argument to start at line \fIn\fR\^.
+In actuality, \fIn\fR may be any editor command not containing a space,
+usefully a scan like \fB+/\fIpat\fR or \fB+?\fIpat\fR.
+In forming new names to the \fBe\fR command, you can use the character
+\fB%\fR which is replaced by the current file name, or the character
+\fB#\fR which is replaced by the alternate file name.
+The alternate file name is generally the last name you typed other than
+the current file. Thus if you try to do a \fB:e\fR and get a diagnostic
+that you haven't written the file, you can give a \fB:w\fR command and
+then a \fB:e #\fR command to redo the previous \fB:e\fR.
+.PP
+You can write part of the buffer to a file by finding out the lines
+that bound the range to be written using \fB^G\fR, and giving these
+numbers after the \fB:\fR
+and before the \fBw\fP, separated by \fB,\fR's.
+You can also mark these lines with \fBm\fR and
+then use an address of the form \fB\(aa\fR\fIx\fR\fB,\fB\(aa\fR\fIy\fR
+on the \fBw\fR command here.
+.PP
+You can read another file into the buffer after the current line by using
+the \fB:r\fR command.
+You can similarly read in the output from a command, just use \fB!\fR\fIcmd\fR
+instead of a file name.
+.PP
+If you wish to edit a set of files in succession, you can give all the
+names on the command line, and then edit each one in turn using the command
+\fB:n\fR. It is also possible to respecify the list of files to be edited
+by giving the \fB:n\fR command a list of file names, or a pattern to
+be expanded as you would have given it on the initial
+.I vi
+command.
+.PP
+If you are editing large programs, you will find the \fB:ta\fR command
+very useful. It utilizes a data base of function names and their locations,
+which can be created by programs such as
+.I ctags,
+to quickly find a function whose name you give.
+If the \fB:ta\fR command will require the editor to switch files, then
+you must \fB:w\fR or abandon any changes before switching. You can repeat
+the \fB:ta\fR command without any arguments to look for the same tag
+again.
+.NH 2
+More about searching for strings
+.PP
+When you are searching for strings in the file with \fB/\fR and \fB?\fR,
+the editor normally places you at the next or previous occurrence
+of the string. If you are using an operator such as \fBd\fR,
+\fBc\fR or \fBy\fR, then you may well wish to affect lines up to the
+line before the line containing the pattern. You can give a search of
+the form \fB/\fR\fIpat\fR\fB/\-\fR\fIn\fR to refer to the \fIn\fR'th line
+before the next line containing \fIpat\fR, or you can use \fB+\fR instead
+of \fB\-\fR to refer to the lines after the one containing \fIpat\fR.
+If you don't give a line offset, then the editor will affect characters
+up to the match place, rather than whole lines; thus use ``+0'' to affect
+to the line which matches.
+.PP
+You can have the editor ignore the case of words in the searches it does
+by giving the command \fB:se ic\fR\s-2CR\s0.
+The command \fB:se noic\fR\s-2CR\s0 turns this off.
+.ne 1i
+.PP
+Strings given to searches may actually be regular expressions.
+If you do not want or need this facility, you should
+.DS
+set nomagic
+.DE
+in your EXINIT.
+In this case,
+only the characters \fB\(ua\fR and \fB$\fR are special in patterns.
+The character \fB\e\fR is also then special (as it is most everywhere in
+the system), and may be used to get at the
+an extended pattern matching facility.
+It is also necessary to use a \e before a
+\fB/\fR in a forward scan or a \fB?\fR in a backward scan, in any case.
+The following table gives the extended forms when \fBmagic\fR is set.
+.DS
+.TS
+lb l.
+\(ua at beginning of pattern, matches beginning of line
+$ at end of pattern, matches end of line
+\fB\&.\fR matches any character
+\e< matches the beginning of a word
+\e> matches the end of a word
+[\fIstr\fP] matches any single character in \fIstr\fP
+[\(ua\fIstr\fP] matches any single character not in \fIstr\fP
+[\fIx\fP\-\fIy\fP] matches any character between \fIx\fP and \fIy\fP
+* matches any number of the preceding pattern
+.TE
+.DE
+If you use \fBnomagic\fR mode, then
+the \fB. [\fR and \fB*\fR primitives are given with a preceding
+\e.
+.NH 2
+More about input mode
+.PP
+There are a number of characters which you can use to make corrections
+during input mode. These are summarized in the following table.
+.sp .5
+.DS
+.TS
+lb l.
+^H deletes the last input character
+^W deletes the last input word, defined as by \fBb\fR
+erase your erase character, same as \fB^H\fP
+kill your kill character, deletes the input on this line
+\e escapes a following \fB^H\fP and your erase and kill
+\s-2ESC\s0 ends an insertion
+\s-2DEL\s0 interrupts an insertion, terminating it abnormally
+\s-2CR\s0 starts a new line
+^D backtabs over \fIautoindent\fP
+0^D kills all the \fIautoindent\fP
+\(ua^D same as \fB0^D\fP, but restores indent next line
+^V quotes the next non-printing character into the file
+.TE
+.DE
+.sp .5
+.PP
+The most usual way of making corrections to input is by typing \fB^H\fR
+to correct a single character, or by typing one or more \fB^W\fR's to
+back over incorrect words. If you use \fB#\fR as your erase character
+in the normal system, it will work like \fB^H\fR.
+.PP
+Your system kill character, normally \fB@\fR, \fB^X\fP or \fB^U\fR,
+will erase all
+the input you have given on the current line.
+In general, you can neither
+erase input back around a line boundary nor can you erase characters
+which you did not insert with this insertion command. To make corrections
+on the previous line after a new line has been started you can hit \s-2ESC\s0
+to end the insertion, move over and make the correction, and then return
+to where you were to continue. The command \fBA\fR which appends at the
+end of the current line is often useful for continuing.
+.PP
+If you wish to type in your erase or kill character (say # or @) then
+you must precede it with a \fB\e\fR, just as you would do at the normal
+system command level. A more general way of typing non-printing characters
+into the file is to precede them with a \fB^V\fR. The \fB^V\fR echoes
+as a \fB\(ua\fR character on which the cursor rests. This indicates that
+the editor expects you to type a control character. In fact you may
+type any character and it will be inserted into the file at that point.*
+.FS
+* This is not quite true. The implementation of the editor does
+not allow the \s-2NULL\s0 (\fB^@\fR) character to appear in files. Also
+the \s-2LF\s0 (linefeed or \fB^J\fR) character is used by the editor
+to separate lines in the file, so it cannot appear in the middle of a
+line. You can insert any other character, however, if you wait for the
+editor to echo the \fB\(ua\fR before you type the character. In fact,
+the editor will treat a following letter as a request for the corresponding
+control character. This is the only way to type \fB^S\fR or \fB^Q\fP,
+since the system normally uses them to suspend and resume output
+and never gives them to the editor to process.
+.FE
+.PP
+If you are using \fIautoindent\fR you can backtab over the indent which
+it supplies by typing a \fB^D\fR. This backs up to a \fIshiftwidth\fR
+boundary.
+This only works immediately after the supplied \fIautoindent\fR.
+.PP
+When you are using \fIautoindent\fR you may wish to place a label at
+the left margin of a line. The way to do this easily is to type \fB\(ua\fR
+and then \fB^D\fR. The editor will move the cursor to the left margin
+for one line, and restore the previous indent on the next. You can also
+type a \fB0\fR followed immediately by a \fB^D\fR if you wish to kill
+all the indent and not have it come back on the next line.
+.NH 2
+Upper case only terminals
+.PP
+If your terminal has only upper case, you can still use
+.I vi
+by using the normal
+system convention for typing on such a terminal.
+Characters which you normally type are converted to lower case, and you
+can type upper case letters by preceding them with a \e.
+The characters { ~ } | \(ga are not available on such terminals, but you
+can escape them as \e( \e\(ua \e) \e! \e\(aa.
+These characters are represented on the display in the same way they
+are typed.\*(dd
+.FS
+\*(dd The \e character you give will not echo until you type another
+key.
+.FE
+.NH 2
+Vi and ex
+.PP
+.I Vi
+is actually one mode of editing within the editor
+.I ex.
+When you are running
+.I vi
+you can escape to the line oriented editor of
+.I ex
+by giving the command
+\fBQ\fR.
+All of the
+.B :
+commands which were introduced above are available in
+.I ex.
+Likewise, most
+.I ex
+commands can be invoked from
+.I vi
+using :.
+Just give them without the \fB:\fR and follow them with a \s-2CR\s0.
+.PP
+In rare instances, an internal error may occur in
+.I vi.
+In this case you will get a diagnostic and be left in the command mode of
+.I ex.
+You can then save your work and quit if you wish by giving a command
+\fBx\fR after the \fB:\fR which \fIex\fR prompts you with, or you can
+reenter \fIvi\fR by giving
+.I ex
+a
+.I vi
+command.
+.PP
+There are a number of things which you can do more easily in
+.I ex
+than in
+.I vi.
+Systematic changes in line oriented material are particularly easy.
+You can read the advanced editing documents for the editor
+.I ed
+to find out a lot more about this style of editing.
+Experienced
+users often mix their use of
+.I ex
+command mode and
+.I vi
+command mode to speed the work they are doing.
+.NH 2
+Open mode: vi on hardcopy terminals and ``glass tty's''
+\(dd
+.PP
+If you are on a hardcopy terminal or a terminal which does not have a cursor
+which can move off the bottom line, you can still use the command set of
+.I vi,
+but in a different mode.
+When you give a
+.I vi
+command, the editor will tell you that it is using
+.I open
+mode.
+This name comes from the
+.I open
+command in
+.I ex,
+which is used to get into the same mode.
+.PP
+The only difference between
+.I visual
+mode
+and
+.I open
+mode is the way in which the text is displayed.
+.PP
+In
+.I open
+mode the editor uses a single line window into the file, and moving backward
+and forward in the file causes new lines to be displayed, always below the
+current line.
+Two commands of
+.I vi
+work differently in
+.I open:
+.B z
+and
+\fB^R\fR.
+The
+.B z
+command does not take parameters, but rather draws a window of context around
+the current line and then returns you to the current line.
+.PP
+If you are on a hardcopy terminal,
+the
+.B ^R
+command will retype the current line.
+On such terminals, the editor normally uses two lines to represent the
+current line.
+The first line is a copy of the line as you started to edit it, and you work
+on the line below this line.
+When you delete characters, the editor types a number of \e's to show
+you the characters which are deleted. The editor also reprints the current
+line soon after such changes so that you can see what the line looks
+like again.
+.PP
+It is sometimes useful to use this mode on very slow terminals which
+can support
+.I vi
+in the full screen mode.
+You can do this by entering
+.I ex
+and using an
+.I open
+command.
+.LP
+.SH
+Acknowledgements
+.PP
+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.
diff --git a/contrib/nvi/docs/USD.doc/vitut/vi.summary b/contrib/nvi/docs/USD.doc/vitut/vi.summary
new file mode 100644
index 000000000000..8a09ce944407
--- /dev/null
+++ b/contrib/nvi/docs/USD.doc/vitut/vi.summary
@@ -0,0 +1,468 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.summary 8.3 (Berkeley) 8/18/96
+.\"
+.ds CH
+.ds CF
+.de TS
+.br
+.if !\\n(1T .RT
+.ul 0
+.ti \\n(.iu
+.if t .sp 0.25
+.if n .sp
+.if \\$1H .TQ
+.nr IX 1
+..
+.nr PS 9
+.ps 9
+.nr VS 11
+.vs 11
+.nr HM .50i
+.nr FM .25i
+.nr PO 1.0i
+.po 1.0i
+.nr LL 4.5i
+.ll 4.5i
+.de nc
+.bp
+..
+.de h
+.LG
+.B
+\\$1
+.R
+.NL
+..
+.LG
+.LG
+.B
+.ce
+Ex Quick Reference
+.R
+.NL
+.LP
+.LP
+.h "Entering/leaving ex"
+.TS
+aw(1.4i)b aw(1.8i).
+% ex \fIname\fP edit \fIname\fP, start at end
+% ex +\fIn\fP \fIname\fP ... at line \fIn\fP
+% ex \-t \fItag\fP start at \fItag\fP
+% ex \-r list saved files
+% ex \-r \fIname\fP recover file \fIname\fP
+% ex \fIname\fP ... edit first; rest via \fB:n\fP
+% ex \-R \fIname\fP read only mode
+: x exit, saving changes
+: q! exit, discarding changes
+.TE
+.h "Ex states"
+.TS
+lw(1i) lw(2.0i).
+Command T{
+Normal and initial state. Input prompted for by \fB:\fP.
+Your kill character cancels partial command.
+T}
+Insert T{
+Entered by \fBa\fP \fBi\fP and \fBc\fP.
+Arbitrary text then terminates with line having only \fB.\fP
+character on it or abnormally with interrupt.
+T}
+Open/visual T{
+Entered by \fBopen\fP or \fBvi\fP, terminates with \fBQ\fP
+or ^\e.
+T}
+.TE
+.h "Ex commands"
+.TS
+lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b.
+abbrev ab next n unabbrev una
+append a number nu undo u
+args ar open o unmap unm
+change c preserve pre version ve
+copy co print p visual vi
+delete d put pu write w
+edit e quit q xit x
+file f read re yank ya
+global g recover rec \fIwindow\fP z
+insert i rewind rew \fIescape\fP !
+join j set se \fIlshift\fP <
+list l shell sh \fIprint next\fP \fRCR\fP
+map source so \fIresubst\fP &
+mark ma stop st \fIrshift\fP >
+move m substitute s \fIscroll\fP ^D
+.TE
+.h "Ex command addresses"
+.TS
+lw(.3i)b lw(0.8i) lw(.3i)b lw(0.8i).
+\fIn\fP line \fIn\fP /\fIpat\fP next with \fIpat\fP
+\&. current ?\fIpat\fP previous with \fIpat\fP
+$ last \fIx\fP-\fIn\fP \fIn\fP before \fIx\fP
++ next \fIx\fP,\fIy\fP \fIx\fP through \fIy\fP
+\- previous \(aa\fIx\fP marked with \fIx\fP
++\fIn\fP \fIn\fP forward \(aa\(aa previous context
+% 1,$
+.TE
+.nc
+.h "Specifying terminal type"
+.TS
+aw(1.7i)b aw(1.5i).
+% setenv TERM \fItype\fP \fIcsh\fP and all version 6
+$ TERM=\fItype\fP; export TERM \fIsh\fP in Version 7
+See also \fItset\fR(1)
+.TE
+.h "Some terminal types"
+.TS
+lw(.4i) lw(.4i) lw(.4i) lw(.4i) lw(.4i).
+2621 43 adm31 dw1 h19
+2645 733 adm3a dw2 i100
+300s 745 c100 gt40 mime
+33 act4 dm1520 gt42 owl
+37 act5 dm2500 h1500 t1061
+4014 adm3 dm3025 h1510 vt52
+.TE
+.h "Initializing options"
+.TS
+lw(.9i)b aw(1.5i).
+EXINIT place \fBset\fP's here in environment var.
+set \fIx\fP enable option
+set no\fIx\fP disable option
+set \fIx\fP=\fIval\fP give value \fIval\fP
+set show changed options
+set all show all options
+set \fIx\fP? show value of option \fIx\fP
+.TE
+.h "Useful options"
+.TS
+lw(.9i)b lw(.3i) lw(1.0i).
+autoindent ai supply indent
+autowrite aw write before changing files
+ignorecase ic in scanning
+lisp \fB( ) { }\fP are s-exp's
+list print ^I for tab, $ at end
+magic \fB. [ *\fP special in patterns
+number nu number lines
+paragraphs para macro names which start ...
+redraw simulate smart terminal
+scroll command mode lines
+sections sect macro names ...
+shiftwidth sw for \fB< >\fP, and input \fB^D\fP
+showmatch sm to \fB)\fP and \fB}\fP as typed
+slowopen slow choke updates during insert
+window visual mode lines
+wrapscan ws around end of buffer?
+wrapmargin wm automatic line splitting
+.TE
+.LP
+.h "Scanning pattern formation"
+.TS
+aw(.9i)b aw(1.0i).
+\(ua beginning of line
+$ end of line
+\fB.\fR any character
+\e< beginning of word
+\e> end of word
+[\fIstr\fP] any char in \fIstr\fP
+[\(ua\fIstr\fP] ... not in \fIstr\fP
+[\fIx\-y\fP] ... between \fIx\fP and \fIy\fP
+* any number of preceding
+.TE
+.nc
+.LP
+.LG
+.LG
+.B
+.ce
+Vi Quick Reference
+.NL
+.R
+.LP
+.LP
+.h "Entering/leaving vi"
+.TS
+aw(1.4i)b aw(1.8i).
+% vi \fIname\fP edit \fIname\fP at top
+% vi +\fIn\fP \fIname\fP ... at line \fIn\fP
+% vi + \fIname\fP ... at end
+% vi \-r list saved files
+% vi \-r \fIname\fP recover file \fIname\fP
+% vi \fIname\fP ... edit first; rest via \fB:n\fP
+% vi \-t \fItag\fP start at \fItag\fP
+% vi +/\fIpat\fP \fIname\fP search for \fIpat\fP
+% view \fIname\fP read only mode
+ZZ exit from vi, saving changes
+^Z stop vi for later resumption
+.TE
+.h "The display"
+.TS
+lw(.75i) lw(2.2i).
+Last line T{
+Error messages, echoing input to \fB: / ?\fP and \fB!\fR,
+feedback about i/o and large changes.
+T}
+@ lines On screen only, not in file.
+~ lines Lines past end of file.
+^\fIx\fP Control characters, ^? is delete.
+tabs Expand to spaces, cursor at last.
+.TE
+.LP
+.h "Vi states"
+.TS
+lw(.75i) lw(2.2i).
+Command T{
+Normal and initial state. Others return here.
+ESC (escape) cancels partial command.
+T}
+Insert T{
+Entered by \fBa i A I o O c C s S\fP \fBR\fP.
+Arbitrary text then terminates with ESC character,
+or abnormally with interrupt.
+T}
+Last line T{
+Reading input for \fB: / ?\fP or \fB!\fP; terminate
+with ESC or CR to execute, interrupt to cancel.
+T}
+.TE
+.h "Counts before vi commands"
+.TS
+lw(1.5i) lw(1.7i)b.
+line/column number z G |
+scroll amount ^D ^U
+replicate insert a i A I
+repeat effect \fRmost rest\fP
+.TE
+.h "Simple commands"
+.TS
+lw(1.5i)b lw(1.7i).
+dw delete a word
+de ... leaving punctuation
+dd delete a line
+3dd ... 3 lines
+i\fItext\fP\fRESC\fP insert text \fIabc\fP
+cw\fInew\fP\fRESC\fP change word to \fInew\fP
+ea\fIs\fP\fRESC\fP pluralize word
+xp transpose characters
+.TE
+.nc
+.h "Interrupting, cancelling"
+.TS
+aw(0.75i)b aw(1.6i).
+ESC end insert or incomplete cmd
+^? (delete or rubout) interrupts
+^L reprint screen if \fB^?\fR scrambles it
+.TE
+.h "File manipulation"
+.TS
+aw(0.75i)b aw(1.6i).
+:w write back changes
+:wq write and quit
+:q quit
+:q! quit, discard changes
+:e \fIname\fP edit file \fIname\fP
+:e! reedit, discard changes
+:e + \fIname\fP edit, starting at end
+:e +\fIn\fR edit starting at line \fIn\fR
+:e # edit alternate file
+^\(ua synonym for \fB:e #\fP
+:w \fIname\fP write file \fIname\fP
+:w! \fIname\fP overwrite file \fIname\fP
+:sh run shell, then return
+:!\fIcmd\fP run \fIcmd\fR, then return
+:n edit next file in arglist
+:n \fIargs\fP specify new arglist
+:f show current file and line
+^G synonym for \fB:f\fP
+:ta \fItag\fP to tag file entry \fItag\fP
+^] \fB:ta\fP, following word is \fItag\fP
+.TE
+.h "Positioning within file"
+.TS
+aw(0.75i)b aw(1.6i).
+^F forward screenfull
+^B backward screenfull
+^D scroll down half screen
+^U scroll up half screen
+G goto line (end default)
+/\fIpat\fR next line matching \fIpat\fR
+?\fIpat\fR prev line matching \fIpat\fR
+n repeat last \fB/\fR or \fB?\fR
+N reverse last \fB/\fR or \fB?\fR
+/\fIpat\fP/+\fIn\fP n'th line after \fIpat\fR
+?\fIpat\fP?\-\fIn\fP n'th line before \fIpat\fR
+]] next section/function
+[[ previous section/function
+% find matching \fB( ) {\fP or \fB}\fP
+.TE
+.h "Adjusting the screen"
+.TS
+aw(0.75i)b aw(1.6i).
+^L clear and redraw
+^R retype, eliminate @ lines
+z\fRCR\fP redraw, current at window top
+z\- ... at bottom
+z\|. ... at center
+/\fIpat\fP/z\- \fIpat\fP line at bottom
+z\fIn\fP\|. use \fIn\fP line window
+^E scroll window down 1 line
+^Y scroll window up 1 line
+.TE
+.nc
+.h "Marking and returning
+.TS
+aw(0.5i)b aw(2.0i).
+\(ga\(ga previous context
+\(aa\(aa ... at first non-white in line
+m\fIx\fP mark position with letter \fIx\fP
+\(ga\fIx\fP to mark \fIx\fP
+\(aa\fIx\fP ... at first non-white in line
+.TE
+.h "Line positioning"
+.TS
+aw(0.5i)b aw(2.0i).
+H home window line
+L last window line
+M middle window line
++ next line, at first non-white
+\- previous line, at first non-white
+\fRCR\fP return, same as +
+\(da \fRor\fP j next line, same column
+\(ua \fRor\fP k previous line, same column
+.TE
+.h "Character positioning"
+.TS
+aw(0.5i)b aw(2.0i).
+\(ua first non white
+0 beginning of line
+$ end of line
+h \fRor\fP \(-> forward
+l \fRor\fP \(<- backwards
+^H same as \fB\(<-\fP
+\fRspace\fP same as \fB\(->\fP
+f\fIx\fP find \fIx\fP forward
+F\fIx\fP \fBf\fR backward
+t\fIx\fP upto \fIx\fP forward
+T\fIx\fP back upto \fIx\fP
+; repeat last \fBf F t\fP or \fBT\fP
+, inverse of \fB;\fP
+| to specified column
+% find matching \fB( { )\fP or \fB}\fR
+.TE
+.h "Words, sentences, paragraphs"
+.TS
+aw(0.5i)b aw(2.0i).
+w word forward
+b back word
+e end of word
+) to next sentence
+} to next paragraph
+( back sentence
+{ back paragraph
+W blank delimited word
+B back \fBW\fP
+E to end of \fBW\fP
+.TE
+.h "Commands for \s-2LISP\s0"
+.TS
+aw(0.5i)b aw(2.0i).
+) Forward s-expression
+} ... but don't stop at atoms
+( Back s-expression
+{ ... but don't stop at atoms
+.TE
+.nc
+.h "Corrections during insert"
+.TS
+aw(.5i)b aw(2.0i).
+^H erase last character
+^W erases last word
+\fRerase\fP your erase, same as \fB^H\fP
+\fRkill\fP your kill, erase input this line
+\e escapes \fB^H\fR, your erase and kill
+\fRESC\fP ends insertion, back to command
+^? interrupt, terminates insert
+^D backtab over \fIautoindent\fP
+\(ua^D kill \fIautoindent\fP, save for next
+0^D ... but at margin next also
+^V quote non-printing character
+.TE
+.h "Insert and replace"
+.TS
+aw(.5i)b aw(2.0i).
+a append after cursor
+i insert before
+A append at end of line
+I insert before first non-blank
+o open line below
+O open above
+r\fIx\fP replace single char with \fIx\fP
+R replace characters
+.TE
+.h "Operators (double to affect lines)"
+.TS
+aw(0.5i)b aw(2.0i).
+d delete
+c change
+< left shift
+> right shift
+! filter through command
+\&= indent for \s-2LISP\s0
+y yank lines to buffer
+.TE
+.h "Miscellaneous operations"
+.TS
+aw(0.5i)b aw(2.0i).
+C change rest of line
+D delete rest of line
+s substitute chars
+S substitute lines
+J join lines
+x delete characters
+X ... before cursor
+Y yank lines
+.TE
+.h "Yank and put"
+.TS
+aw(0.5i)b aw(2.0i).
+p put back lines
+P put before
+"\fIx\fPp put from buffer \fIx\fP
+"\fIx\fPy yank to buffer \fIx\fP
+"\fIx\fPd delete into buffer \fIx\fP
+.TE
+.h "Undo, redo, retrieve"
+.TS
+aw(0.5i)b aw(2.0i).
+u undo last change
+U restore current line
+\fB.\fP repeat last change
+"\fId\fP\|p retrieve \fId\fP'th last delete
+.TE
diff --git a/contrib/nvi/docs/changelog b/contrib/nvi/docs/changelog
new file mode 100644
index 000000000000..1f2a8c67b478
--- /dev/null
+++ b/contrib/nvi/docs/changelog
@@ -0,0 +1,1102 @@
+1.78 -> 1.79 (10/23/96)
+ + Rename delete() to del(), for C++.
+ + Add Spanish to the list of translations.
+ + Update to Perl 5.003_06, and other Perl interpreter updates.
+ + Update the set-edit-option interface for the scripting languages.
+ + Rework ex command parsing to match historic practice for backslash
+ escaped <newline> characters inside of global commands.
+ + Enhance the comment edit option to skip C++ comments.
+ + Change installation to configure the recovery shell script to match
+ the system pathnames and to install it into the vi data directory.
+ Move the recover script into the build directory, and delete the
+ recover directory.
+ + Enhance LynxOS support.
+1.76 -> 1.78 (10/01/96)
+ + Fix bugs when both the leftright scrolling and number edit options
+ were on.
+ + Fix bug where splitting in the middle of the screen could repaint
+ incorrectly.
+ + Fix first-nul in input bug, where random garbage was inserted.
+ + Correct search and mark-as-motion-command bug, it's a line mode
+ action if the search starts at or before the first non<blank>.
+ + Fix bug autoindent bug, where ^D could shift too far in the line.
+ + Fix core dump where ! command called from the .exrc file.
+ + Add the -S command-line option, which initializes vi to have the
+ secure edit option preset.
+1.75 -> 1.76 (09/15/96)
+ + Fix bug where ^V didn't keep input mapping from happening.
+ + Fix a core dump bug in the R command.
+ + Give up on licensing: no more shareware, adware, whatever.
+ + Fix cursor positioning bug for C, S and c$ in an empty file.
+1.74 -> 1.75 (08/22/96)
+ + Add French to the error message translations.
+ + Move the UNLICENSED message to the end of the message line.
+ + Fix bug where wide characters in a file name weren't calculated
+ correctly in the status message.
+ + Fix bug where cl_rename was called directly, by the ex shell code.
+ + Fix bug where splitting a screen resulting in a new screen at the
+ top of the display resulted in badly displayed status messages.
+1.73 -> 1.74 (08/18/96)
+ + Fix bug where the status line wasn't redisplayed if the user ran
+ an ex command that trashed the screen.
+ + Fix bug where the long version of the status line wasn't displayed
+ when switching screens.
+ + Rework fast-path filename completion code to sort the entries, and
+ strip out . and .. by default.
+ + Fix bug where ex went to the first line instead of the last one when
+ reading in a file.
+1.72 -> 1.73 (08/12/96)
+ + Do filename completion and some file expansion internally for speed.
+ + Fix CSCOPE_DIRS environmental variable support.
+ + Ex parser fix for global commands in script files.
+ + Add the O_PATH option, so you can specify a directory search path
+ for files.
+ + Make it possible to specify the database file to cscope, allowing
+ multiple databases in a single directory.
+ + Fix incremental search to overwrite erased characters so the user
+ can tell where they are on the colon-command line.
+ + Fix incremental search to restart the search if the user enters an
+ unescaped shell meta character.
+1.71 -> 1.72 (07/12/96)
+ + Cscope fix: test for files newer than the database was reversed.
+ + Display "files to edit" message for rewind, next and initial screen.
+ + Fix a bug in the R command where it could fail if the user extended
+ the file.
+ + Fix a bug where text abbreviations could corrupt the line.
+ + Fix a bug where the windowname edit option couldn't be set before a
+ file was loaded into the edit buffer.
+ + Fix a bug where the system .exrc values weren't being overridden by
+ the user's $HOME .exrc values.
+ + Fix a bug in the filename completion code, where garbage characters
+ could be added to the colon command line.
+ + Fix bug where multiple edit sessions on a non-existent file could
+ all write the file without warning.
+ + Fix bug where screen update was incorrect if a character triggered
+ both a wrapmargin and showmatch condition.
+ + Fix bug in leftright scrolling where <CR> during text input didn't
+ return the cursor to the left margin.
+ + Rev the Perl interpreter code, new version from Sven Verdoolaege,
+ based on Perl 5.003.01.
+ + Fix bug in tags file pattern search introduced in 1.71.
+1.70 -> 1.71 (07/01/96)
+ + Don't include <term.h> -- neither HPUX or Solaris can cope with it.
+ + Fix bug where ^M's in the original pattern were converted into new
+ lines in the file during substitution commands.
+ + Make window resize events separate from interrupts -- too many users
+ complained.
+ + Fix bug in first-character-is-null text input semantic.
+ + Rework search routines to take a length instead of a nul-terminated
+ string for a pattern. This fixes a couple of bugs in searching, but
+ probably introduces new ones.
+ + Fix prompting the user after a write filter command, the way I did
+ it in 1.70 broke the display.
+ + Don't switch to the alternate xterm screen when entering the ex
+ text input commands from vi mode.
+ + Implement the Fg command, so can foreground a background screen into
+ a split screen.
+ + Change the fg command to match screen names using the last component
+ of the filename the full filename fails.
+1.69 -> 1.70 (06/28/96)
+ + Change the ex read command to support named pipes.
+ + Copy the EXINIT/NEXINIT strings before executing their commands so
+ we don't step on the process environment.
+ + Don't do "line modification" reports for intermediate commands
+ executed from the vi colon command line, it screws up filter
+ reads, causing nvi to prompt for the user to continue.
+ + Add "smd" as an abbreviation for showmode: HP, ICL and SCO have it.
+ + Change nvi to always prompt the user after a write filter command.
+ This matches historic practice.
+ + Fix recovery information mailed to the user to reflect the program's
+ installed name.
+ + Change configuration script to not cache option information, e.g.,
+ --disable-curses.
+ + Fix a bug where the second character of the vi [[, ]] and ZZ
+ commands could start a command mapped sequence.
+ + Fix 3 write bugs: partial writes (3,$write), were clearing the
+ modified flag, full writes using line numbers (1,$write) were
+ not, and append historically never cleared the modified flag, and
+ we didn't get that right.
+ + Shorten the "more files to edit" message so it can gang on a single
+ line, lots of people have complained. Add the number of files that
+ are left to edit, it's historic practice.
+ + Fix core dump where message catalogs collided with truncating the
+ write path. Add a new write message so the string "appended" is
+ taken from a message catalog.
+ + Fix bug where an undo followed by '.' to repeat it wouldn't work
+ if no other repeatable commands had been entered.
+ + Fix core dump when resolution of input lines' autoindent characters
+ invalidated cached display information.
+ + Set the name of the X11 xterm icon/window to "xterm" when exiting,
+ if modified based on the windowname option.
+ + Include <term.h> if it exists, fixes portability problems on IRIX
+ systems.
+1.68 -> 1.69 (06/17/96)
+ + Add the windowname edit option and code to change the icon/window
+ name for xterm's.
+ + Enhance the comment edit option to skip shell comments.
+ + Add conditional prototypes to replacement C library functions.
+ + Minor enhancements/reworking to Makefile.in, other build files.
+ + Fix bug in vi text input ^D processing, could result in cursor
+ warp to the beginning of the line.
+ + Fix leftright screen bug where the screen wasn't repainted when
+ being repainted from scratch.
+ + Update the Swedish and Dutch catalogs.
+ + Truncate paths in write commands if they don't fit on one line.
+ + Fix alternate screen bug where the screen flashed and output lost
+ when switching to/from the X11 xterm alternate screen. Fix bug
+ where nvi switched into the alternate screen during filter-read
+ commands, which doesn't match historic practice.
+ + Minor relative cursor positioning change, make cursor position
+ changes from ex real and permanent.
+1.67 -> 1.68 (06/09/96)
+ + Fix core dump when tagging out of a modified file.
+1.66 -> 1.67 (06/09/96)
+ + Convert the license to adware.
+ + Leftright scrolling tweak, don't repaint the screen as often.
+ + Change so that search warning/error messages don't appear during an
+ incremental search.
+ + Cscope fix: test for files newer than the database was reversed.
+ + Don't display ex `welcome message' if in ex batch mode.
+ + Test for vsnprintf and snprintf separately, HP 10.10 has snprintf
+ but not vsnprintf.
+ + Reverse lookup order between LC_MESSAGES and LANG.
+ + Fix Tcl/Perl core dumps in common API code to get/set options.
+ + Fix R command -- it used a DB pinned page after discarding it.
+ + Minor fixes in multiple edit buffer message handling code.
+ + Fix yk command moving to shorter line core dump.
+ + Rework message handling to try and gang more messages onto a single
+ line.
+1.65 -> 1.66 (05/18/96)
+ + Convert vi man page to historic -man macro package, and install it.
+ + Fix bug were !! on an empty line with a nonexistent command left the
+ cursor on the second character, not the first.
+ + Fix bug where line redisplay was wrong when a <tab> replaced a
+ previous <tab> in the line.
+ + Fix bug where D (d$) didn't reset the relative cursor position.
+ + Fix bug where yG incorrectly reset the relative cursor position.
+ + Fix bug where the window size couldn't be grown once it was shrunk.
+ + Fix bug where the extended edit option caused tag searches to fail.
+ + If multiple lines in the tags file with the same leading tag, build
+ a tags stack like the Cscope stack. This is the obvious extension,
+ and the way that Larry McVoy's ctags program works.
+ + Send the appropriate TI/TE sequence in the curses screen whenever
+ entering ex/vi mode. This means that :shell now shows the correct
+ screen when using xterm alternate screens.
+ + Rework the options display code to get five columns in an 80 column
+ screen.
+ + Interactive Unix V3.0 port -- mostly file name shortening, other
+ minor changes. Only preliminary, more work will be necessary.
+ + Add debugging option to not read EXINIT/.exrc information.
+ + Fix bug where re_compile printed an error message to the screen
+ when the user entered [ to an incremental search.
+ + Turn off screen beeps when incremental search is failing.
+ + Fix bug where the iclower option didn't trigger an RE recompilation.
+ + Fix bug where -t into an already locked file forced the user to wait
+ as if a startup command had failed.
+ + LynxOS port -- mostly adding <sys/types.h> even though <sys/param.h>
+ was already included.
+ + Fix ex output bug, where it appeared as if an ex command was skipped
+ due to flags not being cleared in the vs_msg() routine.
+ + Fix core dump when global command tried to switch screens.
+1.64 -> 1.65 (05/13/96)
+ + Fix cscope <blank>-matching pattern to use extended RE's, and bug
+ that kept cscope from finding patterns containing <blank>s.
+ + Fix core dumps in both leftright and folded screens when tabstops
+ edit option value was large, and tab characters occurred as the last
+ character in the logical screen.
+ + Fix core dump where the second screen of a folded line wasn't
+ displayed correctly.
+ + Fix incremental search to match the current location for strings
+ starting with \< patterns.
+ + Fix bug where margins were ignored during replay of text input.
+ + Fix bug where motion components to shorter lines could lose because
+ the relative motion flags weren't ever set. This has been broken
+ forever, but the change almost certainly breaks something else -- I
+ have no idea what.
+ + Tags display: don't print the current entry separately, display
+ them all and add a trailing asterisk for the current one.
+ + Change the cscope add command to put the directory name through
+ standard file name expansion.
+ + Fix cscope use of buffers -- search commands weren't nul-terminated.
+1.63 -> 1.64 (05/08/96)
+ + Add installation target to the Makefile.
+ + Add documentation on the new tags commands to the Vi Reference
+ Manual.
+ + Make the sidescroll edit option work again.
+ + Fix bug where messages output during startup by ex could be lost.
+ + Change ex/vi commands errors into beeps, unless the verbose edit
+ option is set -- there are too many macros that are expected to
+ eventually fail. This matches historic practice.
+ + Truncate paths in initial vi screen if they won't fit on one line.
+ + Make cursor position after filter write match historic practice.
+ + Force the user to wait if there is output and the user is leaving
+ the screen for any reason -- don't permit further ex commands.
+ + Don't use a <newline> character to scroll the screen when exiting,
+ scroll in the vi screen before endwin() is called.
+ + Fix bug where the column number could be incorrect because the old
+ screen wasn't updated after a screen split.
+ + Fix ex print routine to correctly specify print flags.
+ + Make -g/-O a separate make/configuration option.
+ + Fix bug where ex/vi messages weren't being joined.
+ + Fix bug where termcap strings were free'd twice.
+ + Fix bug where TI/TE still weren't working -- I didn't put in the
+ translation strings for BSD style curses.
+ + Fix bug where I misspelled the iclower edit option as icloser.
+1.62 -> 1.63 (04/29/96)
+ + Robustness and type/lint fixes for the Tcl interface code.
+ + Fix core dump if TERM wasn't set or terminal type was unknown.
+ + Fix bug where combining ex commands that did/did not require an
+ ex screen would overwrite the command with the want-to-continue
+ messsage.
+ + Fix bug where the screen was never resolved if the user continued
+ entering ex commands using the : character, but then backspaced
+ over the prompt to quit or tried to edit their colon command-line
+ history.
+ + Fix bug where cursor wasn't placed over the ^ placeholder character
+ when quoting using the literal-next character.
+ + Fix bug where nvi under BSD style curses wasn't sending TI/TE termcap
+ strings when suspending the process.
+ + Rename mic again, to iclower.
+ + Fix bug where 'z' commands trailing / or ? commands weren't being
+ executed.
+ + Change incremental search to leave the cursor at its last position
+ when searching for something that was never found.
+ + Fix bug where search-with-confirmation from vi mode didn't position
+ the cursor correctly after displaying the confirm message.
+ + Fix bug where the "search wrapped" message was dependent on the
+ verbose edit option, which doesn't match historic practice. Change
+ search messages to be in inverse video.
+ + Fix bug where matched showmatch character wasn't being displayed
+ before the matching character was displayed.
+ + Another cursor update bug required a change to vs_paint().
+ + Fix bug were initial line offset was wrong for the first split screen
+ (symptom is very strange column numbers and blank first line).
+ + Create filename "argument" lists when creating new screens.
+ + Fix bug where globals with associated commands that included both
+ buffer execution and other commands could fail to execute the latter.
+1.61 -> 1.62 (04/22/96)
+ + Rename the "searchci" edit option to be "mic".
+ + Fix memory corruption in global commands ending in searches.
+ + Fix text resolution bug, corrected the cursor based on the
+ first line input, not the last.
+ + Rework the readonly edit option to match historic practice.
+ + Fix several minor incremental search bugs; make incremental
+ searches work in maps.
+ + Fix long-line core dump, where an incorrect screen map could be
+ used.
+1.60 -> 1.61 (04/12/96)
+ + The cursor now ends up on the FIRST character of the put text for
+ all versions of the vi put commands, regardless of the source
+ of the text. This matches System III/V behavior and POSIX 1003.2.
+ + Fixed bug where showmatch messages were getting discarded.
+ + Minor Perl integration fixes.
+ + Integrate Cscope into the tags stack code -- major change.
+ + Fixed bug where ^T would drop core if returning to a temporary file.
+ + Changed vs_ routine to display ex output to replace tab characters
+ with spaces.
+ + Fix autoindent code to not back up past beginning of line when ^T
+ inserted into the middle of a line, i.e. offset != 0.
+ + Fix "notimeout" option, was being ignored, by a coding error.
+ + Fix showmatch code to never flash on a match if keys are waiting.
+ + Change the vi 'D' command to ignore any supplied count, matching
+ historic practice.
+ + Fix viusage for D, S, C and Y (the aliased vi commands).
+ + Fix the Perl5 configuration bug in the configuration script.
+ + Make file completion commands in empty lines work.
+ + Fix where the change to let vi use the default ex command structure
+ broke the ex specification of the script or source file name.
+ + Fix to free saved RE structures when screens exit. This is a major
+ RE change, which fixed several bugs in the handling of saved/subst
+ RE's. It's likely to have added new bugs, however.
+ + Add case-independent searching (the searchci edit option).
+ + Add incremental search (the searchincr edit option).
+ + Home the cursor when executing ex commands from vi.
+1.59 -> 1.60 (03/29/96)
+ + Fix ":w >>" core dump, make that command match historic practice.
+ + Fix autoindent bug where the length of the line was incorrectly
+ calculated.
+ + Fix cursor bug where cursor could end up at the wrong place if the
+ movement keys were entered quickly enough.
+ + Change the read/write whirling indicator to appear only every 1/4
+ second, clean up the appearance.
+ + Don't change the options real values until underlying functions
+ have returned OK -- fix "set tabstop=0" core dump.
+ + Fix resizing on Sun's: use SA_INTERRUPT to interrupt read calls.
+ + Fix two forward mark command bugs: one where it wasn't setting the
+ "favorite cursor" position because of the refresh optimization,
+ and one where it didn't have VM_RCM_SET set in the command flags
+ for some reason.
+ + Fix a bug were the 's' command on top of a <tab> didn't correctly
+ copy the buffer.
+ + Make :exusage command work for commands having optional leading
+ capital letters, e.g. Next.
+ + Previous changes broke the inital-matching-prefix code in the key
+ mapping part of v_event_get -- fix it, and fix the infinite macro
+ interrupt code at the same time.
+ + Add "cedit" edit option, so colon command-line editing is optional.
+ Change filec/cedit so that you can set them to the same character,
+ and they do cedit if in column 1, and filec otherwise.
+ + Fix "source of non-existent file" core dump.
+ + Fix bug where functions keys specified in startup information were
+ never resolved/activated.
+ + Fix v_txt bug where could infinitely loop if <escape> triggered an
+ abbreviation expansion.
+ + Move version string into VERSION file, out of ex_version.c
+1.58 -> 1.59
+ + Configuration changes, several minor bug fixes, including a few
+ core dumps. No functional changes.
+1.57 -> 1.58
+ + Fix the problem where colon command-line temporary files were
+ getting left in /tmp.
+ + Fix the configuration scripts to quit immediately if the Perl
+ or Tk/Tcl libraries are specified but not found.
+ + Several screen fixes -- the changes in 1.57 weren't as safe as
+ I thought. More specifically, the refresh-only-if-waiting change
+ caused a lot of problems. In general, fixing them should provide
+ even more speedup, but I'm nervous.
+ + Lots of changes in the configuration scripts, hopefully this is
+ just a first-round ordeal.
+ + Several other minor bug fixes.
+1.56 -> 1.57
+ + Add <esc> hook to colon commands, so you can edit colon commands.
+ + Add Perl5 interpreter.
+ + Change shell expansion code to fail if it doesn't read at least
+ one non-blank character from the shell. If the shell expansion
+ process fails, or if not at least one non-blank character, it
+ now displays an error message to the user.
+ + Rework the screen display so that it matches the historic vi screen
+ refreshes.
+ + Rework options processing: print/noprint are no longer cumulative,
+ provide more information to underlying edit options modules, move
+ O_MESG information into the screen specific code.
+ + Make file completion character settable.
+ + Rework terminal restart -- you can now use ":set term" to switch
+ terminal types. This cleaned up screen resizing considerably.
+ + Character display fix, display \177 as ^?, not in hex/octal.
+ + Tag search bug fix, don't repeat search if successful.
+ + Replace sys_siglist[] use with private sigmsg() routine.
+ + Fix core dump if illegal screenId specified to Tcl routine.
+ + Add get/set mark interface to Tcl Interpreter interface.
+ + Fix core dump if file expansion code stressed (re: filec edit option)
+ + Fix bug where filter commands in empty files couldn't find line 0.
+ + Switch to GNU autoconf 2.7 for configuration, delete nvi/PORT.
+ Many random portability fixes.
+1.55 -> 1.56 (11/26/95)
+ + Bug fix release -- generally available beta release.
+1.54 -> 1.55 (11/18/95)
+ + Bug fix release.
+ + Integrate Tcl interpreter.
+1.53 -> 1.54 (11/11/95)
+ + Bug fix release. A major change in reworking the ex commands, when
+ called from the colon command line, to match historic practice, and
+ permit them to be entered repeatedly after ex has trashed the screen.
+ + Use restartable endwin() from System V curses to implement screen
+ + suspend.
+1.52 -> 1.53 (10/29/95)
+ + Switch to using vendor's curses library for all ports.
+ + Back out the event driven version, leaving screen separation.
+ + User configuration of <escape> timeout (the escapetime edit option).
+ + Add Tcl/Tk screen support.
+ + Add file name completion (the filec edit option).
+ + Disallow access to outside applications (the secure edit option).
+1.51 -> 1.52 (7/26/95)
+ + Minor cleanups, snapshotted for SMI.
+1.50 -> 1.51 (7/05/95)
+ + Lots and lots of changes for event driven model, largely in moving
+ the boundary between the screen code and the editor up and down.
+ Private release for Rob Zimmermann @ Tartan and Bill Shannon @ SMI.
+1.49 -> 1.50 Fri Jun 9 13:56:17 1995
+ + Minor bug fixes for stability.
+ + Convert to an event driven model, with the usual Nachos Supreme
+ layering that results. This is a completely new version, nothing
+ done previously matters any more.
+1.48 -> 1.49 Wed Mar 8 10:42:17 1995
+ + Changes in 1.46 broke ^A processing.
+ + Add :previous to split screen commands.
+ + Lots o' random bug fixes -- passes purify testing again.
+1.47 -> 1.48 Thu Feb 9 18:13:29 1995
+ + Random bug fixes for 1.47.
+ + Move the FREF (file structure) list out of the screen and into
+ the global area.
+ + Change semantics to :E to more closely match :e -- ":E" joins
+ the current file, so ":E /tmp" is now the command to match the
+ historic ":split".
+1.46 -> 1.47 Wed Feb 8 19:43:41 1995
+ + All ex commands (including visual and excluding global and v)
+ are now supported inside ex global commands.
+ + Rework the append/change/insert commands to match historic
+ practice for text appended to the ex command line, and inside
+ of ex global commands.
+ + Restructure to make single-line screens work.
+ + Restructure to create curses independent screen routines.
+ + Restructure to permit Edit, Next, and Tag routines to create new
+ screens on the fly.
+ + Change hexadecimal output to be \x## instead of 0x##.
+ + Change ex commands run from vi to stay in vi mode for as long as
+ possible, i.e. until ex modifies the screen outside of the editor.
+1.45 -> 1.46 Tue Jan 24 10:22:27 1995
+ + Restructure to build as a library.
+1.44 -> 1.45 Thu Jan 12 21:33:06 1995
+ + Fix relative cursor motion to handle folded lines.
+ + Recompile the search pattern if applicable edit options change.
+ + Change +/-c command ordering to match historic practice.
+ + Rework autoindent code to always resolve preceeding <blank>
+ characters when a ^T or ^D are entered.
+ + Add the print/noprint edit options, so can now specify if
+ a character is printable.
+ + Change ex to run in canonical mode.
+ + Fix ex text input to support the number edit option.
+ + Vi text input fix for the R command to correctly restore
+ characters entered and then backspaced over.
+ + Several vi increment command fixes.
+1.43 -> 1.44
+ + Bug fix, vi was printing the last line number on the status line
+ at startup. Change to execute commands at first line set, i.e.
+ "vi -t tag -c cmd" executes cmd at the tag line, not EOF.
+1.42 -> 1.43 Sat Dec 3 13:11:32 1994
+ + Marks, SunOS signed comparison fix for 1.42.
+1.41 -> 1.42 Fri Dec 2 20:08:16 1994
+ + Make autowrite require the file not be read-only.
+ + Make the ex insert command work in empty files.
+ + Tab expansion is no longer limited to values < 20 (which matches
+ historical practice).
+ + Simplify (and fix limit detection for) the # command. It's no
+ longer possible to use the # command itself to repeat or modify
+ a previous # command, '.' is the only possibility.
+ + Lots more reworking of the ex addresses, putting ? and / into
+ the ex addressing code broke the world.
+ + Make the Put, Preserve and Print commands work (don't ask).
+ + Split stdout/stderr from shell expansions; stdout is expansion
+ text, stderr is entered on the message queue.
+1.40 -> 1.41 Fri Nov 18 16:13:52 1994
+ + Addition of a port for AUX 3.1
+ + Addition of a message catalog for Russian.
+ + Make vi ? and / commands be true ex addresses (historic practice).
+ + Display the date first in vi -r recovery list.
+1.39 -> 1.40 Mon Nov 14 10:46:56 1994
+ + Two bug fixes for 1.39; -r option and v_change core dump.
+1.38 -> 1.39 Sun Nov 13 18:04:08 1994
+ + Ex substitution with confirmation now matches historic practice
+ (except that it still runs in raw mode, not cooked).
+ + Nvi now clears the screen before painting, if repainting the
+ entire screen.
+ + Fix final cursor position for put command entering text in a
+ single line.
+ + Change to break error message lines on the last <blank> in the
+ line.
+ + Always center the current line when returning to a previously
+ edited file or moving to a tag line that's not visible on the
+ screen.
+ + Change write of the current file using an explicit name or % to
+ match the semantics of :w<CR>, not :w file<CR>.
+ + Add command aliases to vi, and remap 6 historic commands to their
+ historic counterparts: D->d$, Y->y_, S->c_, C->c$, A->$a, I->^i.
+ + Match option display to historic practice; if boolean or numeric
+ options changed to default values, not displayed by default.
+ Nvi treats string options the same way, vi always displayed any
+ string option that was changed.
+ + Added lock edit option, if not set, no file locking is done.
+ + Rework ex to permit any ex command in the EXINIT variable or
+ exrc startup files. This fixes the bug were `vi +100 file'
+ painted the screen and then moved to line 100 and repainted.
+ (Yanked to SCCS ID 9.1.)
+ + Bug fix: could report file modified more recently than it was
+ written, incorrectly.
+ + Search fix: historically, motions with deltas were not corrected
+ to the previous/next line based on the starting/stopping column.
+ + Addressing fixes: make trailing non-existent addresses work, change
+ % to be text substitution, not a unique address (to follow future
+ POSIX).
+1.37 -> 1.38 Mon Oct 24 12:51:58 1994
+ + Scrolling fix; ^B can move to nonexistent lines.
+ + Fix to vi mapped commands; <escape> characters while already in
+ command mode did not historically cause the mapped characters to
+ be flushed.
+ + Add the backup edit option, automatically version edit files.
+ + Make it possible to edit files that db can't read, i.e. edit a
+ temporary file, with the correct file name.
+ + Only anchor the last line of the file to the bottom line of the
+ screen if there's half or less of a screen between the target
+ line and the end of the file.
+ + Fix wrapmargin text allocation bug.
+ + Fix ex put command to work in any empty file.
+ + Fix global command to handle move's to line 0 correctly.
+ + Regularize the yank cursor motions, several bug fixes for historic
+ practice.
+ + Fix N and n, when used as a motion command for the ! command,
+ repeat the last bang command instead of prompting for a new
+ one.
+ + Timeout maps beginning with <escape> quickly, instead of based
+ on the keytime option.
+ + Bug fix for wraplen option, wasn't triggered for input commands.
+1.36 -> 1.37 Sun Oct 9 19:02:53 1994
+ + Change PORT directories to install patches before distribution.
+ + Fix ^A to set search direction and pattern for consistency.
+ + Fold the showdirty option into the showmode option.
+ + Ex addressing fix: change search offset and line arguments (e.g.
+ the copy command) to be ex addressing offsets, matching historic
+ practice.
+ + Ex addressing fix: support ^ as an offset/flag equivalent to -.
+ + Ex addressing fix: historically, any missing address defaulted to
+ dot, e.g. "4,,," was the same as ".,.".
+ + Ex addressing fix: historically, <blank> separated numbers were
+ additive, e.g. "3 5p" displayed line 8.
+ + Ex addressing fix: make ';' as a range delimiter match historic
+ practice.
+ + Change nvi to exit immediately if stdout isn't a terminal.
+ + Change alternate file name behavior to match historic practice,
+ make the :write command set the current file name.
+ + Text input fix; input keys from a map, with an associated count,
+ weren't historically affected by the wrapmargin value.
+ + Add wraplen option, same as wrapmargin, but from the left-hand
+ column, not the right.
+ + Make ex address .<number> be equivalent to .+<number>, i.e. the
+ '+' is understood; matches historic practice, and it's widely
+ documented for ed(1).
+ + Input mode ^V^J historically mapped into a single ^J.
+ + Minor catalog changes, fixes; don't use 's' to pluralize words.
+1.35 -> 1.36 Thu Sep 8 08:40:25 1994
+ + Don't overwrite user's maps with standard (termcap) mappings.
+ + Make \ escape kill and erase characters in vi text input mode.
+ + Fix ^D autoindent bug by resolving leading <blank>s at ^D.
+ + Rework abbreviation tests (again!) to match historic practice.
+ + Change ^D/^U default scrolling value to be based on window option
+ value, not screen lines, correct scrolling option value, both to
+ match historic practice. NOTE: System V does this differently!
+1.34 -> 1.35 Wed Aug 31 19:20:15 1994
+ + Add the historic -l option.
+ + Message catalogs.
+ + Display global messages at each flush, just in case some are there.
+ + Fix global substitute code, `\\' wasn't handled correctly.
+ + Fix abbreviation code to use <blank>s as the preceding character.
+ + Fix ruler to display logical column, not physical column.
+ + Block signals when user issues :preserve command, so no race caused
+ by SIGHUP/SIGTERM.
+1.33 -> 1.34 Wed Aug 17 14:37:32 1994 (PUBLICLY AVAILABLE VERSION)
+ + Back out sccsid string fix, it won't work on SunOS 4.1.
+1.32 -> 1.33 Wed Aug 17 09:31:41 1994 (PUBLICLY AVAILABLE VERSION)
+ + Get back 5K of data space for the sccsid strings.
+ + Fix bug where cG fix in version 1.31 broke cw cursor positioning
+ when the change command extended the line.
+ + Fix core dump in map/seq code if character larger than 7 bits.
+ + Block signals when manipulating the SCR chains.
+ + Fix memory allocation for machines with multiple pointer sizes.
+1.31 -> 1.32 Mon Aug 15 14:27:49 1994
+ + Turn off recno mmap call for Solaris 2.4/SunOS 5.4.
+1.30 -> 1.31 Sun Aug 14 13:13:35 1994
+ + Fix bug were cG on the last line of a file wasn't done in line mode,
+ and where the cursor wasn't positioned correctly after exiting text
+ insert mode.
+ + Add termcap workaround to make function keys greater than 9 work
+ correctly (or fail if old-style termcap support).
+ + Change ex/vi to not flush mapped keys on error -- this is historic
+ practice, and people depended on it.
+ + Rework vi parser so that no command including a mapped key ever
+ becomes the '.' command, matching historic practice.
+ + Make <escape> cancellation in the vi parser match POSIX 1003.2.
+ + Fix curses bug where standout string was written for each standout
+ character, and where standout mode was never exited explicitly.
+ Fix bugs in curses SF/sf and SR/sr scrolling, as seen on Sun and
+ x86 consoles.
+ + The v/global commands execute the print command by default.
+ + The number option historically applies to ex as well as vi.
+1.29 -> 1.30 Mon Aug 8 10:30:42 1994
+ + Make first read into a temporary set the file's name.
+ + Permit any key to continue scrolling or ex commands -- this
+ allows stacked colon commands, and matches historic practice.
+ + Don't output normal ! command commentary in ex silent mode.
+ + Allow +/- flags after substitute commands, make line (flag)
+ offsets from vi mode match historic practice.
+ + Return <eof> to ex immediately, even if preceded by spaces. Rework
+ ex parser to do erase the prompt instead of depending on the print
+ routines to do it. Minor fixes to the ex parser for display of
+ default and scrolling commands. MORE EX PARSER CHANGES.
+1.28 -> 1.29 Fri Aug 5 10:18:07 1994
+ + Make the abbreviated ex delete command work (:dele---###lll for
+ example, is historically legal.
+ + When autoprint fires, multiple flags may be set, use ex_print
+ directly instead of the stub routines.
+ + Change v/global commands to turn off autoprint while running.
+ + Minor changes to make the ! command display match historic output.
+ + Rework the ex parser to permit multiple command separators without
+ commands -- MAJOR CHANGE, likely to introduce all sorts of new bugs.
+ + Fix cd command to expand argument in the context of each element
+ of the cdpath option, make relative paths always relative to the
+ current directory.
+ + Rework write/quit cases for temporary files, so that user's don't
+ discard them accidentally.
+ + Check for window size changes when continuing after a suspend.
+ + Fix memory problem in svi_screen, used free'd memory.
+ + Change the ex change, insert, append commands to match historic
+ cursor positions if no data entered by the user.
+ + Change ex format flags (#, l, p) to affect future commands, not
+ just the current one, to match historic practice.
+ + Make the user's EOF character an additional scroll character in ex.
+ + Fix ex ^D scrolling to be the value of the scroll option, not half
+ the screen.
+ + Fix buffer execution to match historic practice -- bugs where the
+ '*' command didn't work, and @<carriage-return> didn't work.
+ + Fix doubled reporting of deleted lines in filters.
+ + Rework the % ` / ? ( ) N n { and ^A commands to always cut into
+ numeric buffers regardless of the location or length of the cut.
+ This matches historic practice.
+ + Fix the { command to check the current line if the cursor doesn't
+ start on the first character of the line.
+ + Do '!' expansion in the ex read command arguments, it's historic
+ practice. In addition, it sets the last '!' command.
+1.27 -> 1.28 Wed Jul 27 21:29:18 1994
+ + Add support for scrolling using the CS and SF/sf/SR/sr termcap
+ strings to the 4BSD curses.
+ + Rework of getkey() introduced a bug where command interrupt put
+ nvi into an infinite loop.
+ + Piping through a filter historically cut the replaced lines into
+ the default buffer, although not the numeric ones.
+ + Read of a filter and !! historically moved to the first nonblank
+ of the resulting cursor line (most of the time).
+ + Rework cursor motion flags, to support '!' as a motion command.
+1.26 -> 1.27 Tue Jul 26 10:27:58 1994
+ + Add the meta option, to specify characters the shell will expand.
+ + Fix the read command to match historic practice, the white space
+ and bang characters weren't getting parsed correctly.
+ + Change SIGALRM handler to save and restore errno.
+ + Change SunOS include/compat.h to include <vfork.h> so that the
+ ex/filter.c code works again.
+ + Don't put lines deleted by the ex delete command into the numeric
+ buffers, matching historic practice.
+ + Fix; if appending to a buffer, default buffer historically only
+ references the appended text, not the resulting text.
+ + Support multiple, semi-colon separated search strings, and 'z'
+ commands after search strings.
+ + Make previous context mark setting match historic practice (see
+ docs/internals/context).
+ + Fix the set command to permit whitespace between the option and
+ the question mark, fix question marks in general.
+ + Fix bug where ex error messages could be accidentally preceded
+ by a single space.
+ + Fix bug where curses reorganization could lose screen specific
+ mappings as soon as any screen exited.
+ + Fix bug in paragraph code where invalid macros could be matched.
+ Make paragraph motions stop at formfeed (^L) characters.
+ + Change 'c' to match historic practice, it cut text into numeric
+ buffers.
+1.25 -> 1.26 Tue Jul 19 17:46:24 1994
+ + Ignore SIGWINCH if the screen size is unchanged; SunOS systems
+ deliver one when a screen is uncovered.
+ + Fix: don't permit a command with a motion component to wrap due
+ to wrapscan and return to the original cursor position.
+ + Fix: ^E wasn't beeping when reaching the bottom of the file.
+ + Fix bg/fg bug where tmp file exiting caused a NULL dereference.
+ + Rework file locking code to use fcntl(2) explicitly.
+ + Fix bug in section code where invalid macros could be matched.
+ + Fix bug where line number reset by vi's Q command.
+ + Add explicit character mode designation to character mode buffers.
+ + Add <sys/ioctl.h> include to sex/sex_window.c, needed by NET/2
+ vintage systems.
+ + Change to always flush a character during suspend, 4BSD curses
+ has the optimization where it doesn't flush after a standend().
+ + Fix bug on OSF1 where <curses.h> changes the values of VERASE,
+ VKILL and VWERASE to incorrect ones.
+ + Fix bug where optarg used incorrectly in main.c.
+ + Block all signals when acting on a signal delivery.
+ + Fix recovery bug where RCV_EMAIL could fire even if there wasn't
+ a backing file; format recovery message.
+1.24 -> 1.25 Sun Jul 17 14:33:38 1994
+ + Stop allowing keyboard suspends (^Z) in insert mode, it's hard
+ to get autowrite correct, and it's not historic practice.
+ + Fix z^, z+ to match historic practice.
+ + Bug in message handling, "vi +35 non-existent_file" lost the
+ status message because the "+35" pushed onto the stack erased
+ it. For now, change so that messages aren't displayed if there
+ are keys waiting -- may need to add a "don't-erase" bit to the
+ character in the stack instead.
+ + Bug in svi_msgflush(), where error messages could come out in
+ normal video.
+1.23 -> 1.24 Sat Jul 16 18:30:18 1994
+ + Fix core dump in exf.c, where editing a non-existent file and
+ exiting could cause already free'd memory to be free'd.
+ + Clean up numerous memory errors, courtesy of Purify.
+ + Change process wait code to fail if wait fails, and not attempt
+ to interpret the wait return information.
+ + Open recovery and DB files for writing as well as reading, System
+ V (fcntl) won't let you acquire LOCK_EX locks otherwise.
+ + Fix substitute bug where could malloc 0 bytes (AIX breaks).
+ + Permit the mapping of <carriage-return>, it's historic practice.
+ + Historic vi didn't eat <blank> characters before the force
+ flag, match historic practice.
+ + Bug in ex argument parsing, corrected for literal characters
+ twice.
+ + Delete screen specific maps when the screen closes.
+ + Move to the first non-<blank> in the line on startup; historic
+ practice.
+ + Change the ex visual command to move directly to a line if no
+ trailing 'z' command.
+ + Fix "[[" and "]]" to match historic practice (yet again...).
+ + Fix "yb" and "y{" commands to update the cursor correctly.
+ + Change "~<motion>" to match the yank cursor movement semantics
+ exactly.
+ + Move all of the curses related code into sex/svi -- major rework,
+ but should help in future ports.
+ + Fix bug in split code caused by new file naming code, where would
+ drop core when a split screen exited.
+ + Change svi_ex_write to do character display translation, so that
+ messages with file names in them are displayed correctly.
+ + Display the file name on split screens instead of a divider line.
+ + Fix move bug, wasn't copying lines before putting them.
+ + Fix bug were :n dropped core if no arguments supplied.
+ + Don't quote characters in executed buffer: "ifoo<esc>" should leave
+ insert mode after the buffer is executed.
+ + Tagpop and tagpush should set the absolute mark in case only moving
+ within a file.
+ + Skip leading whitespace characters before tags and cursor word
+ searches.
+ + Fix bug in ex_global where re_conv() was allocating the temporary
+ buffer and not freeing it.
+1.22 -> 1.23: Wed Jun 29 19:22:33 1994
+ + New <sys/cdefs.h> required "inline" to change to "__inline"
+ + Fix System V curses code for new ^Z support.
+ + Fix off-by-one in the move code, avoid ":1,$mo$" with only one
+ line in the buffer.
+ + Line orientation of motion commands was remembered too long,
+ i.e. '.' command could be incorrectly marked as line oriented.
+ + Move file modification time into EXF, so it's shared across
+ split screens.
+ + Put the prev[ious] command back in, people complained.
+ + Random fixes to next/prev semantics changed in 1.22.
+ + Historically vi doesn't only move to the last address if there's
+ ANYTHING after the addresses, e.g. ":3" moves to line 3, ":3|"
+ prints line 3.
+1.21 -> 1.22: Mon Jun 27 11:01:41 1994
+ + Make the line between split screens inverse video again.
+ + Delete the prev[ious] command, it's not useful enough to keep.
+ + Rework :args/file name handling from scratch -- MAJOR CHANGE,
+ likely to introduce all sorts of new bugs.
+ + Fix RE bug where no subexpressions in the pattern but there were
+ subexpressions referenced in the replacement, e.g. "s/XXX/\1/g".
+ + Change recovery to not leave unmodified files around after a
+ crash, by using the owner 'x' bit on unmodified backup files.
+ MAJOR CHANGE, the system recovery script has to change!
+ + Change -r option to delete recovery.* files that reference non-
+ existent vi.* files.
+ + Rework recovery locking so that fcntl(2) locking will work.
+ + Fix append (upper-case) buffers, broken by cut fixes.
+ + Fix | to not set the absolute motion mark.
+ + Read $HOME/.exrc file on startup if the effective user ID is
+ root. This makes running vi while su(1)'d work correctly.
+ + Use the full pathname of the file as the recovery name, not
+ just the last component. Matches historic practice.
+ + Keep marks in empty files from being destroyed.
+ + Block all caught signals before calling the DB routines.
+ + Make the line change report match historic practice (yanked
+ lines were different than everything else).
+ + Add section on multiple screens to the reference manual.
+ + Display all messages at once, combine onto a single line if
+ possible. Delete the trailing period from all messages.
+1.20 -> 1.21: Thu May 19 12:21:58 1994
+ + Delete the -l flag from the recover mail.
+ + Send the user email if ex command :preserve executed, this matches
+ historic practice. Lots of changes to the preserve and recovery
+ code, change preserve to snapshot files (again, historic practice).
+ + Make buffers match historic practice: "add logically stores text
+ into buffer a, buffer 1, and the unnamed buffer.
+ + Print <tab> characters as ^I on the colon command line if the
+ list option set.
+ + Adjust ^F and ^B scroll values in the presence of split screens
+ and small windows.
+ + Break msg* routines out from util.c into msg.c, start thinking
+ about message catalogs.
+ + Add tildeop set option, based on stevie's option of the same name.
+ Changes the ~ command into "[count] ~ motion", i.e. ~ takes a
+ trailing motion.
+ + Chose NOT to match historic practice on cursor positioning after
+ consecutive undo commands on a single line; see vi/v_undo.c for
+ the comment.
+ + Add a one line cache so that multiple changes to the same line
+ are only counted once (e.g. "dl35p" changes one line, not 35).
+ + Rework signals some more. Block file sync signals in vi routines
+ that interface to DB, so can sync the files at interrupt time.
+ Write up all of the signal handling arguments, see signal.c.
+1.19 -> 1.20: Thu May 5 19:24:57 1994
+ + Return ^Z to synchronous handling. See the dicussion in signal.c
+ and svi_screen.c:svi_curses_init().
+ + Fix bug where line change report was wrong in util.c:msg_rpt().
+1.18 -> 1.19: Thu May 5 12:59:51 1994
+ + Block DSUSP so that ^Y isn't delivered at SIGTSTP.
+ + Fix bug -- put into an empty file leaves the cursor at 1,0,
+ not the first nonblank.
+ + Fix bug were number of lines reported for the 'P' command was
+ off-by-one.
+ + Fix bug were 0^D wasn't being handled correctly.
+ + Delete remnants of ^Z as a raw character.
+ + Fix bug where if a map was an entire colon command, it may never
+ have been displayed.
+ + Final cursor position fixes for the vi T and t commands.
+ + The ex :next command took an optional ex command as it's first
+ argument similar to the :edit commands. Match historic practice.
+1.17 -> 1.18: Wed May 4 13:57:10 1994
+ + Rework curses information in the PORT/Makefile's.
+ + Minor fixes to ^Z asynchronous code.
+1.16 -> 1.17: Wed May 4 11:15:56 1994
+ + Make ex comment handling match historic practice.
+ + Make ^Z work asynchronously, we can no longer use the SIGTSTP
+ handler in the curses library.
+1.15 -> 1.16: Mon May 2 19:42:07 1994
+ + Make the 'p' and 'P' commands support counts, i.e. "Y10p" works.
+ + Make characters that map to themselves as the first part of the
+ mapping work, it's historic practice.
+ + Fix bug where "s/./\& /" discarded the space in the replacement
+ string.
+ + Add support for up/down cursor arrows in text input mode, rework
+ left/right support to match industry practice.
+ + Fix bug were enough character remapping could corrupt memory.
+ + Delete O_REMAPMAX in favor of setting interrupts after N mapped
+ characters without a read, delete the map counter per character.
+ MAJOR CHANGE. All of the interrupt signal handling has been
+ reworked so that interrupts are always turned on instead of
+ being turned on periodically, when an interruptible operation is
+ pending.
+ + Fix bug where vi wait() was interrupted by the recovery alarm.
+ + Make +cmd's and initial commands execute with the current line
+ set to the last line of the file. This is historic practice.
+ + Change "lock failed" error message to a file status message.
+ It always fails over NFS, and making all NFS files readonly
+ isn't going to fly.
+ + Use the historic line number format, but check for overflow.
+ + Fix bug where vi command parser ignored buffers specified as
+ part of the motion command.
+ + Make [@*]buffer commands on character mode buffers match historic
+ practice.
+ + Fix bug where the cmap/chf entries of the tty structure weren't
+ being cleared when new characters were read.
+ + Fix bug where the default command motion flags were being set
+ when the command was a motion component.
+ + Fix wrapmargin bug; if appending characters, and wrapmargin breaks
+ the line, an additional space is eaten.
+1.14 -> 1.15: Fri Apr 29 07:44:57 1994
+ + Make the ex delete command work in any empty file.
+ + Fix bug where 't' command placed the cursor on the character
+ instead of to its left.
+ + ^D and ^U didn't set the scroll option value historically.
+ Note, this change means that any user set value (e.g. 15^D)
+ will be lost when splitting the screen, since the split code
+ now resets the scroll value regardless.
+ + Fix the ( command to set the absolute movement mark.
+ + Only use TIOCGWINSZ for window information if SIGWINCH signal
+ caught.
+ + Delete the -l flag, and make -r work for multiple arguments.
+ Add the ex "recover[!] file" command.
+ + Switch into ex terminal mode and use the sex routines when
+ append/change/insert called from vi mode.
+ + Make ^F and ^B match historic practice. This required a fairly
+ extensive rework of the svi scrolling code.
+ + Cursor positioning in H, M, L, G (first non-blank for 1G) wasn't
+ being done correctly. Delete the SETLFNB flag. H, M, and L stay
+ logical movements (SETNNB) and G always moves to the first nonblank.
+ + System V uses "lines" and "cols", not "li" and "co", change as
+ necessary. Check termcap function returns for errors.
+ + Fix `<character> command to do start/end of line correction,
+ and to set line mode if starting and stopping at column 0.
+ + Fix bug in delete code where dropped core if deleted in character
+ mode to an empty line. (Rework the delete code for efficiency.)
+ + Give up on SunOS 4.1.X, and use "cc" instead of /usr/5bin/cc.
+ + Protect ex_getline routine from interrupted system calls (if
+ possible, set SA_RESTART on SIGALRM, too).
+ + Fix leftright scrolling bug, when moving to a shorter line.
+ + Do validity checking on the copy, move, t command target line
+ numbers.
+ + Change for System V % pattern broke trailing flags for empty
+ replacement strings.
+ + Fix bug when RCM flags retained in the saved dot structure.
+ + Make the ex '=' command work for empty files.
+ + Fix bug where special_key array was being free'd (it's no longer
+ allocated).
+ + Matches cut in line mode only if the starting cursor is at or
+ before the first nonblank in its line, and the ending cursor is
+ at or after the last nonblank in its line.
+ + Add the :wn command, so you can write a file and switch to a new
+ file in one command.
+ + Allow only a single key as an argument to :viusage.
+ + New movement code broke filter/paragraph operations in empty
+ files ("!}date" in an empty file was dropping core).
+1.12 -> 1.14: Mon Apr 18 11:05:10 1994 (PUBLICLY AVAILABLE VERSION, 4.4BSD)
+ + Fix FILE structure leakage in the ex filter code.
+ + Rework suspend code for System V curses. Nvi has to do the
+ the work, there's no way to get curses to do it right.
+ + Revert SunOS 4.1.X ports to the distributed curses. There's
+ a bug in Sun's implementation that we can't live with.
+ + Quit immediately if row/column values are unreasonable.
+ + Fix the function keys to match vi historic behavior.
+ + Replace the echo/awk magic in the Makefile's with awk scripts.
+1.11 -> 1.12: Thu Apr 14 11:10:19 1994
+ + Fix bug where only the first vi key was checked for validity.
+ + Make 'R' continue to overwrite after a <carriage-return>.
+ + Only display the "no recovery" message once.
+ + Rework line backup code to restore the line to its previous
+ condition.
+ + Don't permit :q in a .exrc file or EXINIT variable.
+ + Fix wrapscan option bug where forward searches become backward
+ searches and do cursor correction accordingly.
+ + Change "dd" to move the cursor to the first non-blank on the line.
+ + Delete cursor attraction to the first non-blank, change non-blank
+ motions to set the most attractive cursor position instead.
+ + Fix 'r' substitute option to set the RE to the last RE, not the
+ last substitute RE.
+ + Fix 'c' and 'g' substitute options to always toggle, and fix
+ edcompatible option to not reset them.
+ + Display ex error messages in inverse video.
+ + Fix errorbells option to match historic practice.
+ + Delete fixed character display table in favor of table built based
+ on the current locale.
+ + Add ":set octal" option, that displays unknown characters as octal
+ values instead of the default hexadecimal.
+ + Make all command and text input modes interruptible.
+ + Fix ex input mode to display error messages immediately, instead
+ of waiting for the lines to be resolved.
+ + Fix bug where vi calling append could overwrite the command.
+ + Fix off-by-one in the ex print routine tab code.
+ + Fix incorrect ^D test in vi text input routines.
+ + Add autoindent support for ex text insert routines.
+ + Add System V substitute command replacement pattern semantics,
+ where '%' means the last replacement pattern.
+ + Fix bug that \ didn't escape newlines in ex commands.
+ + Regularize the names of special characters to CH_*.
+ + Change hex insert character from ^Vx<hex_char> to ^X<hex_char>
+ + Integrate System V style curses, so SunOS and Solaris ports can
+ use the native curses implementation.
+1.10 -> 1.11: Thu Mar 24 16:07:45 EST 1994 (PUBLICLY AVAILABLE VERSION)
+ + Change H, M, and L to set the absolute mark, historical practice.
+ + Fix bug in stepping through multiple tags files.
+ + Add "remapmax" option that turns off map counts so you can remap
+ infinitely. If it's off, term_key() can be interrupted from the
+ keyboard, which will cause the buffers to flush. I also dropped
+ the default max number of remaps to 50. (Only Dave Hitz's TM
+ macros and maze appear to go over that limit.)
+ + Change :mkexrc to not dump w{300,1200,9600}, lisp options.
+ + Fix backward search within a line bug.
+ + Change all the includes of "pathnames.h" to use <>'s so that the
+ PORT versions can use -I. to replace it with their own versions.
+ + Make reads and writes interruptible. Rework code that enters and
+ leaves ex for '!' and filter commands, rework all interrupt and
+ timer code.
+ + Fix core dump when user displayed option in .exrc file.
+ + Fix bug where writing empty files didn't update the saved
+ modification time.
+ + Fix bug where /pattern/ addressing was always a backward search.
+ + Fix bug triggered by autoindent of more than 32 characters, where
+ nvi wasn't checking the right TEXT length.
+ + Fix bug where joining only empty lines caused a core dump.
+1.09 -> 1.10: Sat Mar 19 15:40:29 EST 1994
+ + Fix "set all" core dump.
+1.08 -> 1.09: Sat Mar 19 10:11:14 EST 1994
+ + If the tag's file path is relative, and it doesn't exist, check
+ relative to the tag file location.
+ + Fix ~ command to free temporary buffer on error return.
+ + Create vi.ref, a first cut at a reference document for vi.
+ The manual page and the reference document only document the
+ set options, so far.
+ + Fix 1G bug not always going to the first non-blank.
+ + Upgrade PORT/regex to release alpha3.4, from Henry Spencer.
+ + Add MKS vi's "cdpath" option, supporting a cd search path.
+ + Handle if search as a motion was discarded, i.e. "d/<erase>".
+ + Change nvi to not create multiple recovery files if modifying
+ a recovered file.
+ + Decide to ignore that the cursor is before the '$' when inserting
+ in list mode. It's too hard to fix.
+1.07 -> 1.08: Wed Mar 16 07:37:36 EST 1994
+ + Leftright and big line scrolling fixes. This meant more changes
+ to the screen display code, so there may be new problems.
+ + Don't permit search-style addresses until a file has been read.
+ + "c[Ww]" command incorrectly handled the "in whitespace" case.
+ + Fix key space allocation bug triggered by cut/paste under SunOS.
+ + Ex move command got the final cursor position wrong.
+ + Delete "optimize option not implemented" message.
+ + Make the literal-next character turn off mapping for the next
+ character in text input mode.
+1.06 -> 1.07: Mon Mar 14 11:10:33 EST 1994
+ + The "wire down" change in 1.05 broke ex command parsing, there
+ wasn't a corresponding change to handle multiple K_VLNEXT chars.
+ + Fix final position for vi's 't' command.
+1.05 -> 1.06: Sun Mar 13 16:12:52 EST 1994
+ + Wire down ^D, ^H, ^W, and ^V, regardless of the user's termios
+ values.
+ + Add ^D as the ex scroll command.
+ + Support ^Q as a literal-next character.
+ + Rework abbreviations to be delimited by any !inword() character.
+ + Add options description to the manual page.
+ + Minor screen cache fix for svi_get.c.
+ + Rework beautify option support to match historical practice.
+ + Exit immediately if not reading from a tty and a command fails.
+ + Default the SunOS 4.* ports to the distributed curses, not SMI's.
+1.04 -> 1.05: Thu Mar 24 16:07:45 EST 1994
+ + Make cursor keys work in input mode.
+ + Rework screen column code in vi curses screen. MAJOR CHANGE --
+ after this, we'll be debugging curses screen presentation from
+ scratch.
+ + Explode include files in vi.h into the source files.
+1.03 -> 1.04: Sun Mar 6 14:14:16 EST 1994
+ + Make the ex move command keep the marks on the moved lines.
+ + Change resize semantics so you can set the screen size to a
+ specific value. A couple of screen fixes for the resize code.
+ + Fixes for foreground/background due to SIGWINCH.
+ + Complete rework of all of vi's cursor movements. The underlying
+ assumption in the old code was that the starting cursor position
+ was part of the range of lines cut or deleted. The command
+ "d[[" is an example where this isn't true. Change it so that all
+ motion component commands set the final cursor position separately
+ from the range, as it can't be done correctly later. This is a
+ MAJOR CHANGE -- after this change, we'll be debugging the cursor
+ positioning from scratch.
+ + Rewrite the B, b, E, e commands to use vi's getc() interface
+ instead of rolling their own.
+ + Add a second MARK structure, LMARK, which is the larger mark
+ needed by the logging and mark queue code. Everything else uses
+ the reworked MARK structure, which is simply a line/column pair.
+ + Rework cut/delete to not expect 1-past-the-end in the range, but
+ to act on text to the end of the range, inclusive.
+ + Sync on write's, to force NFS to flush.
+1.01 -> 1.03: Sun Jan 23 17:50:35 EST 1994 (PUBLICLY AVAILABLE VERSION)
+ + Tag stack fixes, was returning to the tag, not the position from
+ which the user tagged.
+ + Only use from the cursor to the end of the word in cursor word
+ searches and tags. (Matches historical vi behavior.)
+ + Fix delete-last-line bug when line number option set.
+ + Fix usage line for :split command.
+ + If O_NUMBER set, long input lines would eventually fail, the column
+ count for the second screen of long lines wasn't set correctly.
+ + Fix for [[ reaching SOF with a column longer than the first line.
+ + Fix for multiple error messages if no screen displayed.
+ + Fix :read to set alternate file name as in historical practice.
+ + Fix cut to rotate the numeric buffers if line mode flag set.
+1.00 -> 1.01: Wed Jan 12 13:37:18 EST 1994
+ + Don't put cut items into numeric buffers if cutting less than
+ parts of two lines.
+0.94 -> 1.00: Mon Jan 10 02:27:27 EST 1994
+ + Read-ahead not there; BSD tty driver problem, SunOS curses
+ problem.
+ + Global command could error if it deleted the last line of
+ the file.
+ + Change '.' to only apply to the 'u' if entered immediately
+ after the 'u' command. "1pu.u.u. is still broken, but I
+ expect that it's going to be sacrificed for multiple undo.
+ + If backward motion on a command, now move to the point; get
+ yank cursor positioning correct.
+ + Rework cut buffers to match historic practice -- yank/delete
+ numeric buffers redone sensibly, ignoring historic practice.
+0.92 -> 0.93: Mon Dec 20 19:52:14 EST 1993
+ + Christos Zoulas reimplemented the script windows using pty's,
+ which means that they now work reasonably. The down side of
+ this is that almost all ports other than 4.4BSD need to include
+ two new files, login_tty.c and pty.c from the PORT/clib directory.
+ I've added them to the Makefiles.
+ + All calloc/malloc/realloc functions now cast their pointers, for
+ SunOS -- there should be far fewer warning messages, during the
+ build. The remaining messages are where CHAR_T's meet char *'s,
+ i.e. where 8-bit clean meets strcmp.
+ + The user's argument list handling has been reworked so that there
+ is always a single consistent position for use by :next, :prev and
+ :rewind.
+ + All of the historical options are now at least accepted, although
+ not all of them are implemented. (Edcompatible, hardtabs, lisp,
+ optimize, redraw, and slowopen aren't implemented.)
+ + The RE's have been reworked so that matches of length 0 are handled
+ in the same way as vi used to handle them.
+ + Several more mapping fixes and ex parser addressing fixes.
diff --git a/contrib/nvi/docs/ev b/contrib/nvi/docs/ev
new file mode 100644
index 000000000000..144295a319f2
--- /dev/null
+++ b/contrib/nvi/docs/ev
@@ -0,0 +1,55 @@
+# @(#)ev 8.4 (Berkeley) 4/29/94
+
+Ev: Vi: Result:
+<CK> <CK> (Cursor keys). Move around the file.
+
+Meta key commands:
+^A<#> <#>G Goto line #.
+^A$ G Goto the end of the file.
+^A/ / Prompt and execute a forward search.
+^A: : Prompt and execute an ex command.
+^A? ? Prompt and execute a backward search.
+^Ac y'<c> Copy to mark in line mode (or copy the current line).
+^AC y`<c> Copy to mark in character mode.
+^Ad d'<c> Delete to mark in line mode (or delete the current line).
+^AD d`<c> Delete to mark in character mode.
+^Aj J Join lines.
+^Am m<c> Mark the current cursor position.
+^AN N Repeat search in the reverse direction.
+^An ^A Search for the word under the cursor.
+^Ar u Redo a command.
+^Au u Undo a command.
+
+Single key commands:
+^B ^B Page up a screen.
+^C ^C Interrupt long-running commands.
+^D ^D Page down a half-screen.
+^E $ End of line.
+^F ^F Page down a screen.
+^G ^G File status/information.
+^H X Delete the character to the left of the cursor.
+^I (TAB)
+^J j Cursor down one line.
+^K k Cursor up one line.
+^L ^L Redraw the screen.
+^M (CR) ^M In insert mode, split the line at the current cursor,
+ creating a new line.
+ In overwrite mode, cursor down one line.
+^N n Repeat previous search, in previous direction.
+^O (UNUSED)
+^P p Paste the cut text at the cursor position.
+^Q (XON/XOFF)
+^R (UNUSED)
+^S (XON/XOFF)
+^T D Truncate the line at the cursor position.
+^U ^U Page up a half-screen.
+^V<c> ^V<c> Insert/overwrite with a literal next character.
+^W w Move forward one whitespace separated word.
+^X x Delete the current character.
+^Y (UNUSED)
+^Z ^Z Suspend.
+
+New ex mode commands:
+
+^A:set ov[erwrite] Toggle "insert" mode, so that input keys overwrite
+ the existing characters.
diff --git a/contrib/nvi/docs/features b/contrib/nvi/docs/features
new file mode 100644
index 000000000000..51650f949efc
--- /dev/null
+++ b/contrib/nvi/docs/features
@@ -0,0 +1,83 @@
+List of things that should be added:
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
++ X11 (Tk, Motif, Xaw) interface.
++ Interpreted language (Perl, Scheme, Tcl/Rush, Python)
++ Additional ports: Windows, Windows NT, MSDOS
++ Forms editing package; use RE's to verify field contents.
++ Internationalization, including wide character and multibyte support.
++ Support for single line window editing, including full editing
+ capability on the vi colon command line.
++ Rob Pike's sam style RE's.
++ Right-to-left and bottom to top text support.
++ Quitall command, to leave all windows. A ! will force the quit.
+
+List of suggested features:
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
++ It would be nice to have the completion mechanism found in tcsh versions
+ >= 6.03. For instance, the completion for the `:cd' command will be
+ directories only. The completion for the `:set' command will be all
+ options not set at that moment, and for `:set un' will be all options
+ that are set at that moment. The completion for `:< count' will be the
+ flags.
+
++ Add an command-line option to initially split the screen based on the
+ number of file arguments, e.g., "nvi -a file1 file2" would initialize
+ a two edit-buffer display.
+
++ Add a "push" command that would push a file on the tags stack.
+ (Essentially make tags a special case of the stack, and make
+ the stack more general purpose.)
+
++ Make :script just run a command and edit the output, and :interactive,
+ which allows interactive shell session, instead of just the current
+ :script.
+
++ Add tagging information to the man page so that users can display
+ the part of the man page that discusses the command in which they're
+ interested.
+
++ Add a zone option so that you can declare that top/bottom few lines
+ of the screen aren't filled except by accident, so that the text
+ you ask for is always concentrated in the center of the screen.
+
++ Change
+ :di[splay] tags -> :tags
+ :di[splay] screens -> :screens
+ :di[splay] buffers -> :buffers
+
++ A macro record function. Add the ability to record a sequence
+ of keystrokes into a named buffer for later use. Handy when
+ you're trying to build a semi-complex macro.
+
++ The semantics of :split, :bg, and :fg aren't right. Someone needs to
+ rethink how they should interact. The main problem arises when users
+ want to get a window into a new file. Currently, the necessary sequence
+ is ":split newfile|^W|:bg". It would be nice if you could simply
+ background the current screen and edit a new one.
+
++ An option to turn on a ``quarter plane'' model so that you can
+ go as far to the right or down as you wish. The File or the
+ current line is only extended if you actually put down a char at
+ the new location. Very handy for ascii graphics and tables.
+
++ Some way of replacing the command bindings. For this to work
+ cleanly the notion of a command must be separate from that of a
+ key. (Simulate the Rand editor?)
+
++ Vertical splitting, so you can see files side by side.
+
++ Tracking. Two or more files are associated so that when one file
+ is scrolled up/down/left/right other files track by the same amount.
+ Tracking may be constrained such that two files only track vertically
+ or horizontally. This is relatively easy to implement.
+
++ A status file so that the next time invocation of the editor returns
+ to the same place, with the same number of windows etc. In case of
+ change of the screen size, reasonable defaults are used. For each
+ window size and location of the window, name of the file and position
+ in it, any tab settings, any other settings for the window (such as
+ insert/overwrite mode, auto indent etc). Last search RE and maybe
+ direction. If a file does not exist the next time you invoke the
+ editor, its window is left in the same place but with some default
+ message.
diff --git a/contrib/nvi/docs/help b/contrib/nvi/docs/help
new file mode 100644
index 000000000000..81df84aa1353
--- /dev/null
+++ b/contrib/nvi/docs/help
@@ -0,0 +1,229 @@
+MOVING THE CURSOR:
+ k - cursor up ^F - page forward /<pattern><CR> - search forward
+ j - cursor down ^B - page backward ?<pattern><CR> - search backward
+ h - cursor left w - move forward a "word" n - repeat the last search
+ l - cursor right b - move backward a "word"
+
+ENTERING TEXT:
+a - append after the cursor. Use the <escape> key to return to
+i - insert before the cursor. command mode.
+o - open a new line below the cursor.
+O - open new line above the cursor.
+
+WRITING AND EXITING:
+:w<Enter> - write the file
+:q<Enter> - exit the file
+:q!<Enter> - exit without writing the file
+:#<Enter> - move to a line (e.g., :35<Enter> moves to line 35)
+
+MISCELLANEOUS:
+^G - display the file name
+ J - join two lines (use i<Enter><escape> to split a line)
+ u - undo the last change (enter . after a 'u' to undo more than one change)
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+VI COMMANDS:
+ ^A search forward for cursor word
+ ^B scroll up by screens
+ ^C interrupt an operation (e.g. read, write, search)
+ ^D scroll down by half screens (setting count)
+ ^E scroll down by lines
+ ^F scroll down by screens
+ ^G file status
+ ^H move left by characters
+ ^J move down by lines
+ ^L redraw screen
+ ^M move down by lines (to first non-blank)
+ ^N move down by lines
+ ^P move up by lines
+ ^R redraw screen
+ ^T tag pop
+ ^U half page up (set count)
+ ^V input a literal character
+ ^W move to next screen
+ ^Y page up by lines
+ ^Z suspend editor
+ ^[ <escape> exit input mode, cancel partial commands
+ ^\ switch to ex mode
+ ^] tag push cursor word
+ ^^ switch to previous file
+ <space> move right by columns
+ ! filter through command(s) to motion
+ # number increment/decrement
+ $ move to last column
+ % move to match
+ & repeat substitution
+ ' move to mark (to first non-blank)
+ ( move back sentence
+ ) move forward sentence
+ + move down by lines (to first non-blank)
+ , reverse last F, f, T or t search
+ - move up by lines (to first non-blank)
+ . repeat the last command
+ / search forward
+ 0 move to first character
+ : ex command
+ ; repeat last F, f, T or t search
+ < shift lines left to motion
+ > shift lines right to motion
+ ? search backward
+ @ execute buffer
+ A append to the line
+ B move back bigword
+ C change to end-of-line
+ D delete to end-of-line
+ E move to end of bigword
+ F character in line backward search
+ G move to line
+ H move to count lines from screen top
+ I insert before first nonblank
+ J join lines
+ L move to screen bottom
+ M move to screen middle
+ N reverse last search
+ O insert above line
+ P insert before cursor from buffer
+ Q switch to ex mode
+ R replace characters
+ S substitute for the line(s)
+ T before character in line backward search
+ U Restore the current line
+ W move to next bigword
+ X delete character before cursor
+ Y copy line
+ ZZ save file and exit
+ [[ move back section
+ ]] move forward section
+ ^ move to first non-blank
+ _ move to first non-blank
+ ` move to mark
+ a append after cursor
+ b move back word
+ c change to motion
+ d delete to motion
+ e move to end of word
+ f character in line forward search
+ h move left by columns
+ i insert before cursor
+ j move down by lines
+ k move up by lines
+ l move right by columns
+ m set mark
+ n repeat last search
+ o append after line
+ p insert after cursor from buffer
+ r replace character
+ s substitute character
+ t before character in line forward search
+ u undo last change
+ w move to next word
+ x delete character
+ y copy text to motion into a cut buffer
+ z reposition the screen
+ { move back paragraph
+ | move to column
+ } move forward paragraph
+ ~ reverse case
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+EX COMMANDS:
+ ^D: scroll lines
+ !: filter lines through commands or run commands
+ #: display numbered lines
+ &: repeat the last subsitution
+ *: execute a buffer
+ <: shift lines left
+ =: display line number
+ >: shift lines right
+ @: execute a buffer
+ append: append input to a line
+ abbreviate: specify an input abbreviation
+ args: display file argument list
+ bg: background the current screen
+ change: change lines to input
+ cd: change the current directory
+ chdir: change the current directory
+ copy: copy lines elsewhere in the file
+ cscope: create a set of tags using a cscope command
+ delete: delete lines from the file
+ display: display buffers, screens or tags
+ [Ee]dit: begin editing another file
+ [Ee]x: begin editing another file
+ exusage: display ex command usage statement
+ file: display (and optionally set) file name
+ fg: switch the current screen and a backgrounded screen
+ global: execute a global command on lines matching an RE
+ help: display help statement
+ insert: insert input before a line
+ join: join lines into a single line
+ k: mark a line position
+ list: display lines in an unambiguous form
+ move: move lines elsewhere in the file
+ mark: mark a line position
+ map: map input or commands to one or more keys
+ mkexrc: write a .exrc file
+ [Nn]ext: edit (and optionally specify) the next file
+ number: change display to number lines
+ open: enter "open" mode (not implemented)
+ print: display lines
+ perl: run the perl interpreter with the command
+ perldo: run the perl interpreter with the command, on each line
+ preserve: preserve an edit session for recovery
+ [Pp]revious: edit the previous file in the file argument list
+ put: append a cut buffer to the line
+ quit: exit ex/vi
+ read: append input from a command or file to the line
+ recover: recover a saved file
+ resize: grow or shrink the current screen
+ rewind: re-edit all the files in the file argument list
+ s: substitute on lines matching an RE
+ script: run a shell in a screen
+ set: set options (use ":set all" to see all options)
+ shell: suspend editing and run a shell
+ source: read a file of ex commands
+ stop: suspend the edit session
+ suspend: suspend the edit session
+ t: copy lines elsewhere in the file
+ [Tt]ag: edit the file containing the tag
+ tagnext: move to the next tag
+ tagpop: return to the previous group of tags
+ tagprev: move to the previous tag
+ tagtop: discard all tags
+ tcl: run the tcl interpreter with the command
+ undo: undo the most recent change
+unabbreviate: delete an abbreviation
+ unmap: delete an input or command map
+ v: execute a global command on lines NOT matching an RE
+ version: display the program version information
+ visual: enter visual (vi) mode from ex mode
+ [Vv]isual: edit another file (from vi mode only)
+ viusage: display vi key usage statement
+ write: write the file
+ wn: write the file and switch to the next file
+ wq: write the file and exit
+ xit: exit
+ yank: copy lines to a cut buffer
+ z: display different screens of the file
+ ~: replace previous RE with previous replacement string,
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Edit options:
+noaltwerase filec="" nomodeline scroll=17 notildeop
+autoindent flash msgcat="./" nosearchincr timeout
+autoprint hardtabs=0 noprint="" nosecure nottywerase
+noautowrite noiclower nonumber shiftwidth=8 noverbose
+backup="" noignorecase nooctal noshowmatch warn
+nobeautify keytime=6 open noshowmode window=35
+cedit="" noleftright optimize sidescroll=16 nowindowname
+columns=80 lines=36 print="" noslowopen wraplen=0
+comment nolisp prompt nosourceany wrapmargin=0
+noedcompatible nolist readonly tabstop=8 wrapscan
+escapetime=1 lock noredraw taglength=0 nowriteany
+noerrorbells magic remap tags="tags"
+exrc matchtime=7 report=5 term="xterm"
+noextended mesg ruler noterse
+cdpath="/usr/src/local/nvi:/tmp"
+directory="/tmp"
+paragraphs="IPLPPPQPP LIpplpipbp"
+recdir="/var/tmp/vi.recover"
+sections="NHSHH HUnhsh"
+shell="/bin/csh"
+shellmeta="~{[*?$`'"\"
diff --git a/contrib/nvi/docs/internals/autowrite b/contrib/nvi/docs/internals/autowrite
new file mode 100644
index 000000000000..dbad6c8bae8a
--- /dev/null
+++ b/contrib/nvi/docs/internals/autowrite
@@ -0,0 +1,88 @@
+# @(#)autowrite 8.3 (Berkeley) 2/17/95
+
+Vi autowrite behavior, the fields with *'s are "don't cares".
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that are affected only by autowrite:
+
+Command File Autowrite? Action:
+ modified?
+-----------------------------------------------
+^Z Y Y Write file and suspend.
+^Z Y N Suspend.
+^Z N * Suspend.
+
+# This behavior is NOT identical to :edit.
+^^ Y Y Write file and jump.
+^^ Y N Error.
+^^ N * Jump.
+
+# The new nvi command ^T (:tagpop) behaves identically to ^].
+# This behavior is identical to :tag, :tagpop, and :tagpush with
+# force always set to N.
+^] Y Y Write file and jump.
+^] Y N Error.
+^] N * Jump.
+
+# There's no way to specify a force flag to the '!' command.
+:! Y Y Write file and execute.
+:! Y N Warn (if warn option) and execute.
+:! N * Execute.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that are affected by both autowrite and force:
+
+NOTE: the "force" flag is never passed on, i.e. the write
+to the file caused by the autowrite flag is never forced.
+
+Command File Autowrite? Force? Action:
+ modified? (!)
+-------------------------------------------------------
+# The first rule (YYY) is historic practice, but seems wrong.
+# In nvi, :next and :prev commands behave identically to :rewind.
+:next Y Y Y Write changes and jump.
+:next Y Y N Write changes and jump.
+:next Y N Y Abandon changes and jump.
+:next Y N N Error.
+:next N * * Jump.
+
+:rewind Y Y Y Abandon changes and jump.
+:rewind Y Y N Write changes and jump.
+:rewind Y N Y Abandon changes and jump.
+:rewind Y N N Error.
+:rewind N * * Jump.
+
+# The new nvi commands, :tagpop and :tagtop, behave identically to :tag.
+# Note, this behavior is the same as :rewind and friends, as well.
+:tag Y Y Y Abandon changes and jump.
+:tag Y Y N Write changes and jump.
+:tag Y N Y Abandon changes and jump.
+:tag Y N N Error.
+:tag N * * Jump.
+
+# The command :suspend behaves identically to :stop.
+:stop Y Y Y Suspend.
+:stop Y Y N Write changes and suspend.
+:stop Y N Y Suspend.
+:stop Y N N Suspend.
+:stop N * * Suspend.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that might be affected by autowrite, but aren't:
+
+Command File Autowrite? Force? Action:
+ modified? (!)
+-------------------------------------------------------
+#:ex, and :vi (executed while in vi mode) behave identically to :edit.
+:edit Y * Y Abandon changes and jump.
+:edit Y * N Error.
+:edit N * * Jump.
+
+:quit Y * Y Quit.
+:quit Y * N Error.
+:quit N * * Quit.
+
+:shell * * * Execute shell.
+
+:xit Y * * Write changes and exit.
+:xit N * * Exit.
diff --git a/contrib/nvi/docs/internals/context b/contrib/nvi/docs/internals/context
new file mode 100644
index 000000000000..8b1db32768b7
--- /dev/null
+++ b/contrib/nvi/docs/internals/context
@@ -0,0 +1,32 @@
+# @(#)context 8.6 (Berkeley) 10/14/94
+
+In historic vi, the previous context mark was always set:
+
+ex address:
+ any number, <question-mark>, <slash>, <dollar-sign>,
+ <single-quote>, <backslash>
+
+ex commands: undo, "z.", global, v
+
+vi commands: (, ), {, }, %, [[, ]], ^]
+
+nvi adds the vi command ^T to this list.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set if the
+line changed:
+
+vi commands: '<mark>, G, H, L, M, z
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set if the
+line or column changed:
+
+vi commands: `<mark>, /, ?, N, n
+
+nvi adds the vi command ^A to this list.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set in non-visual
+mode for ^R and ^L if the line changed, but I have yet to figure
+out how the line could change.
diff --git a/contrib/nvi/docs/internals/cscope.NOTES b/contrib/nvi/docs/internals/cscope.NOTES
new file mode 100644
index 000000000000..e0e3483d26ed
--- /dev/null
+++ b/contrib/nvi/docs/internals/cscope.NOTES
@@ -0,0 +1,142 @@
+Cscope Notes:
+
+The nvi tags structure has been reworked to handle the notion of multiple
+locations per tag. This supports cscope, which returns multiple locations
+per query. It will hopefully support ctags programs that create databases
+with multiple locations per tag as well.
+
+There is now a list of "tag queues" chained from each screen. Each tag
+queue has one or more "tag locations".
+
+ +----+ +----+ +----+ +----+
+ | EP | -> | Q1 | <-- | T1 | <-- | T2 |
+ +----+ +----+ --> +----+ --> +----+
+ |
+ +----+ +----+
+ | Q2 | <-- | T1 |
+ +----+ --> +----+
+ |
+ +----+ +----+
+ | Q3 | <-- | T1 |
+ +----+ --> +----+
+
+In the above diagram, each "Q" is a "tag queue", and each "T" is a
+tag location. Generally, the commands:
+
+ :tag create a new Q
+ ^[ create a new Q
+ :cscope find create a new Q
+ :tagnext move to the next T
+ :tagprev move to the previous T
+ :tagpop discard one or more Q's
+ ^T discard the most recent Q
+ :tagtop discard all Q's
+
+More specifically:
+
+:cs[cope] a[dd] cscope-dir
+
+ Attach to the cscope database in cscope-dir.
+
+:cs[cope] f[ind] c|d|e|f|g|i|s|t buffer|pattern
+
+ Query all attached cscopes for the pattern. The pattern is a
+ regular expression. If the pattern is a double-quote character
+ followed by a valid buffer name (e.g., "t), then the contents
+ of the named buffer are used as the pattern.
+
+ c: find callers of name
+ d: find all function calls made from name
+ e: find pattern
+ f: find files with name as substring
+ g: find definition of name
+ i: find files #including name
+ s: find all uses of name
+ t: find assignments to name
+
+ The find command pushes the current location onto the tags stack,
+ and switches to the first location resulting from the query, if
+ the query returned at least one result.
+
+:cs[cope] h[elp] [command]
+
+ List the cscope commands, or usage help on one command.
+
+:display c[onnections]
+
+ Display the list of cscope connections
+
+:display t[ags]
+
+ The tags display has been enhanced to display multiple tag
+ locations per tag query.
+
+:cs[cope] k[ill] #
+
+ Kill cscope connection number #.
+
+:cs[cope] r[eset]
+ Kill all attached cscopes. Useful if one got hung but you don't
+ know which one.
+
+:tagn[ext][!]
+
+ Move to the next tag resulting from a query.
+
+:tagpr[ev][!]
+
+ Return to the previous tag resulting from a query.
+
+:tagp[op], ^T
+
+ Return to the previous tag group (no change).
+
+:tagt[op]
+
+ Discard all tag groups (no change).
+
+Suggested maps:
+
+ " ^N: move to the next tag
+ map ^N :tagnext^M
+ " ^P: move to the previous tag
+ map ^P :tagprev^M
+
+ " Tab+letter performs a C-Scope query on the current word.
+ " C-Scope 12.9 has a text-string query (type t).
+ " C-Scope 13.3 replaces it with an assignment query; hence a==t.
+ map <tab>a "tye:csc find t"t
+ map <tab>c "tye:csc find c"t
+ map <tab>d "tye:csc find d"t
+ map <tab>e "tye:csc find e"t
+ map <tab>f "tye:csc find f"t
+ map <tab>g "tye:csc find g"t
+ map <tab>i "tye:csc find i"t
+ map <tab>s "tye:csc find s"t
+ map <tab>t "tye:csc find t"t
+
+To start nvi with an initial set of cscope directories, use the environment
+variable CSCOPE_DIRS. This variable should contain a <blank>-separated
+list of directories containing cscope databases. (This MAY be changed to
+be an edit option, I haven't really decided, yet.)
+
+Each cscope directory must contain a file named "cscope.out" which is the
+main cscope database, or nvi will not attempt to connect to a cscope to
+handle requests for that database.
+
+The file "cscope.tpath" may contain a colon-separated directory search
+path which will be used to find the files reported by cscope. If this
+cscope.tpath does not exist, then the paths are assumed to be relative to
+the cscope directory itself. This is an extension to the standard cscope,
+but seems important enough to keep.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Cscope Availability:
+
+UNIXWare System V Release 4.0 variants such as Sun Solaris 2.x
+(/opt/SUNWspro/bin) have version 11.5, and UNIXWare System V
+Release 4.1 has version 12.10 with an option for much faster
+searching.
+
+You can buy version 13.3 source with an unrestricted license
+for $400 from AT&T Software Solutions by calling +1-800-462-8146.
diff --git a/contrib/nvi/docs/internals/gdb.script b/contrib/nvi/docs/internals/gdb.script
new file mode 100644
index 000000000000..a1122343c162
--- /dev/null
+++ b/contrib/nvi/docs/internals/gdb.script
@@ -0,0 +1,76 @@
+# @(#)gdb.script 8.5 (Berkeley) 5/4/96
+
+# display the VI screen map
+# usage dmap(sp)
+define dmap
+ set $h = ((VI_PRIVATE *)$arg0->vi_private)->h_smap
+ set $t = ((VI_PRIVATE *)$arg0->vi_private)->t_smap
+ while ($h <= $t)
+ printf "lno: %2d; soff %d coff %d ", \
+ (int)$h->lno, (int)$h->soff, (int)$h->coff
+ if ($h->c_ecsize == 0)
+ printf "flushed\n"
+ else
+ printf "\n\tsboff %d; scoff %d\n", \
+ (int)$h->c_sboff, (int)$h->c_scoff
+ printf "\teboff %d; eclen %d; ecsize %d\n", \
+ (int)$h->c_eboff, (int)$h->c_eclen, \
+ (int)$h->c_ecsize
+ end
+ set $h = $h + 1
+ end
+end
+
+# display the tail of the VI screen map
+define tmap
+ set $h = ((VI_PRIVATE *)$arg0->vi_private)->h_smap
+ set $t = ((VI_PRIVATE *)$arg0->vi_private)->t_smap
+ while ($t >= $h)
+ printf "lno: %2d; soff %d coff %d ", \
+ (int)$t->lno, (int)$t->soff, (int)$t->coff
+ if ($t->c_ecsize == 0)
+ printf "flushed\n"
+ else
+ printf "\n\tsboff %d; scoff %d\n", \
+ (int)$t->c_sboff, (int)$t->c_scoff
+ printf "\teboff %d; eclen %d; ecsize %d\n", \
+ (int)$t->c_eboff, (int)$t->c_eclen, \
+ (int)$t->c_ecsize
+ end
+ set $t = $t - 1
+ end
+end
+
+# display the private structures
+define clp
+ print *((CL_PRIVATE *)sp->gp->cl_private)
+end
+define vip
+ print *((VI_PRIVATE *)sp->vi_private)
+end
+define exp
+ print *((EX_PRIVATE *)sp->ex_private)
+end
+
+# display the marks
+define markp
+ set $h = sp->ep->marks.next
+ set $t = &sp->ep->marks
+ while ($h != 0 && $h != $t)
+ printf "key %c lno: %d cno: %d flags: %x\n", \
+ ((MARK *)$h)->name, ((MARK *)$h)->lno, \
+ ((MARK *)$h)->cno, ((MARK *)$h)->flags
+ set $h = ((MARK *)$h)->next
+ end
+end
+
+# display the tags
+define tagp
+ set $h = sp->taghdr.next
+ set $t = &sp->taghdr
+ while ($h != 0 && $h != $t)
+ printf "tag: %s lno %d cno %d\n", ((TAG *)$h)->frp->fname, \
+ ((TAG *)$h)->lno, ((TAG *)$h)->cno
+ set $h= ((TAG *)$h)->next
+ end
+end
diff --git a/contrib/nvi/docs/internals/input b/contrib/nvi/docs/internals/input
new file mode 100644
index 000000000000..9a7506ee2337
--- /dev/null
+++ b/contrib/nvi/docs/internals/input
@@ -0,0 +1,350 @@
+# @(#)input 5.5 (Berkeley) 7/2/94
+
+MAPS, EXECUTABLE BUFFERS AND INPUT IN EX/VI:
+
+The basic rule is that input in ex/vi is a stack. Every time a key which
+gets expanded is encountered, it is expanded and the expansion is treated
+as if it were input from the user. So, maps and executable buffers are
+simply pushed onto the stack from which keys are returned. The exception
+is that if the "remap" option is turned off, only a single map expansion
+is done. I intend to be fully backward compatible with this.
+
+Historically, if the mode of the editor changed (ex to vi or vice versa),
+any queued input was silently discarded. I don't see any reason to either
+support or not support this semantic. I intend to retain the queued input,
+mostly because it's simpler than throwing it away.
+
+Historically, neither the initial command on the command line (the + flag)
+or the +cmd associated with the ex and edit commands was subject to mapping.
+Also, while the +cmd appears to be subject to "@buffer" expansion, once
+expanded it doesn't appear to work correctly. I don't see any reason to
+either support or not support these semantics, so, for consistency, I intend
+to pass both the initial command and the command associated with ex and edit
+commands through the standard mapping and @ buffer expansion.
+
+One other difference between the historic ex/vi and nex/nvi is that nex
+displays the executed buffers as it executes them. This means that if
+the file is:
+
+ set term=xterm
+ set term=yterm
+ set term=yterm
+
+the user will see the following during a typical edit session:
+
+ nex testfile
+ testfile: unmodified: line 3
+ :1,$yank a
+ :@a
+ :set term=zterm
+ :set term=yterm
+ :set term=xterm
+ :q!
+
+This seems like a feature and unlikely to break anything, so I don't
+intend to match historic practice in this area.
+
+The rest of this document is a set of conclusions as to how I believe
+the historic maps and @ buffers work. The summary is as follows:
+
+1: For buffers that are cut in "line mode", or buffers that are not cut
+ in line mode but which contain portions of more than a single line, a
+ trailing <newline> character appears in the input for each line in the
+ buffer when it is executed. For buffers not cut in line mode and which
+ contain portions of only a single line, no additional characters
+ appear in the input.
+2: Executable buffers that execute other buffers don't load their
+ contents until they execute them.
+3: Maps and executable buffers are copied when they are executed --
+ they can be modified by the command but that does not change their
+ actions.
+4: Historically, executable buffers are discarded if the editor
+ switches between ex and vi modes.
+5: Executable buffers inside of map commands are expanded normally.
+ Maps inside of executable buffers are expanded normally.
+6: If an error is encountered while executing a mapped command or buffer,
+ the rest of the mapped command/buffer is discarded. No user input
+ characters are discarded.
+7: Characters in executable buffers are remapped.
+8: Characters in executable buffers are not quoted.
+
+Individual test cases follow. Note, in the test cases, control characters
+are not literal and will have to be replaced to make the test cases work.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+1: For buffers that are cut in "line mode", or buffers that are not cut
+ in line mode but which contain portions of more than a single line, a
+ trailing <newline> character appears in the input for each line in the
+ buffer when it is executed. For buffers not cut in line mode and which
+ contain portions of only a single line, no additional characters
+ appear in the input.
+
+=== test file ===
+3Gw
+w
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+=== end test file ===
+
+ If the first line is loaded into 'a' and executed:
+
+1G"ayy@a
+
+ The cursor ends up on the '2', a result of pushing "3Gw^J" onto
+ the stack.
+
+ If the first two lines are loaded into 'a' and executed:
+
+1G2"ayy@a
+
+ The cursor ends up on the 'f' in "foo" in the fifth line of the
+ file, a result of pushing "3Gw^Jw^J" onto the stack.
+
+ If the first line is loaded into 'a', but not using line mode,
+ and executed:
+
+1G"ay$@a
+
+ The cursor ends up on the '1', a result of pushing "3Gw" onto
+ the stack
+
+ If the first two lines are loaded into 'a', but not using line mode,
+ and executed:
+
+1G2"ay$@a
+
+ The cursor ends up on the 'f' in "foo" in the fifth line of the
+ file, a result of pushing "3Gw^Jw^J" onto the stack.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+2: Executable buffers that execute other buffers don't load their
+ contents until they execute them.
+
+=== test file ===
+cwLOAD B^[
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"byy
+=== end test file ===
+
+ The command is loaded into 'e', and then executed. 'e' executes
+ 'a', which loads 'b', then 'e' executes 'b'.
+
+5G"eyy6G"ayy1G@e
+
+ The output should be:
+
+=== output file ===
+cwLOAD B^[
+LOAD B 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"byy
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+3: Maps and executable buffers are copied when they are executed --
+ they can be modified by the command but that does not change their
+ actions.
+
+ Executable buffers:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"eyy
+cwEXECUTE B^[
+=== end test file ===
+
+4G"eyy5G"ayy6G"byy1G@eG"ep
+
+ The command is loaded into 'e', and then executed. 'e' executes
+ 'a', which loads 'e', then 'e' executes 'b' anyway.
+
+ The output should be:
+
+=== output file ===
+line 1 foo bar baz
+EXECUTE B 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"eyy
+cwEXECUTE B^[
+line 1 foo bar baz
+=== end output file ===
+
+ Maps:
+
+=== test file ===
+Cine 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+=== end test file ===
+
+ Entering the command ':map = :map = rB^V^MrA^M1G==' shows that
+ the first time the '=' is entered the '=' map is set and the
+ character is changed to 'A', the second time the character is
+ changed to 'B'.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+4: Historically, executable buffers are discarded if the editor
+ switches between ex and vi modes.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwCHANGE^[Q:set
+set|visual|1Gwww
+=== end test file ===
+
+vi testfile
+4G"ayy@a
+
+ex testfile
+$p
+yank a
+@a
+
+ In vi, the command is loaded into 'a' and then executed. The command
+ subsequent to the 'Q' is (historically, silently) discarded.
+
+ In ex, the command is loaded into 'a' and then executed. The command
+ subsequent to the 'visual' is (historically, silently) discarded. The
+ first set command is output by ex, although refreshing the screen usually
+ causes it not to be seen.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+5: Executable buffers inside of map commands are expanded normally.
+ Maps inside of executable buffers are expanded normally.
+
+ Buffers inside of map commands:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwREPLACE BY A^[
+=== end test file ===
+
+4G"ay$:map x @a
+1Gx
+
+ The output should be:
+
+=== output file ===
+REPLACE BY A 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwREPLACE BY A^[
+=== end output file ===
+
+ Maps commands inside of executable buffers:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+=== end test file ===
+
+:map X cwREPLACE BY XMAP^[
+4G"ay$1G@a
+
+ The output should be:
+
+=== output file ===
+REPLACE BY XMAP 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+=== end output file ===
+
+ Here's a test that does both, repeatedly.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+Y
+cwREPLACED BY C^[
+blank line
+=== end test file ===
+
+:map x @a
+4G"ay$
+:map X @b
+5G"by$
+:map Y @c
+6G"cy$
+1Gx
+
+ The output should be:
+
+=== output file ===
+REPLACED BY C 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+Y
+cwREPLACED BY C^[
+blank line
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+6: If an error is encountered while executing a mapped command or
+ a buffer, the rest of the mapped command/buffer is discarded. No
+ user input characters are discarded.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+:map = 10GcwREPLACMENT^V^[^[
+=== end test file ===
+
+ The above mapping fails, however, if the 10G is changed to 1, 2,
+ or 3G, it will succeed.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+7: Characters in executable buffers are remapped.
+
+=== test file ===
+abcdefghijklmnnop
+ggg
+=== end test file ===
+
+:map g x
+2G"ay$1G@a
+
+ The output should be:
+
+=== output file ===
+defghijklmnnop
+ggg
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+8: Characters in executable buffers are not quoted.
+
+=== test file ===
+iFOO^[
+
+=== end test file ===
+
+1G"ay$2G@a
+
+ The output should be:
+
+=== output file ===
+iFOO^[
+FOO
+=== end output file ===
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
diff --git a/contrib/nvi/docs/internals/openmode b/contrib/nvi/docs/internals/openmode
new file mode 100644
index 000000000000..c64b76774b3e
--- /dev/null
+++ b/contrib/nvi/docs/internals/openmode
@@ -0,0 +1,36 @@
+ @(#)openmode 8.1 (Berkeley) 10/29/94
+
+Open mode has the following special behaviors:
+
+z, ^F, ^B:
+ If count is not specified, it shall default to the window
+ edit option - 2.
+
+ Write lines from the edit buffer starting at:
+
+ (the current line) - ((count - 2) / 2)
+
+ until:
+
+ (((count + 1) / 2) * 2) - 1
+
+ lines, or the last line in the edit buffer has been written. A
+ line consisting of the smaller of the number of columns in the
+ display divided by two or 40 ``-'' characters shall be written
+ immediately before and after the specified is written. These two
+ lines shall count against the total number of lines to be written.
+ A blank line shall be written after the last line is written.
+
+ z, ^F and ^B all behave identically.
+
+^D: Display the next scroll value lines, change the current line.
+
+^U: Change the current line, do nothing else.
+
+^E, ^Y: Do nothing.
+
+^L: Clear the screen and redisplay the current line.
+
+H, L, M:
+ Move to the first nonblank of the current line and do nothing
+ else.
diff --git a/contrib/nvi/docs/internals/quoting b/contrib/nvi/docs/internals/quoting
new file mode 100644
index 000000000000..a5fb8926a33c
--- /dev/null
+++ b/contrib/nvi/docs/internals/quoting
@@ -0,0 +1,208 @@
+# @(#)quoting 5.5 (Berkeley) 11/12/94
+
+QUOTING IN EX/VI:
+
+There are four escape characters in historic ex/vi:
+
+ \ (backslashes)
+ ^V
+ ^Q (assuming it wasn't used for IXON/IXOFF)
+ The terminal literal next character.
+
+Vi did not use the lnext character, it always used ^V (or ^Q).
+^V and ^Q were equivalent in all cases for vi.
+
+There are four different areas in ex/vi where escaping characters
+is interesting:
+
+ 1: In vi text input mode.
+ 2: In vi command mode.
+ 3: In ex command and text input modes.
+ 4: In the ex commands themselves.
+
+1: Vi text input mode (a, i, o, :colon commands, etc.):
+
+ The set of characters that users might want to escape are as follows.
+ As ^L and ^Z were not special in input mode, they are not listed.
+
+ carriage return (^M)
+ escape (^[)
+ autoindents (^D, 0, ^, ^T)
+ erase (^H)
+ word erase (^W)
+ line erase (^U)
+ newline (^J) (not historic practice)
+
+ Historic practice was that ^V was the only way to escape any
+ of these characters, and that whatever character followed
+ the ^V was taken literally, e.g. ^V^V is a single ^V. I
+ don't see any strong reason to make it possible to escape
+ ^J, so I'm going to leave that alone.
+
+ One comment regarding the autoindent characters. In historic
+ vi, if you entered "^V0^D" autoindent erasure was still
+ triggered, although it wasn't if you entered "0^V^D". In
+ nvi, if you escape either character, autoindent erasure is
+ not triggered.
+
+ Abbreviations were not performed if the non-word character
+ that triggered the abbreviation was escaped by a ^V. Input
+ maps were not triggered if any part of the map was escaped
+ by a ^V.
+
+ The historic vi implementation for the 'r' command requires
+ two leading ^V's to replace a character with a literal
+ character. This is obviously a bug, and should be fixed.
+
+2: Vi command mode
+
+ Command maps were not triggered if the second or later
+ character of a map was escaped by a ^V.
+
+ The obvious extension is that ^V should keep the next command
+ character from being mapped, so you can do ":map x xxx" and
+ then enter ^Vx to delete a single character.
+
+3: Ex command and text input modes.
+
+ As ex ran in canonical mode, there was little work that it
+ needed to do for quoting. The notable differences between
+ ex and vi are that it was possible to escape a <newline> in
+ the ex command and text input modes, and ex used the "literal
+ next" character, not control-V/control-Q.
+
+4: The ex commands:
+
+ Ex commands are delimited by '|' or newline characters.
+ Within the commands, whitespace characters delimit the
+ arguments. Backslash will generally escape any following
+ character. In the abbreviate, unabbreviate, map and unmap
+ commands, control-V escapes the next character, instead.
+
+ This is historic behavior in vi, although there are special
+ cases where it's impossible to escape a character, generally
+ a whitespace character.
+
+ Escaping characters in file names in ex commands:
+
+ :cd [directory] (directory)
+ :chdir [directory] (directory)
+ :edit [+cmd] [file] (file)
+ :ex [+cmd] [file] (file)
+ :file [file] (file)
+ :next [file ...] (file ...)
+ :read [!cmd | file] (file)
+ :source [file] (file)
+ :write [!cmd | file] (file)
+ :wq [file] (file)
+ :xit [file] (file)
+
+ Since file names are also subject to word expansion, the
+ underlying shell had better be doing the correct backslash
+ escaping. This is NOT historic behavior in vi, making it
+ impossible to insert a whitespace, newline or carriage return
+ character into a file name.
+
+4: Escaping characters in non-file arguments in ex commands:
+
+ :abbreviate word string (word, string)
+* :edit [+cmd] [file] (+cmd)
+* :ex [+cmd] [file] (+cmd)
+ :map word string (word, string)
+* :set [option ...] (option)
+* :tag string (string)
+ :unabbreviate word (word)
+ :unmap word (word)
+
+ These commands use whitespace to delimit their arguments, and use
+ ^V to escape those characters. The exceptions are starred in the
+ above list, and are discussed below.
+
+ In general, I intend to treat a ^V in any argument, followed by
+ any character, as that literal character. This will permit
+ editing of files name "foo|", for example, by using the string
+ "foo\^V|", where the literal next character protects the pipe
+ from the ex command parser and the backslash protects it from the
+ shell expansion.
+
+ This is backward compatible with historical vi, although there
+ were a number of special cases where vi wasn't consistent.
+
+4.1: The edit/ex commands:
+
+ The edit/ex commands are a special case because | symbols may
+ occur in the "+cmd" field, for example:
+
+ :edit +10|s/abc/ABC/ file.c
+
+ In addition, the edit and ex commands have historically
+ ignored literal next characters in the +cmd string, so that
+ the following command won't work.
+
+ :edit +10|s/X/^V / file.c
+
+ I intend to handle the literal next character in edit/ex consistently
+ with how it is handled in other commands.
+
+ More fun facts to know and tell:
+ The acid test for the ex/edit commands:
+
+ date > file1; date > file2
+ vi
+ :edit +1|s/./XXX/|w file1| e file2|1 | s/./XXX/|wq
+
+ No version of vi, of which I'm aware, handles it.
+
+4.2: The set command:
+
+ The set command treats ^V's as literal characters, so the
+ following command won't work. Backslashes do work in this
+ case, though, so the second version of the command does work.
+
+ set tags=tags_file1^V tags_file2
+ set tags=tags_file1\ tags_file2
+
+ I intend to continue permitting backslashes in set commands,
+ but to also permit literal next characters to work as well.
+ This is backward compatible, but will also make set
+ consistent with the other commands. I think it's unlikely
+ to break any historic .exrc's, given that there are probably
+ very few files with ^V's in their name.
+
+4.3: The tag command:
+
+ The tag command ignores ^V's and backslashes; there's no way to
+ get a space into a tag name.
+
+ I think this is a don't care, and I don't intend to fix it.
+
+5: Regular expressions:
+
+ :global /pattern/ command
+ :substitute /pattern/replace/
+ :vglobal /pattern/ command
+
+ I intend to treat a backslash in the pattern, followed by the
+ delimiter character or a backslash, as that literal character.
+
+ This is historic behavior in vi. It would get rid of a fairly
+ hard-to-explain special case if we could just use the character
+ immediately following the backslash in all cases, or, if we
+ changed nvi to permit using the literal next character as a
+ pattern escape character, but that would probably break historic
+ scripts.
+
+ There is an additional escaping issue for regular expressions.
+ Within the pattern and replacement, the '|' character did not
+ delimit ex commands. For example, the following is legal.
+
+ :substitute /|/PIPE/|s/P/XXX/
+
+ This is a special case that I will support.
+
+6: Ending anything with an escape character:
+
+ In all of the above rules, an escape character (either ^V or a
+ backslash) at the end of an argument or file name is not handled
+ specially, but used as a literal character.
+
diff --git a/contrib/nvi/docs/internals/structures b/contrib/nvi/docs/internals/structures
new file mode 100644
index 000000000000..a25c780c8e63
--- /dev/null
+++ b/contrib/nvi/docs/internals/structures
@@ -0,0 +1,68 @@
+# @(#)structures 5.4 (Berkeley) 10/4/95
+
+There are three major data structures in this package, plus a single data
+structure per screen type. The first is a single global structure (GS)
+which contains information common to all files and screens. It hold
+global things like the input key queues, and functions as a single place
+to hang things. For example, interrupt routines have to be able to find
+screen structures, and they can only do this if they have a starting
+point. The number of globals in nvi is dependent on the screen type, but
+every screen type will have at least one global, __global_list, which
+references the GS structure.
+
+The GS structure contains linked lists of screen (SCR) structures.
+Each SCR structure normally references a file (EXF) structure.
+
+The GS structure has a set of functions which update the screen and/or
+return information about the screen from the underlying screen package.
+The GS structure never goes away. The SCR structure persists over
+instances of screens, and the EXF structure persists over references to
+files.
+
+File names have different properties than files themselves, so the name
+information for a file is held in an FREF structure which is chained from
+the SCR structure.
+
+In general, functions are always passed an SCR structure, which usually
+references an underlying EXF structure. The SCR structure is necessary
+for any routine that wishes to talk to the screen, the EXF structure is
+necessary for any routine that wants to modify the file. The relationship
+between an SCR structure and its underlying EXF structure is not fixed,
+and various ex commands will substitute a new EXF in place of the current
+one, and there's no way to detect this.
+
+The naming of the structures is consistent across the program. (Macros
+even depend on it, so don't try and change it!) The global structure is
+"gp", the screen structure is "sp", and the file structure is "ep".
+
+A few other data structures:
+
+TEXT In nvi/cut.h. This structure describes a portion of a line,
+ and is used by the input routines and as the "line" part of a
+ cut buffer.
+
+CB In nvi/cut.h. A cut buffer. A cut buffer is a place to
+ hang a list of TEXT structures.
+
+CL The curses screen private data structure. Everything to
+ do standalone curses screens.
+
+MARK In nvi/mark.h. A cursor position, consisting of a line number
+ and a column number.
+
+MSG In nvi/msg.h. A chain of messages for the user.
+
+SEQ In nvi/seq.h. An abbreviation or a map entry.
+
+TK The Tcl/Tk screen private data structure. Everything to
+ do standalone Tcl/Tk screens.
+
+EXCMD In nvi/ex/ex.h. The structure that gets passed around to the
+ functions that implement the ex commands. (The main ex command
+ loop (see nvi/ex/ex.c) builds this up and then passes it to the
+ ex functions.)
+
+VICMD In nvi/vi/vi.h. The structure that gets passed around to the
+ functions that implement the vi commands. (The main vi command
+ loop (see nvi/vi/vi.c) builds this up and then passes it to the
+ vi functions.)
diff --git a/contrib/nvi/docs/interp/interp b/contrib/nvi/docs/interp/interp
new file mode 100644
index 000000000000..3da5a8f656dc
--- /dev/null
+++ b/contrib/nvi/docs/interp/interp
@@ -0,0 +1,190 @@
+# @(#)interp 8.5 (Berkeley) 10/19/96
+ Nvi Interpreter API
+
+Introduction:
+ The intention is to provide a way to graft a fairly generic extension
+ language into nvi. I think that the obvious candidates are Tcl/Rush,
+ Scheme, Python and Perl. Since the interpretation language chosen
+ is often a religious issue, the method should be as flexible as
+ possible. I don't expect to rewrite the editor in the interpreted
+ language, so that isn't a consideration.
+
+ Q: Is there any reason for nvi to support multiple interpreters in
+ a single executable?
+
+Interpreter functions in nvi:
+
+ 1: Function to get the current screen pointer.
+
+ SCR *inter_screen();
+
+ Return a pointer to the current screen.
+
+ 2: Functions to execute both ex and vi commands. The return value of the
+ function will be success/failure. The editor itself will continue to
+ handle the display of all messages and text for the foreseeable future.
+
+ int inter_vicmd(SCR *, char *cmds, size_t len);
+ int inter_excmd(SCR *, char *cmds, size_t len);
+
+ The byte string cmds, of length len, is entered into the standard
+ vi or ex parser, as if typed by the user. The characters are not
+ mapped in any way, i.e. the user's vi mappings don't apply. If
+ any error occurs, an error value is returned, and the rest of the
+ characters are discarded.
+
+ 3: Functions to handle lines of text in the file.
+
+ int inter_gline(SCR *, recno_t lno, char **lp, size_t *lenp);
+
+ Return a pointer to the text of the line lno, into the location
+ referenced by lp, and its length into the location referenced by
+ lenp.
+
+ int inter_dline(SCR *, recno_t lno);
+
+ Delete the line lno from the file.
+
+ int inter_aline(SCR *, recno_t lno, char *lp, size_t len);
+
+ Append a line consisting of the len bytes of text referenced by
+ lp to the line lno.
+
+ int inter_iline(SCR *, recno_t lno, char *lp, size_t len);
+
+ Insert a line consisting of the len bytes of text referenced by
+ lp before the line lno.
+
+ int inter_sline(SCR *, recno_t lno, char *lp, size_t len);
+
+ Replace line lno with the len bytes of text referenced by lp.
+
+ int inter_lline(SCR *, recno_t *lnop);
+
+ Return the number of the last line in the file in the location
+ referenced by lnop.
+
+ 4: Function to post an error message to the user.
+
+ int inter_msgq(SCR *, enum msgtype, char *fmt, ...);
+
+ Display the message for the user. Valid message types are:
+
+ M_BERR Error: M_ERR if verbose, else bell.
+ M_ERR Error: Display in inverse video.
+ M_INFO Info: Display in normal video.
+ M_SYSERR Error: M_ERR, using strerror(3) message.
+ M_VINFO Info: M_INFO if verbose, else ignore.
+
+ 5: Function to manipulate cut buffers.
+
+ int inter_setbuf(SCR *, CHAR_T buffer);
+
+ Create the specified buffer if it does not exist (the
+ buffer will have no contents).
+
+ int inter_getbuf(SCR *, CHAR_T buffer, TEXT **textp);
+
+ Return a pointer to the specified buffer in the location
+ referenced by textp. (Since a pointer to the real item
+ is being returned, it can be manipulated in any way the
+ interpreter chooses.)
+
+ 6: Functions to manipulate marks.
+
+ int inter_setmark(SCR *, CHAR_T name);
+
+ Create the specified mark if it does not exist (the
+ mark will have no contents).
+
+ int inter_getmark(SCR *, CHAR_T name, MARK **markp);
+
+ Return a pointer to the specified mark in the location
+ referenced by markp. (Since a pointer to the real item
+ is being returned, it can be manipulated in any way the
+ interpreter chooses.)
+
+ 7: Function to manipulate screens.
+
+ SCR *inter_iscreen();
+
+ Create a new screen, and return a pointer to it.
+
+ int inter_escreen(SCR *);
+
+ End a screen.
+
+ 8: Functions to get input from the user.
+
+ int inter_getchar(CHAR_T *chp,
+ enum maptype {NONE, INPUT, COMMAND} mapt);
+
+ Return a character from the keyboard into the location referenced
+ by chp. Mapt can be set to INPUT, COMMAND or NONE, depending on
+ what vi mappings should be applied to the character.
+
+ int inter_getline(SCR *, char *prompt, CHAR_T **linep,
+ size_t *lenp, enum maptype {NONE, INPUT, COMMAND} mapt);
+
+ Return a pointer to a line entered by the user, and its length,
+ into the locations linep and lenp. A prompt may be specified
+ by prompt, and mappings by mapt.
+
+ int inter_freeline(CHAR_T *linep);
+
+ Free the memory that was allocated by inter_getline();
+
+ 9: Function to retrieve and set the cursor.
+
+ int inter_getcursor(SCR *, MARK *mark);
+
+ Store the current cursor position in mark.
+
+ int inter_setcursor(SCR *, MARK *mark);
+
+ Set the current cursor position to mark.
+
+10: Function to return a motion command from the user.
+
+ int inter_getmotion(SCR *,
+ MARK *start, MARK *end, enum movetype {LINE, CHAR} *mt);
+
+ Nvi gets a motion command from the user and returns the starting
+ and stopping points of the movement, reordered from the beginning
+ to the end of the file. The standard rules for line/character
+ motions are applied, and returned to the interpreter through the
+ mt argument.
+
+11: Functions to return pathnames.
+
+12: Functions to return edit options.
+
+13: Nvi commands which will send text to the interpreter.
+
+ Nvi will have a new ex command "inter", which will pipe the rest of
+ the line up to the first unescaped <newline> to the interpreter, of
+ the following form:
+
+ :[address[,address]] inter [count] command
+
+ The interface from the ex command to the interpreter is a function:
+
+ int inter_ex(
+ SCR *, /* Current screen. */
+ char *cmd; /* The command. */
+ size_t len; /* The command length. */
+ MARK *start, /* Starting address for INTER_EX */
+ MARK *end, /* Ending address for INTER_EX */
+ int count); /* Count. */
+
+ Nvi will have a new vi command "*<buffer>" which will pipe the contents
+ of the named buffer to the interpreter, of the following form:
+
+ [count]*<buffer>
+
+ The interface from the vi command to the interpreter is a function:
+
+ int inter_vi(
+ SCR *, /* Current screen. */
+ CHAR_T buffer, /* Buffer. */
+ int count); /* Count. */
diff --git a/contrib/nvi/docs/interp/spell.ok b/contrib/nvi/docs/interp/spell.ok
new file mode 100644
index 000000000000..4ca990c21720
--- /dev/null
+++ b/contrib/nvi/docs/interp/spell.ok
@@ -0,0 +1,46 @@
+API
+BERR
+Mapt
+Nvi
+Perl
+SCR
+SYSERR
+Tcl
+VINFO
+aline
+callback
+chp
+cmd
+cmds
+dline
+enum
+escreen
+excmd
+freeline
+getbuf
+getcursor
+getline
+getmotion
+gline
+iline
+int
+interp
+iscreen
+lenp
+linep
+lline
+lno
+lnop
+lp
+mapt
+maptype
+movetype
+msgq
+msgtype
+nvi
+recno
+setcursor
+sline
+strerror
+textp
+vicmd
diff --git a/contrib/nvi/docs/spell.ok b/contrib/nvi/docs/spell.ok
new file mode 100644
index 000000000000..ec854ffa41b4
--- /dev/null
+++ b/contrib/nvi/docs/spell.ok
@@ -0,0 +1,173 @@
+API's
+Amiga
+Amir
+Bostic
+CFLAGS
+CR
+CTYPE
+Cscope
+Ctags
+DB
+DPURIFY
+Darren
+Ds
+Dw
+EXINIT
+Englar
+FreeBSD
+GDB
+Hiebert
+Kirkendall
+LC
+LN
+Linux
+Lite
+MSDOS
+Makefile
+Mayoff
+NEXINIT
+NVI
+NetBSD
+Neville
+Nvi
+Nvi's
+OS
+POSIX
+POSIX.2
+Perl
+PostScript
+README
+Roff
+Solaris
+SunOS
+Sven
+Tcl
+Tk
+Todo
+USD
+USD.doc
+USD:14
+USD:15
+USD:16
+UUNET
+UX
+Verdoolaege
+Vi
+Vi's
+WindowsNT
+ags
+al
+american
+api
+autowrite
+berkeley
+bitstring
+bitstring.h
+bostic
+bsd
+bugs.current
+ccil
+changelog
+cl
+clib
+cont
+cs
+cs.berkeley.edu
+cscope
+csh
+cshrc
+ctags
+darren
+db
+dbopen
+devel
+doc
+docs
+edu
+elvis
+email
+enum
+escapetime
+esr
+execl
+exrc
+exref
+fcntl
+filesystem
+free's
+ftp.cs.berkeley.edu
+gdb
+gdb.script
+gvr
+gz
+gzip'd
+hardtabs
+hiwaay
+html
+http
+ic
+iclower
+ignorecase
+il
+init
+init.tcl
+iso
+isprint
+kB
+keystrokes
+ksh
+lang
+ld
+lt
+lu
+mmap
+ncurses
+nex
+nexrc
+nul's
+nvi
+nvi's
+nvi.ALPHA.tar.gz
+nvi.tar.Z
+nvi.tar.gz
+openmode
+org
+perl
+preformatted
+ps
+queue.h
+readonly
+recover.script
+redistributable
+regex
+remapped
+setenv
+settable
+shiftwidth
+sirsi
+slowopen
+sourced
+struct
+sunsite
+svi
+tcl
+tclapi
+terminfo
+tk
+tknvi
+txt
+ucb
+unc
+uunet
+version's
+vi
+vi's
+vi.man
+vi.ref
+vi.ref.ps
+vi.ref.txt
+vitut
+writeable
+www
+xaw
+ynq
diff --git a/contrib/nvi/docs/tutorial/vi.advanced b/contrib/nvi/docs/tutorial/vi.advanced
new file mode 100644
index 000000000000..f757ad19c44a
--- /dev/null
+++ b/contrib/nvi/docs/tutorial/vi.advanced
@@ -0,0 +1,1458 @@
+Section 26: Index to the rest of the tutorial
+
+The remainder of the tutorial can be perused at your leisure. Simply find the
+topic of interest in the following list, and {/Section xx:/^M} to get to the
+appropriate section. (Remember that ^M means the return key)
+
+The material in the following sections is not necessarily in a bottom up
+order. It should be fairly obvious that if a section mentions something with
+which you are not familiar, say, buffers, you might {/buffer/^M} followed by
+several {n} to do a keyword search of the file for more details on that item.
+Another point to remember is that commands are surrounded by curly-braces and
+can therefore be found rather easily. To see where, say, the X command is
+used try {/{X}/^M}. Subsequent {n} will show you other places the command was
+used. We have tried to maintain the convention of placing the command letter
+surrounded by curly-braces on the section line where that command is
+mentioned.
+
+Finally, you should have enough 'savvy' at this point to be able to do your
+own experimentation with commands without too much hand-holding on the part of
+the tutorial. Experimentation is the best way to learn the effects of the
+commands.
+
+ Section Topic - description
+ ------- -------------------
+(Sections 1 through 25 are located in the file vi.beginner.)
+ 1 introduction: {^F} {ZZ}
+ 2 introduction (con't) and positioning: {^F} {^B}
+ 3 introduction (con't) and positioning: {^F} {^B}
+ 4 positioning: {^F} {^B} ^M (return key)
+ 5 quitting: {:q!} ^M key
+ 6 marking, cursor and screen positioning: {m} {G} {'} {z}
+ 7 marking, cursor and screen positioning: {m} {G} {'} {z}
+ 8 marking, cursor and screen positioning: {z} {m} {'}
+ 9 marking and positioning: {m} {''}
+ 10 line positioning: {^M} {-}
+ 11 scrolling with {^M}
+ 12 scrolling with {-} and screen adjustment {z}
+ 13 notes on use of tutorial
+ 14 other scrolling and postioning commands: {^E} {^Y} {^D} {^U}
+ 15 searching: {/ .. /^M}
+ 16 searching: {? .. ?^M} {n} (in search strings ^ $)
+ 17 searching: \ and magic-characters in search strings
+ 18 colon commands, exiting: {:} {ZZ}
+ 19 screen positioning: {H} {M} {L}
+ 20 character positioning: {w} {b} {0} {W} {B} {e} {E} {'} {`}
+ 21 cursor positioning: {l} {k} {j} {h}
+ 22 adding text: {i} {a} {I} {A} {o} {O} ^[ (escape key)
+ 23 character manipulation: {f} {x} {X} {w} {l} {r} {R} {s} {S} {J}
+ 24 undo: {u} {U}
+ 25 review
+(The following sections are in this file.)
+ 26 Index to the rest of the tutorial ******** YOU ARE HERE *******
+ 27 discussion of repeat counts and the repeat command: {.}
+ 28 more on low-level character motions: {t} {T} {|}
+ 29 advanced correction operators: {d} {c}
+ 30 updating the screen: {^R}
+ 31 text buffers: {"}
+ 32 rearranging and duplicating text: {p} {P} {y} {Y}
+ 33 recovering lost lines
+ 34 advanced file manipulation with vi
+ 34.1 more than one file at a time: {:n}
+ 34.2 reading files and command output: {:r}
+ 34.3 invoking vi from within vi: {:e} {:vi}
+ 34.4 escaping to a shell: {:sh} {:!}
+ 34.5 writing parts of a file: {:w}
+ 34.6 filtering portions of text: {!}
+ 35 advanced searching: magic patterns
+ 36 advanced substitution: {:s}
+ 37 advanced line addressing: {:p} {:g} {:v}
+ 38 higher level text objects and nroff: ( ) { } [[ ]]
+ 39 more about inserting text
+ 40 more on operators: {d} {c} {<} {>} {!} {=} {y}
+ 41 abbreviations: {:ab}
+ 42 vi's relationship with the ex editor: {:}
+ 43 vi on hardcopy terminals and dumb terminals: open mode
+ 44 options: {:set} {setenv EXINIT}
+ 44.1 autoindent
+ 44.2 autoprint
+ 44.3 autowrite
+ 44.4 beautify
+ 44.5 directory
+ 44.6 edcompatible
+ 44.7 errorbells
+ 44.8 hardtabs
+ 44.9 ignorecase
+ 44.10 lisp
+ 44.11 list
+ 44.12 magic
+ 44.13 mesg
+ 44.14 number
+ 44.15 open
+ 44.16 optimize
+ 44.17 paragraphs
+ 44.18 prompt
+ 44.19 readonly
+ 44.20 redraw
+ 44.21 remap
+ 44.22 report
+ 44.23 scroll
+ 44.24 sections
+ 44.25 shell
+ 44.26 shiftwidth
+ 44.27 showmatch
+ 44.28 slowopen
+ 44.29 tabstop
+ 44.30 tags
+ 44.31 taglength
+ 44.32 term
+ 44.33 terse
+ 44.34 timeout
+ 44.35 ttytype
+ 44.36 warn
+ 44.37 window
+ 44.38 wrapscan
+ 44.39 wrapmargin
+ 44.40 writeany
+ 44.41 w300, w1200, w9600
+
+Section 27: repetition counts and the repeat command {.}
+
+Most vi commands will use a preceding count to affect their behavior in some
+way. We have already seen how {3x} deletes three characters, and {22G} moves
+us to line 22 of the file. For almost all of the commands, one can survive by
+thinking of these leading numbers as a 'repeat count' specifying that the
+command is to be repeated so many number of times.
+
+Other commands use the repeat count slightly differently, like the {G} command
+which use it as a line number.
+
+For example:
+
+{3^D} means scroll down in the file three lines. Subsequent {^D} OR {^U} will
+scroll only three lines in their respective directions!
+
+{3z^M} says put line three of the file at the top of the screen, while {3z.}
+says put line three as close to the middle of the screen as possible.
+
+{50|} moves the cursor to column fifty in the current line.
+
+{3^F} says move forward 3 screenfulls. This is a repetition count. The
+documents advertise that {3^B} should move BACK three screenfulls, but I
+can't get it to work.
+
+Position the cursor on some text and try {3r.}. This replaces three characters
+with '...'. However, {3s.....^[} is the same as {3xi.....^[}.
+
+Try {10a+----^[}.
+
+A very useful instance of a repetition count is one given to the '.' command,
+which repeats the last 'change' command. If you {dw} and then {3.}, you will
+delete first one and then three words. You can then delete two more words with
+{2.}. If you {3dw}, you will delete three words. A subsequent {.} will delete
+three more words. But a subsequent {2.} will delete only two words, not three
+times two words.
+
+Caveat: The author has noticed that any repetition count with {^B} will NOT
+work: indeed, if you are at the end of your file and try {3^B} sufficiently
+often, the editor will hang you in an infinite loop. Please don't try it:
+take my word for it.
+
+Section 28: {t} {T} {|}
+
+Position the cursor on line 13 below:
+
+Line 13: Four score and seven years ago, our forefathers brought ...
+
+Note that {fv} moves the cursor on/over the 'v' in 'seven'. Do a {0} to return
+to the beginning of the line and try a {tv}. The cursor is now on/over the
+first 'e' in 'seven'. The {f} command finds the next occurrence of the
+specified letter and moves the cursor to it. The {t} command finds the
+specified letter and moves the cursor to the character immediately preceding
+it. {T} searches backwards, as does {F}.
+
+Now try {60|}: the cursor is now on the 'o' in 'brought', which is the
+sixtieth character on the line.
+
+Section 29: {d} {c}
+
+Due to their complexity we have delayed discussion of two of the most powerful
+operators in vi until now. Effective use of these operators requires more
+explanation than was deemed appropriate for the first half of the tutorial.
+
+{d} and {c} are called operators instead of commands because they consist of
+three parts: a count specification or a buffer specification (see section
+#BUFFERS), the {d} or {c}, and the object or range description. We will not
+discuss buffers at this stage, but will limit ourselves to count
+specifications. Examples speak louder than words: position the cursor at the
+beginning of line 14:
+
+Line 14: Euclid alone has looked on beauty bear.
+
+Obviously, there is something wrong with this quotation. Type {2fb} to
+position the cursor on the 'b' of 'bear'. Now, type {cwbare^[}
+and observe the results. The {cw} specifies that the change command {c} is to
+operate on a word object. More accurately, it specifies that the range of the
+change command includes the next word.
+
+Position the cursor on the period in Line 14. (one way is to use {f.})
+Now, type {cbbeast^[}. This specifies the range of the change command to be the
+previous word (the 'b' reminiscent of the {b} command). If we had wished to
+delete the word rather than change it, we would have used the {d} operator,
+rather than the {c} operator.
+
+Position the cursor at the beginning of the line with {0}. Type
+{d/look/^M}. The search string specified the range of the delete.
+Everything UP TO the word 'looking' was deleted from the line.
+
+In general, almost any command that would move the cursor will specify a range
+for these commands. The most confusing exception to this rule is when {dd} or
+{cc} is entered: they refer to the whole line. Following is a summary of the
+suffixes (suffices? suffici?) and the ranges they specify:
+
+ suffix will delete{d}/change{c}
+ ------ ------------------------
+ ^[ cancels the command
+ w the word to the right of the cursor
+ W ditto, but ignoring punctuation
+ b the word to the left of the cursor
+ B ditto, but ignoring punctuation
+ e see below.
+ E ditto
+ (space) a character
+ $ to the end of the line
+ ^ to the beginning of the line
+ / .. / up to, but not including, the string
+ ? .. ? back to and including the string
+ fc up to and including the occurrence of c
+ Fc back to and including the occurrence of c
+ tc up to but not including the occurrence of c
+ Tc back to but not including the occurrence of c
+ ^M TWO lines (that's right: two)
+ (number)^M that many lines plus one
+ (number)G up to and including line (number)
+ ( the previous sentence if you are at the beginning of
+ the current sentence, or the current sentence up to where
+ you are if you are not at the beginning of the current
+ sentence. Here, 'sentence' refers to the intuitive
+ notion of an English sentence, ending with '!', '?',
+ or '.' and followed by an end of line or two spaces.
+ ) the rest of the current sentence
+ { analogous to '(', but in reference to paragraphs:
+ sections of text surrounded by blank lines
+ } analogous to ')', but in reference to paragraphs
+ [[ analogous to '(', but in reference to sections
+ ]] analogous to ')', but in reference to sections
+ H the first line on the screen
+ M the middle line on the screen
+ L the last line on the screen
+ 3L through the third line from the bottom of the screen
+ ^F forward a screenful
+ ^B backward a screenful
+ :
+ : etc. etc. etc.
+
+This list is not exhaustive, but it should be sufficient to get the idea
+across: after the {c} or {d} operator, you can specify a range with another
+move-the-cursor command, and that is the region of text over which the command
+will be effective.
+
+Section 30: updating the screen {^R}
+
+Vi tries to be very intelligent about the type of terminal you are working on
+and tries to use the in-terminal computing power (if any) of your terminal.
+Also if the terminal is running at a low baud rate (say 1200 or below), vi sets
+various parameters to make things easier for you. For example, if you were
+running on a 300 baud terminal (that's 30 characters per second transmission
+rate) not all 24 lines of the screen would be used by vi. In addition, there
+is a large portion of the editor keeping track of what your screen currently
+looks like, and what it would look like after a command has been executed. Vi
+then compares the two, and updates only those portions of the screen that have
+changed.
+
+Furthermore, some of you may have noticed (it depends on your terminal) that
+deleting lines or changing large portions of text may leave some lines on the
+screen looking like:
+@
+meaning that this line of the screen does not correspond to any line in your
+file. It would cost more to update the line than to leave it blank for the
+moment. If you would like to see your screen fully up-to-date with the
+contents of your file, type {^R}.
+
+To see it in action, delete several lines with {5dd}, type {^R}, and then type
+{u} to get the lines back.
+
+Here is as good a place as any to mention that if the editor is displaying the
+end of your file, there may be lines on the screen that look like:
+~
+indicating that that screen line would not be affected by {^R}. These lines
+simply indicate the end of the file.
+
+Section 31: text buffers {"}
+
+Vi gives you the ability to store text away in "buffers". This feature is very
+convenient for moving text around in your file. There are a total of thirty-
+five buffers available in vi. There is the "unnamed" buffer that is used by all
+commands that delete text, including the change operator {c}, the substitute
+and replace commands {s} and {r}, as well as the delete operator {d} and delete
+commands {x} and {X}. This buffer is filled each time any of these commands
+are used. However, the undo command {u} has no effect on the unnamed buffer.
+
+There are twenty-six buffers named 'a' through 'z' which are available for the
+user. If the name of the buffer is capitalized, then the buffer is not
+overwritten but appended to. For example, the command {"qdd} will delete one
+line and store that line in the 'q' buffer, destroying the previous contents of
+the buffer. However, {"Qdd} will delete one line of text and append that line
+to the current contents of the 'q' buffer.
+
+Finally, there are nine buffers named '1' through '9' in which the last nine
+deletes are stored. Buffer 1 is the default buffer for the modify commands and
+is sometimes called the unnamed buffer.
+
+To reference a specific buffer, use the double-quote command {"} followed by
+the name of the buffer. The next two sections show how buffers can be used to
+advantage.
+
+Section 32: rearranging and duplicating text: {y} {Y} {p} {P}
+
+Position yourself on line 15 below and {z^M}:
+
+Line 15: A tree as lovely as a poem ...
+Line 16: I think that I shall never see
+
+Type {dd}. Line 15 has disappeared and been replaced with the empty line (one
+with the single character @ on it) or (again depending on your terminal) Line
+16 has moved up and taken its place. We could recover Line 15 with an undo
+{u} but that would simply return it to its original location. Obviously, the
+two lines are reversed, so we want to put line 15 AFTER line 16. This is
+simply done with the put command {p}, which you should type now. What has
+happened is that {dd} put Line 15 into the unnamed buffer, and the {p} command
+retrieved the line from the unnamed buffer.
+
+Now type {u} and observe that Line 15 disappears again (the put was undone
+without affecting the unnamed buffer). Type {P} and see that the capital {P}
+puts the line BEFORE the cursor.
+
+To get Line 15 where it belongs again type {dd}{p}.
+
+Also in Line 15 note that the words 'tree' and 'poem' are reversed. Using the
+unnamed buffer again: {ft}{dw}{ma}{fp}{P}{w}{dw}{`aP} will set things aright
+(note the use of the reverse quote).
+
+The put commands {p} and {P} do not affect the contents of the buffer.
+Therefore, multiple {p} or {P} will put multiple copies of the unnamed buffer
+into your file.
+
+Experiment with {d} and {p} on words, paragraphs, etc. Whatever {d}
+deletes, {p} can put.
+
+Position the cursor on Line 17 and {z^M}:
+
+Line 17: interest apple cat elephant boy dog girl hay farmer
+
+Our task is to alphabetize the words on line 17. With the named buffers (and a
+contrived example) it is quite easy:
+
+{"idw}{"adw}{"cdw}{"edw}{"bdw}{"ddw}{"gdw}{"hdw}{"fdw}
+
+stores each of the words in the named buffer corresponding to the first letter
+of each of the words ('interest' goes in buffer "i, 'apple' goes in buffer "a,
+etc.). Now to put the words in order type:
+
+{"ap$}{"bp$}{"cp$}{"dp$}{"ep$}{"fp$}{"gp$}{"hp$}{"ip$}
+
+Notice that, because 'farmer' was at the end of the line, {dw} did not include
+a space after it, and that, therefore, there is no space between 'farmer' and
+'girl'. This is corrected with {Fg}{i ^[}.
+
+This example could have been done just as easily with lines as with
+words.
+
+You do not have to delete the text in order to put it into a buffer. If all
+you wish to do is to copy the text somewhere else, don't use {d}, rather use
+the yank commands {y} or {Y}. {y} is like {d} and {c} - an operator rather
+than a command. It, too, takes a buffer specification and a range
+specification. Therefore, instead of {dw}{P} to load the unnamed buffer with a
+word without deleting the word, use {yw} (yank a word).
+
+{Y} is designed yank lines, and not arbitrary ranges. That is, {Y} is
+equivalent to {yy} (remember that operators doubled means the current line),
+and {3Y} is equivalent to {3yy}.
+
+If the text you yank or modify forms a part of a line, or is an object such as
+a sentence which partially spans more than one line, then when you put the text
+back, it will be placed after the cursor (or before if you use {P}). If the
+yanked text forms whole lines, they will be put back as whole lines, without
+changing the current line. In this case, the put acts much like the {o} or {O}
+command.
+
+The named buffers "a through "z are not affected by changing edit files.
+However, the unnamed buffer is lost when you change files, so to move text from
+one file to another you should use a named buffer.
+
+Section 33: recovering lost lines
+
+Vi also keeps track of the last nine deletes, whether you ask for it or not.
+This is very convenient if you would like to recover some text that was
+accidentally deleted or modified. Position the cursor on line 18 following,
+and {z^M}.
+
+
+Line 18: line 1
+Line 19: line 2
+Line 20: line 3
+Line 21: line 4
+Line 22: line 5
+Line 23: line 6
+Line 24: line 7
+Line 25: line 8
+Line 26: line 9
+Type {dd} nine times: now don't cheat with {9dd}! That is totally different.
+
+The command {"1p} will retrieve the last delete. Furthermore, when the
+numbered buffers are used, the repeat-command command {.} will increment the
+buffer numbers before executing, so that subsequent {.} will recover all nine
+of the deleted lines, albeit in reverse order. If you would like to review the
+last nine deletes without affecting the buffers or your file, do an undo {u}
+after each put {p} and {.}:
+
+{"1p}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}
+
+will show you all the buffers and leave them and your file intact.
+
+If you had cheated above and deleted the nine lines with {9dd}, all nine lines
+would have been stored in both the unnamed buffer and in buffer number 1.
+(Obviously, buffer number 1 IS the unnamed buffer and is just the default
+buffer for the modify commands.)
+
+Section 34: advanced file manipulation: {:r} {:e} {:n} {:w} {!} {:!}
+
+We've already looked at writing out the file you are editing with the
+{:w} command. Now let's look at some other vi commands to make editing
+more efficient.
+
+Section 34.1: more than one file at a time {:n} {:args}
+
+Many times you will want to edit more than one file in an editing session.
+Instead of entering vi and editing the first file, exiting, entering vi and
+editing the second, etc., vi will allow you to specify ALL files that you wish
+to edit on the invocation line. Therefore, if you wanted to edit file1 and
+file2:
+
+% vi file1 file2
+
+will set up file1 for editing. When you are done editing file one, write it
+out {:w^M} and then type {:n^M} to get the next file on the list. On large
+programming projects with many source files, it is often convenient just to
+specify all source files with, say:
+
+% vi *.c
+
+If {:n^M} brings in a file that does not need any editing, another {:n^M}
+will bring in the next file.
+
+If you have made changes to the first file, but decide to discard these changes
+and proceed to the next file, {:n!^M} forces the editor to discard the current
+contents of the editor.
+
+You can specify a new list of files after {:n}; e.g., {:n f1 f2 f3^M}. This
+will replace the current list of files (if any).
+
+You can see the current list of files being edited with {:args^M}.
+
+Section 34.2: reading files and command output: {:r}
+
+Typing {:r fname^M} will read the contents of file fname into the editor and
+put the contents AFTER the cursor line.
+
+Typing {:r !cmd^M} will read the output of the command cmd and place that
+output after the cursor line.
+
+Section 34.3: invoking vi from within vi: {:e} {:vi}
+
+To edit another file not mentioned on the invocation line, type {:e filename^M}
+or {:vi filename^M}. If you wish to discard the changes to the current file,
+use the exclamation point after the command, e.g. {:e! filename^M}.
+
+Section 34.4: escaping to a shell: {:sh} {:!} {^Z}
+
+Occasionally, it is useful to interrupt the current editing session to perform
+a UNIX task. However, there is no need to write the current file out, exit
+the editor, perform the task, and then reinvoke the editor on the same file.
+One thing to do is to spin off another process. If there are several UNIX
+commands you will need to execute, simply create another shell with {:sh^M}.
+At this point, the editor is put to sleep and will be reawakened when you log
+out of the shell.
+
+If it is a single command that you want to execute, type {:!cmd^M}, where cmd
+is the command that you wish to run. The output of the command will come to
+the terminal as normal, and will not be made part of your file. The message
+"[Hit return to continue]" will be displayed by vi after the command is
+finished. Hitting return will then repaint the screen. Typing another
+{:!cmd^M} at this point is also acceptable.
+
+However, there is a quicker, easier way: type {^Z}. Now this is a little
+tricky, but hang in there. When you logged into UNIX, the first program you
+began communicating with was a program that is called a "shell" (i.e. it 'lays
+over' the operating system protecting you from it, sort of like a considerate
+porcupine). When you got your first prompt on the terminal (probably a '%'
+character) this was the shell telling you to type your first command. When
+you typed {vi filename} for some file, the shell did not go away, it just went
+to sleep. The shell is now the parent of vi. When you type {^Z} the editor
+goes to sleep, the shell wakes up and says "you rang?" in the form of another
+prompt (probably '%'). At this point you are talking to the shell again and
+you can do anything that you could before including edit another file! (The
+only thing you can't do is log out: you will get the message "There are
+stopped jobs.")
+
+When your business with the shell is done, type {fg} for 'foreground' and the
+last process which you ^Z'd out of will be reawakened and the shell will go
+back to sleep. I will refer you to the documentation for the Berkeley shell
+'csh' for more information on this useful capability.
+
+Section 34.5: writing parts of a file: {:w}
+
+The {:w} command will accept a range specifier that will then write only a
+selected range of lines to a file. To write this section to a file, position
+the cursor on the section line (e.g. {/^Section 34.5:/^M}) and {z^M}. Now type
+{^G} to find out the line number (it will be something like "line 513"). Now
+{/^Section 34.6:/-1^M} to find the last line of this section, and {^G} to find
+its line number (it will be something like 542). To write out this section of
+text by itself to a separate file which we will call "sepfile", type
+{:510,542w sepfile^M}. If sepfile already exists, you will have to use the
+exclamation point: {:1147,1168w! sepfile^M} or write to a different, non-
+existent file.
+
+{:!cat sepfile^M} will display the file just written, and it should be the
+contents of this section.
+
+There is an alternate method of determining the line numbers for the write.
+{:set number^M} will repaint the screen with each line numbered. When the file
+is written and the numbers no longer needed, {:set nonumber^M} will remove the
+numbers, and {^R} will adjust the screen.
+
+Or, if you remember your earlier lessons about marking lines of text,
+mark the beginning and ending lines. Suppose we had used {ma} to mark the
+first line of the section and {mb} to mark the last. Then the command
+{:'a,'bw sepfile^M} will write the section into "sepfile". In general,
+you can replace a line number with the 'name' of a marked line (a single-quote
+followed by the letter used to mark the line)
+
+
+Section 34.6: filtering portions of text: {!}
+
+{!} is an operator like {c} and {d}. That is, it consists of a repetition
+count, {!}, and a range specifier. Once the {!} operator is entered in its
+entirety, a prompt will be given at the bottom of the screen for a UNIX
+command. The text specified by the {!} operator is then deleted and
+passed/filtered/piped to the UNIX command you type. The output of the UNIX
+command is then placed in your file. For example, place the cursor at the
+beginning of the following line and {z^M}:
+
+ls -l vi.tutorial
+********* marks the bottom of the output from the ls command **********
+
+Now type {!!csh^M}. The line will be replaced with the output from the ls
+command. The {u} command works on {!}, also.
+
+Here is an extended exercise to display some of these capabilities. When this
+tutorial was prepared, certain auxiliary programs were created to aid in its
+development. Of major concern was the formatting of sections of the tutorial
+to fit on a single screen, particularly the first few sections. What was
+needed was a vi command that would 'format' a paragraph; that is, fill out
+lines with as many words as would fit in eighty columns. There is no such vi
+command. Therefore, another method had to be found.
+
+Of course, nroff was designed to do text formatting. However, it produces a
+'page'; meaning that there may be many blank lines at the end of a formatted
+paragraph from nroff. The awk program was used to strip these blank lines from
+the output from nroff. Below are the two files used for this purpose: I refer
+you to documentation on nroff and awk for a full explanation of their function.
+Position the cursor on the next line and {z^M}.
+
+******** contents of file f **********
+#
+nroff -i form.mac | awk "length != 0 { print }"
+***** contents of file form.mac ******
+.na
+.nh
+.ll 79
+.ec 
+.c2 
+.cc 
+**************************************
+
+Determine the line numbers of the two lines of file f. They should be
+something like 574 and 575, although you better double check: this file is
+under constant revision and the line numbers may change inadvertently. Then
+{:574,575w f^M}. Do the same for the lines of file form.mac. They will be
+approximately 577 and 582. Then {:577,582w form.mac^M}. File f must have
+execute privileges as a shell file: {:!chmod 744 f^M}.
+
+Observe that this paragraph is
+rather ratty in appearance. With our newly created files we can
+clean it up dramatically. Position the cursor at the beginning
+of this paragraph and type the following sequence of
+characters
+(note that we must abandon temporarily our convention
+of curly braces since the command itself contains a curly brace - we
+will use square brackets for the nonce): [!}f^M].
+
+Here is a brief explanation of what has happened. By typing [!}f^M] we
+specified that the paragraph (all text between the cursor and the first blank
+line) will be removed from the edit file and piped to a UNIX program called
+"f". This is a shell command file that we have created. This shell file runs
+nroff, pipes its output to awk to remove blank lines, and the output from awk
+is then read back into our file in the place of the old, ratty paragraph. The
+file form.mac is a list of commands to nroff to get it to produce paragraphs
+to our taste (the right margin is not justified, the line is 79 characters
+long, words are not hyphenated, and three nroff characters are renamed to
+avoid conflict: note that in this file, the {^G} you see there is vi's display
+of the control-G character, and not the two separate characters ^ up-arrow and
+G upper-case g).
+
+This example was created before the existence of the fmt program. I now type
+[!}fmt^M] to get the same effect much faster. Actually, I don't type those
+six keys each time: I have an abbreviation (which see).
+
+Section 35: searching with magic patterns
+
+The documentation available for "magic patterns" (i.e. regular expressions) is
+very scanty. The following should explain this possibly very confusing feature
+of the editor. This section assumes that the magic option is on. To make
+sure, you might want to type {:set magic^M}.
+
+By "magic pattern" we mean a general description of a piece of text that the
+editor attempts to find during a search. Most search patterns consist of
+strings of characters that must be matched exactly, e.g. {/card/^M} searches
+for a specific string of four characters. Let us suppose that you have
+discovered that you consistently have mistyped this simple word as either ccrd
+or czrd (this is not so far-fetched for touch typists). You could {/ccrd/^M}
+and {n} until there are no more of this spelling, followed by {/czrd/^M} and
+{n} until there are no more of these. Or you could {/c.rd/^M} and catch all of
+them on the first pass. Try typing {/c.rd/^M} followed by several {n} and
+observe the effect.
+
+Line 27: card cord curd ceard
+
+When '.' is used in a search string, it has the effect of matching any single
+character.
+
+The character '^' (up-arrow) used at the beginning of a search string means
+the beginning of the line. {/^Line 27/^M} will find the example line above,
+while {/Line 27/^M} will find an occurrence of this string anywhere in the
+line.
+
+Similarly, {/ the$/^M} will find all occurrences of the word 'the' occurring
+at the end of a line. There are several of them in this file.
+
+Note that {:set nomagic^M} will turn off the special meaning of these magic
+characters EXCEPT for '^' and '$' which retain their special meanings at the
+beginning and end of a search string. Within the search string they hold no
+special meaning. Try {/\/ the$\//^M} and note that the dollar-sign is not the
+last character in the search string. Let the dollar-sign be the last
+character in the search string, as in {/\/ the$/^M} and observe the result.
+
+Observe the result of {/back.*file/^M}. This command, followed by sufficient
+{n}, will show you all lines in the file that contain both the words 'back'
+and 'file' on the same line. The '*' magic character specifies that the
+previous regular expression (the '.' in our example) is to be repeatedly
+matched zero or more times. In our example we specified that the words 'back'
+and 'file' must appear on the same line (they may be parts of words such as
+'backwards' or 'workfile') separated by any number (including zero) of
+characters.
+
+We could have specified that 'back' and 'file' are to be words by themselves by
+using the magic sequences '\<' or '\>'. E.g. {/\<back\>.*\<file\>/^M}. The
+sequence '\<' specifies that this point of the search string must match the
+beginning of a word, while '\>' specifies a match at the end of a word. By
+surrounding a string with these characters we have specified that they must be
+words.
+
+To find all words that begin with an 'l' or a 'w', followed by an 'a' or an
+'e', and ending in 'ing', try {/\<[lw][ea][a-z]*ing\>/^M}. This will match
+words like 'learning', 'warning', and 'leading'. The '[..]' notation matches
+exactly ONE character. The character matched will be one of the characters
+enclosed in the square brackets. The characters may be specified individually
+as in [abcd] or a '-' may be used to specify a range of characters as in [a-d].
+That is, [az] will match the letter 'a' OR the letter 'z', while [a-z] will
+match any of the lower case letters from 'a' through 'z'. If you would like to
+match either an 'a', a '-', or a 'z', then the '-' must be escaped: [a\-z] will
+match ONE of the three characters 'a', '-', or 'z'.
+
+If you wish to find all Capitalized words, try {/\<[A-Z][a-z]*\>/^M}. The
+following will find all character sequences that do NOT begin with an
+uncapitalized letter by applying a special meaning to the '^' character in
+square brackets: {/\<[^a-z][a-z]*\>/^M}. When '^' is the first character of a
+square-bracket expression, it specifies "all but these characters". (No
+one claimed vi was consistent.)
+
+To find all variable names (the first character is alphabetic, the remaining
+characters are alphanumeric): try {/\<[A-Za-z][A-Za-z0-9]*\>/^M}.
+
+In summary, here are the primitives for building regular expressions:
+
+ ^ at beginning of pattern, matches beginning of line
+ $ at end of pattern, matches end of line
+ . matches any single character
+ \< matches the beginning of a word
+ \> matches the end of a word
+ [str] matches any single character in str
+ [^str] matches any single character NOT in str
+ [x-y] matches any character in the ASCII range between x and y
+ * matches any number (including zero) of the preceding pattern
+
+Section 36: advanced substitution: {:s}
+
+The straightforward colon-substitute command looks like the substitute
+command of most line-oriented editors. Indeed, vi is nothing more than a
+superstructure on the line-oriented editor ex and the colon commands are
+simply a way of accessing commands within ex (see section #EX). This gives us
+a lot of global file processing not usually found in visual oriented editors.
+
+The colon-substitute command looks like: {:s/ .. / .. /^M} and will find the
+pattern specified after the first slash (this is called the search pattern),
+and replace it with the pattern specified after the second slash (called,
+obviously enough, the replacement pattern). E.g. position the cursor on line
+28 below and {:s/esample/example/^M}:
+
+Line 28: This is an esample.
+
+The {u} and {U} commands work for {:s}. The first pattern (the search pattern)
+may be a regular expression just as for the search command (after all, it IS a
+search, albeit limited to the current line). Do an {u} on the above line, and
+try the following substitute, which will do almost the same thing:
+{:s/s[^ ]/x/^M}.
+Better undo it with {u}. The first pattern {s[^ ]} matches an 's'
+NOT followed by a blank: the search therefore ignores the 's'es in 'This' and
+'is'. However, the character matched by {[^ ]} must appear in the replacement
+pattern. But, in general, we do not know what that character is! (In this
+particular example we obviously do, but more complicated examples will follow.)
+Therefore, vi (really ex) has a duplication mechanism to copy patterns matched
+in the search string into the replacement string. Line 29 below is a copy of
+line 28 above so you can adjust your screen.
+
+Line 29: This is an esample.
+
+In general, you can nest parts of the search pattern in \( .. \) and refer to
+it in the replacement pattern as \n, where n is a digit. The problem outlined
+in the previous paragraph is solved with {:s/s\([^ ]\)/x\1/^M}: try it. Here
+\1 refers to the first pattern grouping \( .. \) in the search string.
+
+Obviously, for a single line, this is rather tedious. Where it becomes
+powerful, if not necessary, is in colon-substitutes that cover a range of
+lines. (See the next section for a particularly comprehensive example.)
+
+If the entire character sequence matched by the search pattern is needed in
+the replacement pattern, then the unescaped character '&' can be used. On
+Line 29 above, try {:s/an e.ample/not &/^M}. If another line is to have the
+word 'not' prepended to a pattern, then '~' can save you from re-typing the
+replacement pattern. E.g. {:s/some pattern/~/^M} after the previous example
+would be equivalent to {:s/some pattern/not &/^M}.
+
+One other useful replacement pattern allows you to change the case of
+individual letters. The sequences {\u} and {\l} cause the immediately
+following character in the replacement to be converted to upper- or lower-case,
+respectively, if this character is a letter. The sequences {\U} and {\L} turn
+such conversion on, either until {\E} or {\e} is encountered, or until the end
+of the replacement pattern.
+
+For example, position the cursor on a line: pick a line, any line. Type
+{:s/.*/\U&/^M} and observe the result. You can undo it with {u}.
+
+The search pattern may actually match more than once on a single line.
+However, only the first pattern is substituted. If you would like ALL
+patterns matched on the line to be substituted, append a 'g' after the
+replacement pattern: {:s/123/456/g^M} will substitute EVERY occurrence
+on the line of 123 with 456.
+
+Section 37: advanced line addressing: {:p} {:g} {:v}
+
+Ex (available through the colon command in vi) offers several methods for
+specifying the lines on which a set of commands will act. For example, if you
+would like to see lines 50 through 100 of your file: {:50,100p^M} will display
+them, wait for you to [Hit return to continue], and leave you on line 100.
+Obviously, it would be easier just to do {100G} from within vi. But
+what if you would like to make changes to just those lines? Then the
+addressing is important and powerful.
+
+Line 30: This is a text.
+Line 31: Here is another text.
+Line 32: One more text line.
+
+The lines above contain a typing error that the author of this tutorial tends
+to make every time he attempts to type the word 'test'. To change all of these
+'text's into 'test's, try the following:
+{:/^Line 30/,/^Line 32/s/text/test/^M}. This finds the beginning and end of
+the portion of text to be changed, and limits the substitution to each of the
+lines in that range. The {u} command applies to ALL of the substitutions as
+a group.
+
+This provides a mechanism for powerful text manipulations.
+And very complicated examples.
+
+Line 33: This test is a.
+Line 34: Here test is another.
+Line 35: One line more test.
+
+The above three lines have the second word out of order. The following command
+string will put things right. Be very careful when typing this: it is very
+long, full of special characters, and easy to mess up. You may want to
+consider reading the following section to understand it before trying the
+experiment. Don't worry about messing up the rest of the file, though: the
+address range is specified.
+
+{:/^Line 33/,/^Line 35/s/\([^:]*\): \([^ ]*\) \([^ ]*\) \([^.]*\)/\1: \2 \4 \3/^M}
+
+There are several things to note about this command string. First of all, the
+range of the substitute was limited by the address specification {/^Line
+33/,/^Line 35/^M}. It might have been simpler to do {:set number^M} to see the
+line numbers directly, and then, in place of the two searches, typed
+the line numbers, e.g. {1396,1398}. Or to mark the lines with {ma} and {mb}
+and use {'a,'b}.
+
+Then follows the substitute pattern itself. To make it easier to understand
+what the substitute is doing, the command is duplicated below with the various
+patterns named for easier reference:
+
+ s/\([^:]*\): \([^ ]*\) \([^ ]*\) \([^.]*\)/\1: \2 \4 \3/
+ |--\1---| |--\2---| |--\3---| |--\4---|
+ |--------search pattern------------------|-replacement|
+ |--pattern---|
+
+In overview, the substitute looks for a particular pattern made up of
+sub-patterns, which are named \1, \2, \3, and \4. These patterns are specified
+by stating what they are NOT. Pattern \1 is the sequence of characters that
+are NOT colons: in the search string, {[^:]} will match exactly one character
+that is not a colon, while appending the asterisk {[^:]*} specifies that the
+'not a colon' pattern is to be repeated until no longer satisfied, and
+{\([^:]*\)} then gives the pattern its name, in this case \1. Outside of the
+specification of \1 comes {: }, specifying that the next two characters must be
+a colon followed by a blank.
+
+Patterns \2 and \3 are similar, specifying character sequences that are
+not blanks. Pattern \4 matches up to the period at the end of the line.
+
+The replacement pattern then consists of specifying the new order of the
+patterns.
+
+This is a particularly complicated example, perhaps the most complicated
+in this tutorial/reference. For our small examples, it is obviously
+tedious and error prone. For large files, however, it may be the most
+efficient way to make the desired modifications.
+
+(The reader is advised to look at the documentation for awk. This tool is very
+powerful and slightly simpler to use than vi for this kind of file
+manipulation. But, it is another command language to learn.)
+
+Many times, you will not want to operate on every line in a certain
+range. Rather you will want to make changes on lines that satisfy
+certain patterns; e.g. for every line that has the string 'NPS' on it,
+change 'NPS' to 'Naval Postgraduate School'. The {:g} addressing
+command was designed for this purpose. The example of this paragraph
+could be typed as {:g/NPS/s//Naval Postgraduate School/^M}.
+
+The general format of the command is {:g/(pattern)/cmds^M} and it
+works in the following way: all lines that match the pattern
+following the {:g} are 'tagged' in a special way. Then each of these
+lines have the commands following the pattern executed over them.
+
+Line 36: ABC rhino george farmer Dick jester lest
+Line 37: george farmer rhino lest jester ABC
+Line 38: rhino lest george Dick farmer ABC jester
+
+Type:
+
+{:g/^Line.*ABC/s/Dick/Harry Binswanger/|s/george farmer/gentleman george/p^M}
+
+There are several things of note here. First, lines 36, 37, and 38 above are
+tagged by the {:g}. Type {:g/^Line.*ABC/p^M} to verify this. Second, there
+are two substitutes on the same line separated by '|'. In general, any colon
+commands can be strung together with '|'. Third, both substitutes operate on
+all three lines, even though the first stubstitute works on only two of the
+lines (36 and 38). Fourth, the second substitute works on only two lines (36
+and 37) and those are the two lines printed by the trailing 'p'.
+
+The {:v} command works similarly to the {:g} command, except that the sense of
+the test for 'tagging' the lines is reversed: all lines NOT matching the search
+pattern are tagged and operated on by the commands.
+
+Using {^V} to quote carriage return (see section 39) can be used in global
+substitutions to split two lines. For example, the command
+{:g/\. /s//.^V^M/g^M} will change your file so that each sentence is on a
+separate line. (Note that we have to 'escape' the '.', because '.' by itself
+matches any character. Our command says to find any line which contains a
+period followed by 2 spaces, and inserts a carriage return after the period.)
+
+Caveat: In some of the documentation for ex and vi you may find the
+comment to the effect that {\^M} can be used between commands following
+{:g}. The author of this tutorial has never gotten this to work and has
+crashed the editor trying.
+
+Section 38: higher level text objects and nroff: {(} {)} [{] [}] {[[} {]]}
+
+(Note: this section may be a little confusing because of our command
+notation. Using curly braces to surround command strings works fine as
+long as the command string does not contain any curly braces itself.
+However, the curly braces are legitimate commands in vi. Therefore, for
+any command sequence that contains curly braces, we will surround that
+sequence with SQUARE braces, as on the previous Section line.)
+
+In working with a document, particularly if using the text formatting
+programs nroff or troff, it is often advantageous to work in terms of
+sentences, paragraphs, and sections. The operations {(} and {)} move to
+the beginning of the previous and next sentences, respectively. Thus
+the command {d)} will delete the rest of the current sentence; likewise
+{d(} will delete the previous sentence if you are at the beginning of
+the current sentence, or, if you are not at the beginning of a sentence,
+it will delete the current sentence from the beginning
+up to where you are.
+
+A sentence is defined to end at a '.', '!', or '?' which is followed
+by either the end of a line, or by two spaces. Any number of closing
+')', ']', '"', and ''' characters may appear after the '.', '!', or '?'
+before the spaces or end of line. Therefore, the {(} and {)} commands
+would recognize only one sentence in the following line, but two
+sentences on the second following line.
+
+Line 39: This is one sentence. Even though it looks like two.
+Line 40: This is two sentences. Because it has two spaces after the '.'.
+
+The operations [{] and [}] move over paragraphs and the operations {[[}
+and {]]} move over sections.
+
+A paragraph begins after each empty line, and also at each of a set of nroff
+paragraph macros. A section begins after each line with a form-feed ^L in the
+first column, and at each of a set of nroff section macros. When preparing a
+text file as input to nroff, you will probably be using a set of nroff macros
+to make the formatting specifications easier, or more to your taste. These
+macros are invoked by beginning a line with a period followed by the one or two
+letter macro name. Vi has been programmed to recognize these nroff macros, and
+if it doesn't recognize your particular macro you can use the {:set paragraphs}
+or {:set sections} commands so that it will.
+
+Section 39: more about inserting text
+
+There are a number of characters which you can use to make correnctions
+during input mode. These are summarized in the following table.
+
+ ^H deletes the last input character
+ ^W deletes the last input word
+ (erase) same as ^H; each terminal can define its own erase character;
+ for some it is ^H, for others it is the DELETE key, and for
+ others it is '@'.
+ (kill) deletes the input on this line; each terminal can define its
+ own line-kill character; for some it is ^U, for others it is
+ '@'; you will need to experiment on your terminal to find
+ out what your line-kill and erase characters are.
+ \ escapes a following ^H, (kill), and (erase) characters: i.e.
+ this is how to put these characters in your file.
+ ^[ escape key; ends insertion mode
+ ^? the delete key; interrupts an insertion, terminating it
+ abnormally.
+ ^M the return key; starts a new line.
+ ^D backtabs over the indentation set by the autoindent option
+ 0^D backtabs over all indentation back to the beginning of the line
+ ^^D (up-arrow followed by control-d)same as 0^D, except the indentation
+ will be restored at the beginning of the next line.
+ ^V quotes the next non-printing character into the file
+
+If you wish to type in your erase or kill character (say # or @ or ^U) then you
+must precede it with a \, just as you would do at the normal system command
+level. A more general way of typing non-printing characters into the file is
+to precede them with a ^V. The ^V echoes as a ^ character on which the cursor
+rests. This indicates that the editor expects you to type a control character
+and it will be inserted into the file at that point. There are a few
+exceptions to note. The implementation of the editor does not allow the null
+character ^@ to appear in files. Also the linefeed character ^J is used by the
+editor to separate lines in the file, so it cannot appear in the middle of a
+line. (Trying to insert a ^M into a file, or putting it in the replacement
+part of a substitution string will result in the matched line being split in
+two. This, in effect, is how to split lines by using a substitution.) You can
+insert any other character, however, if you wait for the editor to echo the ^
+before you type the character. In fact, the editor will treat a following
+letter as a request for the corresponding control character. This is the only
+way to type ^S or ^Q, since the system normally uses them to suspend and resume
+output and never gives them to the editor to process.
+
+If you are using the autoindent option you can backtab over the indent which it
+supplies by typing a ^D. This backs up to the boundary specified by the
+shiftwidth option. This only works immediately after the supplied autoindent.
+
+When you are using the autoindent option you may wish to place a label at the
+left margin of a line. The way to do this easily is to type ^ (up-arrow) and
+then ^D. The editor will move the cursor to the left margin for one line, and
+restore the previous indent on the next. You can also type a 0 followed
+immediately by a ^D if you wish to kill all indentation and not have it resume
+on the next line.
+
+Section 40: more on operators: {d} {c} {<} {>} {!} {=} {y}
+
+Below is a non-exhaustive list of commands that can follow the operators
+to affect the range over which the operators will work. However, note
+that the operators {<}, {>}, {!}, and {=} do not operate on any object
+less than a line. Try {!w} and you will get a beep. To get the
+operator to work on just the current line, double it. E.g. {<<}.
+
+ suffix will operate on
+ ------ ------------------------
+ ^[ cancels the command
+ w the word to the right of the cursor
+ W ditto, but ignoring punctuation
+ b the word to the left of the cursor
+ B ditto, but ignoring punctuation
+ e see below.
+ E ditto
+ (space) a character
+ $ to the end of the line
+ ^ to the beginning of the line
+ / .. / up to, but not including, the string
+ ? .. ? back to and including the string
+ fc up to and including the occurrence of c
+ Fc back to and including the occurrence of c
+ tc up to but not including the occurrence of c
+ Tc back to but not including the occurrence of c
+ ^M TWO lines (that's right: two)
+ (number)^M that many lines plus one
+ (number)G up to and including line (number)
+ ( the previous sentence if you are at the beginning of
+ the current sentence, or the current sentence up to where
+ you are if you are not at the beginning of the current
+ sentence. Here, 'sentence' refers to the intuitive
+ notion of an English sentence, ending with '!', '?',
+ or '.' and followed by an end of line or two spaces.
+ ) the rest of the current sentence
+ { analogous to '(', but in reference to paragraphs:
+ sections of text surrounded by blank lines
+ } analogous to ')', but in reference to paragraphs
+ [[ analogous to '(', but in reference to sections
+ ]] analogous to ')', but in reference to sections
+ H the first line on the screen
+ M the middle line on the screen
+ L the last line on the screen
+ 3L through the third line from the bottom of the screen
+ ^F forward a screenful
+ ^B backward a screenful
+ :
+ : etc. etc. etc.
+
+This list is not exhaustive, but it should be sufficient to get the idea
+across: after the operator, you can specify a range with a move-the-cursor
+command, and that is the region of text over which the operator will be
+effective.
+
+Section 41: abbreviations: {:ab}
+
+When typing large documents you may find yourself typing a large phrase
+over and over. Vi gives you the ability to specify an abbreviation for
+a long string such that typing the abbreviation will automatically
+expand into the longer phrase.
+
+Type {:ab nps Naval Postgraduate School^M}. Now type:
+
+{iThis is to show off the nps's UNIX editor.^M^[}
+
+Section 42: vi's relationship with the ex editor: {:}
+
+Vi is actually one mode of editing within the editor ex. When you are
+running vi you can escape to the line oriented editor of ex by giving
+the command {Q}. All of the colon-commands which were introduced above
+are available in ex. Likewise, most ex commands can be invoked from vi
+using {:}.
+
+In rare instances, an internal error may occur in vi. In this case you
+will get a diagnostic and will be left in the command mode of ex. You can
+then save your work and quit if you wish by giving the command {x} after
+the colon prompt of ex. Or you can reenter vi (if you are brave) by
+giving ex the command {vi}.
+
+Section 43: vi on hardcopy terminals and dumb terminals: open mode
+
+(The author has not checked the following documentation for accuracy. It is
+abstracted from the Introduction to Vi Editing document.)
+
+If you are on a hardcopy terminal or a terminal which does not have a cursor
+which can move off the bottom line, you can still use the command set of vi,
+but in a different mode. When you give the vi command to UNIX, the editor will
+tell you that it is using open mode. This name comes from the open command in
+ex, which is used to get into the same mode.
+
+The only difference between visual mode (normal vi) and open mode is the way in
+which the text is displayed.
+
+In open mode the editor uses a single line window into the file, and moving
+backward and forward in the file causes new lines to be displayed, always below
+the current line. Two commands of vi work differently in open: {z} and {^R}.
+The {z} command does not take parameters, but rather draws a window of context
+around the current line and then returns you to the current line.
+
+If you are on a hardcopy terminal, the {^R} command will retype the current
+line. On such terminals, the editor normally uses two lines to represent the
+current line. The first line is a copy of the line as you started to edit it,
+and you work on the line below this line. When you delete characters, the
+editor types a number of \'s to show you the characters which are deleted. The
+editor also reprints the current line soon after such changes so that you can
+see what the line looks like again.
+
+It is sometimes useful to use this mode on very slow terminals which can
+support vi in the full screen mode. You can do this by entering ex and using
+an {open} command.
+
+*********************************************************************
+Section 44: options: {:set} {setenv EXINIT}
+
+You will discover options as you need them. Do not worry about them very much
+on the first pass through this document. My advice is to glance through them,
+noting the ones that look interesting, ignoring the ones you don't understand,
+and try re-scanning them in a couple of weeks.
+
+If you decide that you have a favorite set of options and would like to change
+the default values for the editor, place a {setenv EXINIT} command in your
+.login file. When you are given an account under UNIX your directory has
+placed in it a file that is executed each time you log in. If one of the
+commands in this file sets the environment variable EXINIT to a string of vi
+commands, you can have many things done for you each time you invoke vi. For
+example, if you decide that you don't like tabstops placed every eight columns
+but prefer every four columns, and that you wish the editor to insert linefeeds
+for you when your typing gets you close to column 72, and you want
+autoindentation, then include the following line in your .login file:
+
+setenv EXINIT='set tabstop=4 wrapmargin=8 autoindent'
+
+or equivalently
+
+setenv EXINIT='se ts=4 wm=8 ai'
+
+Each time you bring up vi, this command will be executed and the options set.
+
+There are forty options in the vi/ex editor that the user can set for his/her
+own convenience. They are described in more detail in individual sections
+below. The section line will show the full spelling of the option name, the
+abbreviation, and the default value of the option. The text itself
+comes from the ex reference manual and is not the epitome of clarity.
+
+Section 44.1: {autoindent}, {ai} default: noai
+
+Can be used to ease the preparation of structured program text. At the
+beginning of each append, change or insert command or when a new line is opened
+or created by an append, change, insert, or substitute operation within open or
+visual mode, ex looks at the line being appended after, the first line changed
+or the line inserted before and calculates the amount of white space at the
+start of the line. It then aligns the cursor at the level of indentation so
+determined.
+
+If the user then types lines of text in, they will continue to be justified at
+the displayed indenting level. If more white space is typed at the beginning
+of a line, the following line will start aligned with the first non-white
+character of the previous line. To back the cursor up to the preceding tab
+stop one can hit {^D}. The tab stops going backwards are defined at multiples
+of the shiftwidth option. You cannot backspace over the indent, except by
+sending an end-of-file with a {^D}. A line with no characters added to it
+turns into a completely blank line (the white space provided for the autoindent
+is discarded). Also specially processed in this mode are lines beginning with
+an up-arrow `^' and immediately followed by a {^D}. This causes the input to
+be repositioned at the beginning of the line, but retaining the previous indent
+for the next line. Similarly, a `0' followed by a {^D} repositions at the
+beginning but without retaining the previous indent. Autoindent doesn't happen
+in global commands or when the input is not a terminal.
+
+Section 44.2: {autoprint}, {ap} default: ap
+
+Causes the current line to be printed after each delete, copy, join, move,
+substitute, t, undo or shift command. This has the same effect as supplying a
+trailing `p' to each such command. Autoprint is suppressed in globals, and
+only applies to the last of many commands on a line.
+
+Section 44.3: {autowrite}, {aw} default: noaw
+
+Causes the contents of the buffer to be written to the current file if you have
+modified it and give a next, rewind, stop, tag, or {!} command, or a control-
+up-arrow {^^} (switch files) or {^]} (tag goto) command in visual. Note, that
+the edit and ex commands do not autowrite. In each case, there is an
+equivalent way of switching when autowrite is set to avoid the autowrite
+({edit} for next, rewind! for rewind, stop! for stop, tag! for tag, shell
+for {!}, and {:e #} and a {:ta!} command from within visual).
+
+Section 44.4: {beautify}, {bf} default: nobeautify
+
+Causes all control characters except tab ^I, newline ^M and form-feed ^L to be
+discarded from the input. A complaint is registered the first time a backspace
+character is discarded. Beautify does not apply to command input.
+
+Section 44.5: {directory}, {dir} default: dir=/tmp
+
+Specifies the directory in which ex places its buffer file. If this directory
+in not writable, then the editor will exit abruptly when it fails to be able to
+create its buffer there.
+
+Section 44.6: {edcompatible} default: noedcompatible
+
+Causes the presence or absence of g and c suffixes on substitute commands to be
+remembered, and to be toggled by repeating the suffices. The suffix r makes
+the substitution be as in the {~} command, instead of like {&}.
+
+[Author's note: this should not concern users of vi.]
+
+Section 44.7: {errorbells}, {eb} default: noeb
+
+Error messages are preceded by a bell. However, bell ringing in open and
+visual modes on errors is not suppressed by setting noeb. If possible the
+editor always places the error message in a standout mode of the terminal (such
+as inverse video) instead of ringing the bell.
+
+Section 44.8: {hardtabs}, {ht} default: ht=8
+
+Gives the boundaries on which terminal hardware tabs are set (or on which the
+system expands tabs).
+
+Section 44.9: {ignorecase}, {ic} default: noic
+
+All upper case characters in the text are mapped to lower case in regular
+expression matching. In addition, all upper case characters in regular
+expressions are mapped to lower case except in character class specifications
+(that is, character in square brackets).
+
+Section 44.10: {lisp} default: nolisp
+
+Autoindent indents appropriately for lisp code, and the {(}, {)}, [{], [}],
+{[[}, and {]]} commands in open and visual modes are modified in a
+striaghtforward, intuitive fashion to have meaning for lisp.
+
+[Author's note: but don't ask me to define them precisely.]
+
+Section 44.11: {list} default: nolist
+
+All printed lines will be displayed (more) unambiguously, showing tabs as ^I
+and end-of-lines with `$'. This is the same as in the ex command {list}.
+
+Section 44.12: {magic} default: magic for {ex} and {vi}, nomagic for edit.
+
+If nomagic is set, the number of regular expression metacharacters is greatly
+reduced, with only up-arrow `^' and `$' having special effects. In addition
+the metacharacters `~' and `&' of the replacement pattern are treated as normal
+characters. All the normal metacharacters may be made magic when nomagic is
+set by preceding them with a `\'.
+
+[Author's note: In other words, if magic is set a back-slant turns the magic
+off for the following character, and if nomagic is set a back-slant turns the
+magic ON for the following character. And, no, we are not playing Dungeons and
+Dragons, although I think the writers of these option notes must have played it
+all the time.]
+
+Section 44.13: {mesg} default: mesg
+
+Causes write permission to be turned off to the terminal while you are in
+visual mode, if nomesg is set.
+
+[Author's note: I don't know if anyone could have made any one sentence
+paragraph more confusing than this one. What it says is: mesg allows people to
+write to you even if you are in visual or open mode; nomesg locks your terminal
+so they can't write to you and mess up your screen.]
+
+Section 44.14: {number, nu} default: nonumber
+
+Causes all output lines to be printed with their line numbers. In addition
+each input line will be prompted with its line number.
+
+Section 44.15: {open} default: open
+
+If {noopen}, the commands open and visual are not permitted. This is set for
+edit to prevent confusion resulting from accidental entry to open or visual
+mode.
+
+[Author's note: As you may have guessed by now, there are actually three
+editors available under Berkeley UNIX that are in reality the same
+program, ex, with different options set: ex itself, vi, and edit.]
+
+Section 44.16: {optimize, opt} default: optimize
+
+Throughput of text is expedited by setting the terminal to not do automatic
+carriage returns when printing more than one (logical) line of output, greatly
+speeding output on terminals without addressable cursors when text with leading
+white space is printed.
+
+[Author's note: I still don't know what this option does.]
+
+Section 44.17: {paragraphs, para} default: para=IPLPPPQPP LIbp
+
+Specifies the paragraphs for the [{] and [}] operations in open and visual.
+The pairs of characters in the option's value are the names of the nroff macros
+which start paragraphs.
+
+Section 44.18: {prompt} default: prompt
+
+Command mode input is prompted for with a `:'.
+
+[Author's note: Doesn't seem to have any effect on vi.]
+
+Section 44.19: {readonly}, {ro} default: noro, unless invoked with -R
+ or insufficient privileges on file
+
+This option allows you to guarantee that you won't clobber your file by
+accident. You can set the option and writes will fail unless you use an `!'
+after the write. Commands such as {x}, {ZZ}, the autowrite option, and in
+general anything that writes is affected. This option is turned on if you
+invoke the editor with the -R flag.
+
+Section 44.20: {redraw} default: noredraw
+
+The editor simulates (using great amounts of output), an intelligent terminal
+on a dumb terminal (e.g. during insertions in visual the characters to the
+right of the cursor position are refreshed as each input character is typed).
+Useful only at very high baud rates, and should be used only if the system is
+not heavily loaded: you will notice the performance degradation yourself.
+
+Section 44.21: {remap} default: remap
+
+If on, macros are repeatedly tried until they are unchanged. For example, if o
+is mapped to O, and O is mapped to I, then if remap is set, o will map to I,
+but if noremap is set, it will map to O .
+
+Section 44.22: {report} default: report=5 for ex and vi, 2 for edit
+
+Specifies a threshold for feedback from commands. Any command which modifies
+more than the specified number of lines will provide feedback as to the scope
+of its changes. For commands such as global, open, undo, and visual which have
+potentially more far reaching scope, the net change in the number of lines in
+the buffer is presented at the end of the command, subject to this same
+threshold. Thus notification is suppressed during a global command on the
+individual commands performed.
+
+Section 44.23: {scroll} default: scroll=1/2 window
+
+Determines the number of logical lines scrolled when a {^D} is received from a
+terminal in command mode, and determines the number of lines printed by a
+command mode z command (double the value of scroll).
+
+[Author's note: Doesn't seem to affect {^D} and {z} in visual (vi) mode.]
+
+Section 44.24: sections {sections} default: sections=SHNHH HU
+
+Specifies the section macros from nroff for the {[[} and {]]} operations in
+open and visual. The pairs of characters in the options's value are the names
+of the macros which start paragraphs.
+
+Section 44.25: {shell}, {sh} default: sh=/bin/sh
+
+Gives the path name of the shell forked for the shell escape command `!', and
+by the shell command. The default is taken from SHELL in the environment, if
+present.
+
+[Editor's note: I would suggest that you place the following line in
+your .login file:
+setenv SHELL '/bin/csh'
+]
+
+Section 44.26: {shiftwidth}, {sw} default: sw=8
+
+Used in reverse tabbing with {^D} when using autoindent to append text, and
+used by the shift commands. Should probably be the same value as the tabstop
+option.
+
+Section 44.27: {showmatch}, {sm} default: nosm
+
+In open and visual mode, when a `)' or `}' is typed, if the matching `(' or `{'
+is on the screen, move the cursor to it for one second. Extremely useful with
+complicated nested expressions, or with lisp.
+
+Section 44.28: {slowopen}, {slow} default: terminal dependent
+
+Affects the display algorithm used in visual mode, holding off display updating
+during input of new text to improve throughput when the terminal in use is both
+slow and unintelligent. See "An Introduction to Display Editing with Vi" for
+more details.
+
+Section 44.29: {tabstop}, {ts} default: ts=8
+
+The editor expands tabs ^I to tabstop boundaries in the display.
+
+Section 44.30: {taglength}, {tl} default: tl=0
+
+Tags are not significant beyond this many characters.
+A value of zero (the default) means that all characters are significant.
+
+Section 44.31: {tags} default: tags=tags /usr/lib/tags
+
+A path of files to be used as tag files for the tag command. A requested tag
+is searched for in the specified files, sequentially. By default files called
+tags are searched for in the current directory and in /usr/lib (a master file
+for the entire system).
+
+[Author's note: The author of this tutorial has never used this option, nor
+seen it used. I'm not even sure I know what they are talking about.]
+
+Section 44.32: {term} default: from environment variable TERM
+
+The terminal type of the output device.
+
+Section 44.33: {terse} default: noterse
+
+Shorter error diagnostics are produced for the experienced user.
+
+Section 44.34: {timeout} default: timeout
+
+Causes macros to time out after one second. Turn it off and they will
+wait forever. This is useful if you want multi-character macros, but if
+your terminal sends escape sequences for arrow keys, it will be
+necessary to hit escape twice to get a beep.
+
+[Editor's note: Another paragraph which requires a cryptographer.]
+
+Section 44.35: ttytype
+
+[Editor's note: I have found no documentation for this option at all.]
+
+Section 44.36: {warn} default: warn
+
+Warn if there has been `[No write since last change]' before a `!' command
+escape.
+
+Section 44.37: {window} default: window=speed dependent
+
+The number of lines in a text window in the visual command. The default is 8
+at slow speeds (600 baud or less), 16 at medium speed (1200 baud), and the full
+screen (minus one line) at higher speeds.
+
+Section 44.38: {wrapscan}, {ws} default: ws
+
+Searches using the regular expressions in addressing will wrap around past the
+end of the file.
+
+Section 44.39: {wrapmargin}, {wm} default: wm=0
+
+Defines a margin for automatic wrapover of text during input in open and visual
+modes. The numeric value is the number of columns from the right edge of the
+screen around which vi looks for a convenient place to insert a new-line
+character (wm=0 is OFF). This is very convenient for touch typists.
+Wrapmargin behaves much like fill/nojustify mode does in nroff.
+
+Section 44.40: {writeany}, {wa} default: nowa
+
+Inhibit the checks normally made before write commands, allowing a write to any
+file which the system protection mechanism will allow.
+
+Section 44.41: {w300}, {w1200}, {w9600} defaults: w300=8
+ w1200=16
+ w9600=full screen minus one
+
+These are not true options but set the default size of the window for when the
+speed is slow (300), medium (1200), or high (9600), respectively. They are
+suitable for an EXINIT and make it easy to change the 8/16/full screen rule.
+
+Section 45: Limitations
+
+Here are some editor limits that the user is likely to encounter:
+ 1024 characters per line
+ 256 characters per global command list
+ 128 characters per file name
+ 128 characters in the previous inserted and deleted text in open or
+ visual
+ 100 characters in a shell escape command
+ 63 characters in a string valued option
+ 30 characters in a tag name
+ 250000 lines in the file (this is silently enforced).
+
+The visual implementation limits the number of macros defined with map to 32,
+and the total number of characters in macros to be less than 512.
+
+[Editor's note: these limits may not apply to versions after 4.1BSD.]
diff --git a/contrib/nvi/docs/tutorial/vi.beginner b/contrib/nvi/docs/tutorial/vi.beginner
new file mode 100644
index 000000000000..3bf35ac939f8
--- /dev/null
+++ b/contrib/nvi/docs/tutorial/vi.beginner
@@ -0,0 +1,741 @@
+Section 1: {^F} {ZZ}
+
+To get out of this tutorial, type: ZZ (two capital Z's).
+
+Learning a new computer system implies learning a new text editor. These
+tutorial lessons were created by Dain Samples to help you come to grips with
+UC Berkeley's screen oriented editor called vi (for VIsual). This tutorial
+uses the vi editor itself as the means of presentation.
+
+For best use of this tutorial, read all of a screen before performing any of
+the indicated actions. This tutorial (or, at least, the first half of it) has
+been designed to systematically present the vi commands IF THE INSTRUCTIONS
+ARE FOLLOWED! If you are too adventuresome, you may find yourself lost. If
+you ever find yourself stuck, remember the first line of this section.
+
+OK, now find the control key on your keyboard; it usually has CTL or CTRL
+written on its upper surface. Your first assignment is to hold the control
+key down while you press the 'F' key on your keyboard. Please do so now.
+
+
+
+Section 2: {^F} {^B}
+Many of vi's commands use the control key and some other key in combination,
+as with the control and the 'F' key above. This is abbreviated CTL-F, or ^F.
+
+As you have probably guessed by now, ^F (CTL-F) moves you forward a fixed
+number of lines in the file. Throughout the remainder of the tutorial when
+you are ready to advance to the next section of text, hit ^F.
+
+The opposite command is ^B. Just for fun, you might want to try a ^B to see
+the previous section again. Be sure to do a ^F to return you here.
+
+Determine what the cursor looks like on your screen. Whatever it is (a box,
+an underscore, blinking, flashing, inverse, etc.) it should now be positioned
+in the upper left-hand corner of your screen under or on the S of Section.
+Become familiar with your cursor: to use vi correctly it is important to
+always know where the cursor is.
+
+Did you notice that when you do a ^F the cursor is left at the top of the
+screen, and a ^B leaves the cursor near the bottom of the screen? Try the two
+commands ^B^F again. And now do another ^F to see the next section.
+
+Section 3: {^F} {^B}
+You now have two basic commands for examining a file, both forwards (^F) and
+backwards (^B).
+
+Note that these are vi text editing commands: they are not commands for the
+tutorial. Indeed, this tutorial is nothing but a text file which you are now
+editing. Everything you do and learn in this tutorial will be applicable to
+editing text files.
+
+Therefore, when you are editing a file and are ready to see more of the text,
+entering ^F will get you to the next section of the file. Entering ^B will
+show you the previous section.
+
+Time for you to do another ^F.
+
+
+
+
+
+
+
+Section 4: {^F} {^B} {^M} (return key)
+We will adopt the notation of putting commands in curly braces so we can write
+them unambiguously. For example, if you are to type the command sequence
+"control B control F" (as we asked you to do above) it would appear as {^B^F}.
+This allows clear delineation of the command strings from the text. Remember
+that the curly braces are NOT part of the command string you are to type. Do
+NOT type the curly braces.
+
+Sometimes, the command string in the curly braces will be rather long, and may
+be such that the first couple of characters of the command will erase from
+the screen the string you are trying to read and type. It is suggested that
+you write down the longer commands BEFORE you type them so you won't forget
+them once they disappear.
+
+Now locate the return key on your keyboard: it is usually marked 'RETURN',
+indicate hitting the return key. In fact, the control-M key sequence is
+exactly the same as if you hit the return key, and vice versa.
+
+Now type {^F}.
+
+
+Section 5: {:q!} {ZZ} {^M} (return key)
+Recognize that this tutorial is nothing more than a text file that you
+are editing. This means that if you do something wrong, it is possible
+for you to destroy the information in this file. Don't worry. If this
+happens, type {ZZ} (two capital Z's) or {:q!^M} to leave the tutorial.
+Restart the tutorial. Once in the tutorial, you can then page forward
+with {^F} until you are back to where you want to be. (There are
+easier ways to do this, some of which will be discussed later, but this
+is the most straightforward.)
+
+You may want to write these commands down in a convenient place for quick
+reference: {:q!^M} and {ZZ}
+
+We will assume that you now know to do a {^F} to advance the file
+
+
+
+
+
+
+
+Section 6: {m} {G} {'} {z}
+Now that you know how to get around in the file via ^F and ^B let's look at
+other ways of examining a text file. Sometimes it is necessary, in the midst
+of editing a file, to examine another part of the file. You are then faced
+with the problem of remembering your place in the file, looking at the other
+text, and then getting back to your original location. Vi has a 'mark'
+command, m. Type {mp}. You have just 'marked' your current location in the
+file and given it the name 'p'. The command string below will do three
+things: position you at the beginning of the file (line 1), then return you to
+the location 'p' that you just marked with the 'm' command, and, since the
+screen will not look exactly the same as it does right now, the 'z' command
+will reposition the screen. (You may want to write the string down before
+typing it: once you type {1G} it will no longer be on the screen.)
+
+So now type {1G'pz^M} - a one followed by a capital G, followed by the quote
+mark, followed by a lower case 'p', then a lower case 'z', then a return
+(which is the same as a ^M). The {1G} moves you to line 1, i.e. the beginning
+of the file. The {'p} moves you to the location you marked with {mp}. The
+{z^M} command will repaint the screen putting the cursor at the top of the
+screen. (Now {^F}.)
+
+Section 7: {m} {G} {'} {z}
+Let's look at some variations on those commands. If you wanted to look at
+line 22 in the file and return to this location you could type {mp22G'p}. Do
+so now, observing that {22G} puts your cursor at the beginning of section 2 in
+the middle of the screen.
+
+Also note that, without the {z^M} command, the line with 'Section 7' on it is
+now in the MIDDLE of the screen, and not at the top. Our cursor is on the
+correct line (where we did the {mp} command) but the line is not where we
+might like it to be on the screen. That is the function of the {z^M} command.
+(Remember, ^M is the same as the 'return' key on your keyboard.) Type {z^M}
+now and observe the effect.
+
+As you can see, the 'Section 7' line is now at the top of the screen with the
+cursor happily under the capital S. If you would like the cursor line (i.e.
+the line which the cursor is on) in the middle of the screen again, you would
+type {z.}. If you wanted the cursor line to be at the BOTTOM of the screen,
+type {z-}. Try typing {z-z.z^M} and watch what happens.
+
+{^F}
+
+Section 8: {z} {m} {'}
+
+Note that the z command does not change the position of our cursor in the file
+itself, it simply moves the cursor around on the screen by moving the contents
+of the file around on the screen. The cursor stays on the same line of the
+file when using the z command.
+
+This brings up an important point. There are two questions that the users of
+vi continually need to know the answer to: "Where am I in the file?" and
+"Where am I on the screen?" The cursor on your terminal shows the answer to
+both questions. Some commands will move you around in the file, usually
+changing the location of the cursor on the screen as well. Other commands
+move the cursor around on the screen without changing your location in the
+file.
+
+Now type {ma}. Your location in the file has been given the name 'a'. If you
+type {'p'a} you will see the previous location we marked in section 7, and
+then will be returned to the current location. (You will want to do a {z^M}
+to repaint the screen afterwards.) Try it.
+{^F}
+
+Section 9: {m} {''}
+Now we can move about in our file pretty freely. By using the {m} command we
+can give the current cursor position a lower-case-character name, like 'p',
+'a', 'e', 'm', or 'b'. Using the {G} command preceded by a line number we can
+look at any line in the file we like. Using the single quote command {'}
+followed by a character used in an {m} command, we can return to any location
+in the file we have marked.
+
+However, try {m3}, or {mM}. You should hear a beep, or bell. Only lower-case
+letters are acceptable to the {m} and {'} commands: numbers, upper-case
+letters, and special characters are not acceptable.
+
+If you type the {'} command with a character that is lower-case alphabetic but
+that has not been used in an {m} command, or for which the 'marked' text has
+been deleted, you will also get a beep. Try {'i}. You should get a beep
+because the command {mi} has never been issued. (Unless you've been
+experimenting.)
+
+The command {''} attempts to return you to the location at which you last
+modified some part of your file. However, my experience has been that it is
+difficult to predict exactly where you will end up.
+Section 10: {^M} {-}
+Now do {ma}, marking your position at the top of the screen. Now hit {^M} (or
+return) until the cursor is right ...
+* <- here, over/under the asterisk. Now
+type {mb'a'b} and watch the cursor move from the asterisk to the top of the
+screen and back again.
+
+The {^M} command moves the cursor to the beginning of the next line. Now type
+{^M} until the cursor is right ...
+* <- here. The command to move the cursor to the beginning of the
+previous line is {-}. Practice moving the cursor around on the screen by using
+{^M} and {-}. BE CAREFUL to not move the cursor OFF the screen just yet. If
+you do, type {'az^M}.
+
+Now we can move to any line within the screen. Practice moving around in the
+file using the {^F}, {^B}, {-}, {^M}, {z}, and {'} commands. When you are
+fairly confident that you can get to where you need to be in the file, and
+position the cursor on the screen where you want it type {'az^M^F} (which, of
+course, moves you back to the beginning of this section, repositions the
+cursor at the top of the screen, and advances you to the next section).
+
+Section 11: scrolling: {^M}
+The cursor should now be on the S of 'Section 11', and this should be on the
+first line of the screen. If it is not, do {^M} or {-} as appropriate to put
+the cursor on the section line, and type {z^M}.
+
+Type {mc} to mark your place.
+
+Now type {^M} until the cursor is on the last line of this screen. Now do one
+more {^M} and observe the result. This is called scrolling. When you
+attempted to move to a line not displayed on the screen, the line at the top of
+the screen was 'scrolled off', and a line at the bottom of the screen was
+'scrolled on'. The top line with 'Section 11' should no longer be visible.
+
+Now type {'cz^M} to reset the screen and type {^F} for the next section.
+
+
+
+
+
+
+
+Section 12: {-} {z}
+
+The {-} command moves the cursor to the previous line in the file. Now type
+{-}, which attempts to move the cursor to the previous line in this file.
+However, that line is not on the screen. The resulting action will depend on
+your terminal. (Do a {^Mz^M} to reposition the file). On intelligent
+terminals (e.g. VT100s, Z19s, Concept 100s), a top line is 'scrolled on' and
+the bottom line is 'scrolled off'. Other terminals, however, may not have
+this 'reverse scrolling' feature. They will simply repaint the screen with
+the cursor line in the middle of the screen. On such terminals it is
+necessary to type {z^M} to get the cursor line back to the top of the screen.
+
+
+
+
+
+
+
+
+
+
+Section 13:
+Up until this point, the tutorial has always tried to make sure that the first
+line of each screen has on it the section number and a list of the commands
+covered in that section. This will no longer be strictly maintained. If you
+want the section line at the top of the screen, you now know enough commands to
+do it easily: do {^M} or {-} until the cursor is on the section line and
+then {z^M}. Also, from this point on, it may not be the case that a {^F} will
+put you at the beginning of the next section. Therefore, be aware of where you
+are in the file as we look at other commands. You may have to find your way
+back to a particular section without any help from the tutorial. If you do not
+feel comfortable with this, then it is suggested that you practice moving from
+section 1 to section 13, back and forth, using {^M}, {-}, {^F}, and {^B}
+commands for a while.
+
+Also make liberal use of the mark command {m}: if, for example, you make a
+habit of using {mz} to mark your current location in the file, then you will
+always be able to return to that location with {'z} if the editor does
+something strange and you have no idea where you are or what happened.
+
+And finally, the proscription against experimentation is hereby lifted: play
+with the editor. Feel free to try out variations on the commands and move
+around in the file. By this time you should be able to recover from any gross
+errors.
+
+Section 14: {^E} {^Y} {^D} {^U}
+Let us now look at a few other commands for moving around in the file, and
+moving the file around on the screen. Note that the commands we have already
+looked at are sufficient: you really don't need any more commands for looking
+in a file. The following commands are not absolutely necessary. However,
+they can make editing more convenient, and you should take note of their
+existence. But it would be perfectly valid to decide to ignore them on this
+first pass: you can learn them later when you see a need for them, if you ever
+do.
+
+First, let's clear up some potentially confusing language. In at least one
+place in the official document ('An Introduction to Display Editing with Vi'
+by William Joy, and Mark Horton, September 1980), the expression "to scroll
+down text" means that the cursor is moved down in your file. However, note
+that this may result in the text on the screen moving UP. This use of the
+word 'scroll' refers to the action of the cursor within the file. However,
+another legitimate use of the word refers to the action of the text on the
+screen. That is, if the lines on your screen move up toward the top of the
+screen, this would be 'scrolling the screen up'. If the lines move down
+toward the bottom of the screen, this would be refered to as scrolling down.
+
+I have tried to maintain the following jargon: 'scrolling' refers to what the
+text does on the screen, not to what the cursor does within the file. For the
+latter I will refer to the cursor 'moving', or to 'moving the cursor'. I
+realize that this is not necessarily consistent with Joy and Horton, but they
+were wrong.
+
+{^E} scrolls the whole screen up one line, keeping the cursor on the same line,
+if possible. However, if the cursor line is the first line on the screen, then
+the cursor is moved to the next line in the file. Try typing {^E}.
+
+{^Y} scrolls the screen down one line, keeping the cursor on the same line, if
+possible. However, if the cursor line is the last line on the screen, then the
+cursor is moved to the previous line in the file. Try it.
+
+{^D} moves the cursor down into the file, scrolling the screen up.
+
+{^U} moves the cursor up into the file, also scrolling the screen if the
+terminal you are on has the reverse scroll capability. Otherwise the
+screen is repainted.
+
+Note that {^E} and {^Y} move the cursor on the screen while trying to keep the
+cursor at the same place in the file (if possible: however, the cursor can
+never move off screen), while {^D} and {^U} keep the cursor at the same place
+on the screen while moving the cursor within the file.
+
+Section 15: {/ .. /^M}
+
+Another way to position yourself in the file is by giving the editor a string
+to search for. Type the following: {/Here 1/^M} and the cursor should end up
+right ...........................here ^. Now type {/Section 15:/^M} and the
+cursor will end up over/on .....................here ^. Now type {//^M} and
+observe that the cursor is now over the capital S five lines above this line.
+Typing {//^M} several more times will bounce the cursor back and forth between
+the two occurrences of the string. In other words, when you type a string
+between the two slashes, it is searched for. Typing the slashes with nothing
+between them acts as if you had typed the previous string again.
+
+Observe that the string you type between the two slashes is entered on the
+bottom line of the screen. Now type {/Search for x /^M} except replace the 'x'
+in the string with some other character, say 'b'. The message "Pattern not
+found" should appear on the bottom of the screen. If you hadn't replaced the
+'x', then you would have found the string. Try it.
+
+Section 16: {? .. ?^M} {n} (search strings: ^ $)
+
+When you surround the sought-for string with slashes as in {/Search/}, the
+file is searched beginning from your current position in the file. If the
+string is not found by the end of the file, searching is restarted at the
+beginning of the file. However, if you do want the search to find the
+PREVIOUS rather than the NEXT occurrence of the string, surround the string
+with question marks instead of slash marks.
+
+Below are several occurrences of the same string.
+Here 2 Here 2 Here 2
+ Here 2 Here 2.
+Observe the effect of the following search commands (try them in the
+sequence shown):
+{/Here 2/^M} {//^M} {??^M}
+{/^Here 2/^M} {//^M} {??^M}
+{/Here 2$/^M} {//^M} {??^M}
+
+The first command looks for the next occurrence of the string 'Here 2'.
+However the second line of commands looks for an occurrence of 'Here 2' that
+is at the beginning of the line. When the up-arrow is the first character of
+a search string it stands for the beginning of the line. When the dollar-sign
+is the last character of the search string it stands for the end of the line.
+Therefore, the third line of commands searches for the string only when it is
+at the end of the line. Since there is only one place the string begins a
+line, and only one place the string ends the line, subsequent {//^M} and
+{??^M} will find those same strings over and over.
+
+The {n} command will find the next occurrence of the / or ? search
+string. Try {/Here 2/^M} followed by several {n} and observe the
+effect. Then try {??^M} followed by several {n}. The {n} command
+remembers the direction of the last search. It is just a way to save a
+few keystrokes.
+
+Section 17: \ and magic-characters in search strings
+
+Now type {/Here 3$/^M}. You might expect the cursor to end up
+right......^ here. However, you will get "Pattern not found" at the bottom of
+the screen. Remember that the dollar-sign stands for the end of the line.
+Somehow, you must tell vi that you do not want the end of the line, but a
+dollar-sign. In other words, you must take away the special meaning that the
+dollar-sign has for the search mechanism. You do this (for any special
+character, including the up-arrow ^) by putting a back-slash ('\', not '/') in
+front of the character.
+
+Now try {/Here 3\$/^M} and you should end up nine lines above this one. Try
+{//^M} and note that it returns you to the same place, and not to the first
+line of this paragraph: the back-slash character is not part of the search
+string and will not be found. To find the string in the first line of this
+paragraph, type {/Here 3\\\$/^M}. There are three back-slashes: the first takes
+away the special meaning from the second, and the third takes away the special
+meaning from the dollar-sign.
+
+Following is a list of the characters that have special meanings in search
+strings. If you wish to find a string containing one of these characters, you
+will have to be precede the character with a backslash. These characters are
+called magic characters because of the fun and games you can have with them
+and they can have with you, if you aren't aware of what they do.
+
+ ^ - (up-arrow) beginning of a line
+ $ - (dollar-sign) end of a line
+ . - (period) matches any character
+ \ - (backslant) the escape character itself
+ [ - (square bracket) for finding patterns (see section #SEARCH)
+ ] - (square bracket) ditto
+ * - (asterisk) ditto
+
+Without trying to explain it here, note that {:set nomagic^M} turns off the
+special meanings of all but the ^ up-arrow, $ dollar-sign, and backslash
+characters.
+
+Section 18: {: (colon commands)} {ZZ}
+
+In this section we will discuss getting into and out of the editor in more
+detail. If you are editing a file and wish to save the results the command
+sequence {:w^M} writes the current contents of the file out to disk, using the
+file name you used when you invoked the editor. That is, if you are at the
+command level in Unix, and you invoke vi with {vi foo} where foo is the name
+of the file you wish to edit, then foo is the name of the file used by the
+{:w^M} command.
+
+If you are done, the write and quit commands can be combined into a single
+command {:wq^M}. An even simpler way is the command {ZZ} (two capital Z's).
+
+If, for some reason, you wish to exit without saving any changes you have made,
+{:q!^M} does the trick. If you have not made any changes, the exclamation
+point is not necessary: {:q^M}. Vi is pretty good about not letting you
+get out without warning you that you haven't saved your file.
+
+We have mentioned before that you are currently in the vi editor, editing a
+file. If you wish to start the tutorial over from the very beginning, you
+could {ZZ}, and then type {vi.tut beginner} in response to the Unix prompt.
+This will create a fresh copy of this file for you, which might be necessary
+if you accidentally destroyed the copy you were working with. Just do a
+search for the last section you were in: e.g. {/Section 18:/^Mz^M}.
+
+Section 19: {H} {M} {L}
+
+Here are a few more commands that will move you around on the screen. Again,
+they are not absolutely necessary, but they can make screen positioning easier:
+
+{H} - puts the cursor at the top of the screen (the 'home' position)
+
+{M} - puts the cursor in the middle of the screen
+
+{L} - puts the cursor at the bottom of the screen.
+
+Try typing {HML} and watch the cursor.
+
+Try typing {5HM5L} and note that 5H puts you five lines from the top of the
+screen, and 5L puts you five lines from the bottom of the screen.
+
+Section 20: {w} {b} {0} {W} {B} {e} {E} {'} {`}
+
+Up to this point we have concentrated on positioning in the file, and
+positioning on the screen. Now let's look at positioning in a line. Put the
+cursor at the beginning of the following line and type {z^M}:
+
+This is a test line: your cursor should initially be at its beginning.
+
+The test line should now be at the top of your screen. Type {w} several times.
+Note that it moves you forward to the beginning of the next word. Now type
+{b} (back to the beginning of the word) several times till you are at the
+beginning of the line. (If you accidentally type too many {b}, type {w} until
+you are on the beginning of the line again.) Type {wwwww} (five w's) and note
+that the cursor is now on the colon in the sentence. The lower-case w command
+moves you forward one word, paying attention to certain characters such as
+colon and period as delimiters and counting them as words themselves. Now
+type {0} (zero, not o 'oh'): this moves you to the beginning of the current
+line. Now type {5w} and notice that this has the effect of repeating {w} five
+times and that you are now back on the colon. Type {0} (zero) again. To
+ignore the delimiters and to move to the beginning of the next word using only
+blanks, tabs and carriage-returns (these are called white-space characters) to
+delimit the words, use the {W} command: upper-case W. {B} takes you back a
+word using white-space characters as word delimiters.
+
+Note that the commands {wbWB} do not stop at the beginning or end of a line:
+they will continue to the next word on the next line in the direction specified
+(a blank line counts as a word).
+
+If you are interested in the END of the word, and not the BEGINNING, then use
+the {e} and {E} commands. These commands only move forward and there are no
+corresponding 'reverse search' commands for the end of a word.
+
+Also, we have been using the {'} command to move the cursor to a position that
+we have previously marked with the {m} command. However, position the cursor
+in the middle of a line (any line, just pick one) and type {mk}, marking that
+position with the letter k. Now type a few returns {^M} and type {'k}.
+Observe that the cursor is now at the beginning of the line that you marked.
+Now try {`k}: note that this is the reverse apostrophe, or back-quote, or grave
+accent, or whatever you want to call it. Also note that it moves you to the
+character that was marked, not just to the line that was marked.
+
+In addition, the {``} command works just like the {''} command except that you
+are taken to the exact character, not just to the line. (I'm still not
+sure which exact character, just as I'm still not sure which line.)
+
+Section 21: {l} {k} {j} {h}
+
+There are several commands to move around on the screen on a character by
+character basis:
+
+l - moves the cursor one character to the RIGHT
+k - moves the cursor UP one line
+j - moves the cursor DOWN one line
+h - moves the cursor one character to the LEFT
+
+Section 22: {i} {a} {I} {A} {o} {O} ^[ (escape key)
+
+For this and following sections you will need to use the ESCAPE key on your
+terminal. It is usually marked ESC. Since the escape key is the same as
+typing {^[} we will use ^[ for the escape key.
+
+Probably the most often used command in an editor is the insert command. Below
+are two lines of text, the first correct, the second incorrect. Position your
+cursor at the beginning of Line 1 and type {z^M}.
+
+Line 1: This is an example of the insert command.
+Line 2: This is an of the insert command.
+
+To make line 2 look like line 1, we are going to insert the characters
+'example ' before the word 'of'. So, now move the cursor so that it is
+positioned on the 'o' of 'of'. (You can do this by typing {^M} to move
+to the beginning of line 2, followed by {6w} or {wwwwww} to position the cursor
+on the word 'of'.)
+
+Now carefully type the following string and observe the effects:
+ {iexample ^[} (remember: ^[ is the escape key)}
+The {i} begins the insert mode, and 'example ' is inserted into the line:
+be sure to notice the blank in 'example '. The ^[ ends insertion mode,
+and the line is updated to include the new string. Line 1 should look exactly
+like Line 2.
+
+Move the cursor to the beginning of Line 3 below and type {z^M}:
+
+Line 3: These lines are examples for the 'a' command.
+Line 4: These line are examples for the '
+
+We will change line four to look like line three by using the append command.
+We need to append an 's' to the word 'line'. Position the cursor on the 'e'
+of 'line'. You can do this in several ways, one way is the following:
+First, type {/line /^M}. This puts us on the word 'line' in Line 4
+(the blank in the search string is important!). Next, type {e}. The 'e' puts
+us at the end of the word. Now, type {as^[ (^[ is the escape character)}.
+The 'a' puts us in insert mode, AFTER the current character. We appended the
+'s', and the escape ^[ ended the insert mode.
+
+The difference between {i} (insert) and {a} (append) is that {i} begins
+inserting text BEFORE the cursor, and {a} begins inserting AFTER the cursor.
+
+Now type {Aa' command.^[}. The cursor is moved to the end of the line and the
+string following {A} is inserted into the text. Line 4 should now look like
+line 3.
+
+Just as {A} moves you to the end of the line to begin inserting, {I} would
+begin inserting at the FRONT of the line.
+
+To begin the insertion of a line after the cursor line, type {o}. To insert a
+line before the cursor line, type {O}. In other words {o123^[} is equivalent
+to {A^M123^[}, and {O123^[} is equivalent to {I123^M^[}. The text after the
+{o} or {O} is ended with an escape ^[.
+
+This paragraph contains information that is terminal dependent: you will just
+have to experiment to discover what your terminal does. Once in the insert
+mode, if you make a mistake in the typing, ^H will delete the previous
+character up to the beginning of the current insertion. ^W will delete the
+previous word, and one of ^U, @, or ^X will delete the current line (up to the
+beginning of the current insertion). You will need to experiment with ^U, @,
+and ^X to determine which works for your terminal.
+
+Section 23: {f} {x} {X} {w} {l} {r} {R} {s} {S} {J}
+
+Position the cursor at the beginning of line 5 and {z^M}:
+
+Line 5: The line as it should be.
+Line 6: The line as it shouldn't be.
+
+To make Line 6 like Line 5, we have to delete the 'n', the apostrophe, and the
+'t'. There are several ways to position ourselves at the 'n'. Choose
+whichever one suits your fancy:
+
+{/n't/^M}
+{^M7w6l} or {^M7w6 } (note the space)
+{^M3fn} (finds the 3rd 'n' on the line)
+
+Now {xxx} will delete the three characters, as will {3x}.
+
+Note that {X} deletes the character just BEFORE the cursor, as opposed
+to the character AT the cursor.
+
+Position the cursor at line 7 and {z^M}:
+
+Line 7: The line as it would be.
+Line 8: The line as it could be.
+
+To change line 8 into line 7 we need to change the 'c' in 'could' into a 'w'.
+The 'r' (replace) command was designed for this. Typing {rc} is the same as
+typing {xic^[} (i.e. delete the 'bad' character and insert the correct
+new character). Therefore, assuming that you have positioned the cursor on the
+'c' of 'could', the easiest way to change 'could' into 'would' is {rw}.
+
+If you would like to now change the 'would' into 'should', use the substitute
+command, 's': {ssh^[}. The difference between 'r' and 's' is that 'r'
+(replace) replaces the current character with another character, while 's'
+(substitute) substitutes the current character with a string, ended with an
+escape.
+
+The capital letter version of replace {R} replaces each character by a
+character one at a time until you type an escape, ^[. The 'S' command
+substitutes the whole line.
+
+Position your cursor at the beginning of line 9 and {z^M}.
+
+Line 9: Love is a many splendored thing.
+Line 10: Love is a most splendored thing.
+
+To change line 10 into line 9, position the cursor at the beginning of 'most',
+and type {Rmany^[}.
+
+You may have noticed that, when inserting text, a new line is formed by typing
+{^M}. When changing, replacing, or substituting text you can make a new line
+by typing {^M}. However, neither {x} nor {X} will remove ^M to make two lines
+into one line. To do this, position the cursor on the first of the two lines
+you wish to make into a single line and type {J} (uppercase J for 'Join').
+
+Section 24: {u} {U}
+
+Finally, before we review, let's look at the undo command. Position
+your cursor on line 11 below and {z^M}.
+
+Line 11: The quick brown fox jumped over the lazy hound dog.
+Line 12: the qwick black dog dumped over the laxy poune fox.
+
+Type the following set of commands, and observe carefully the effect of each
+of the commands:
+
+{/^Line 12:/^M} {ft} {rT} {fw} {ru} {w} {Rbrown fox^[} {w} {rj}
+{fx} {rz} {w} {Rhound dog^[}
+
+Line 12 now matches line 11. Now type {U} - capital 'U'. And line 12 now
+looks like it did before you typed in the command strings. Now type:
+
+{ft} {rT} {fw} {ru} {^M} {^M}
+
+and then type {u}: the cursor jumps back to the line containing the second
+change you made and 'undoes' it. That is, {U} 'undoes' all the changes on the
+line, and {u} 'undoes' only the last change. Type {u} several times and
+observe what happens: {u} can undo a previous {u}!
+
+Caveat: {U} only works as long as the cursor is still on the line. Move the
+cursor off the line and {U} will have no effect, except to possibly beep at
+you. However, {u} will undo the last change, no matter where it occurred.
+
+Section 25: review
+
+At this point, you have all the commands you need in order to make use of vi.
+The remainder of this tutorial will discuss variations on these commands as
+well as introduce new commands that make the job of editing more efficient.
+Here is a brief review of the basic commands we have covered. They are listed
+in the order of increasing complexity and/or decreasing necessity (to say that
+a command is less necessary is not to say that it is less useful!). These
+commands allow you to comfortably edit any text file. There are other
+commands that will make life easier but will require extra time to learn,
+obviously. You may want to consider setting this tutorial aside for several
+weeks and returning to it later after gaining experience with vi and getting
+comfortable with it. The convenience of some of the more exotic commands may
+then be apparent and worth the extra investment of time and effort
+required to master them.
+
+to get into the editor from Unix: {vi filename}
+to exit the editor
+ saving all changes {ZZ} or {:wq^M}
+ throwing away all changes {:q!^M}
+ when no changes have been made {:q^M}
+save a file without exiting the editor {:w^M}
+write the file into another file {:w filename^M}
+insert text
+ before the cursor {i ...text... ^[}
+ at the beginning of the line {I ...text... ^[}
+ after the cursor (append) {a ...text... ^[}
+ at the end of the line {A ...text... ^[}
+ after the current line {o ...text... ^[}
+ before the current line {O ...text... ^[}
+delete the character ...
+ under the cursor {x}
+ to the left of the cursor {X}
+delete n characters {nx} or {nX} (for n a number)
+make two lines into one line (Join) {J}
+find a string in the file ...
+ searching forward {/ ...string... /^M}
+ searching backwards {? ...string... ?^M}
+repeat the last search command {n}
+repeat the last search command in the
+ opposite direction {N}
+find the character c on this line ...
+ searching forward {fc}
+ searching backward {Fc}
+repeat the last 'find character' command {;}
+replace a character with character x {rx}
+substitute a single character with text {s ...text... ^[}
+substitute n characters with text {ns ...text... ^[}
+replace characters one-by-one with text {R ...text... ^[}
+undo all changes to the current line {U}
+undo the last single change {u}
+move forward in the file a "screenful" {^F}
+move back in the file a "screenful" {^B}
+move forward in the file one line {^M} or {+}
+move backward in the file one line {-}
+move to the beginning of the line {0}
+move to the end of the line {$}
+move forward one word {w}
+move forward one word, ignoring punctuation {W}
+move forward to the end of the next word {e}
+to the end of the word, ignoring punctuation{E}
+move backward one word {b}
+move back one word, ignoring punctuation {B}
+return to the last line modified {''}
+scroll a line onto the top of the screen {^Y}
+scroll a line onto the bottom of the screen {^E}
+move "up" in the file a half-screen {^U}
+move "down" in the file a half-screen {^D}
+move the cursor to the top screen line {H}
+move the cursor to the bottom screen line {L}
+move the cursor to the middle line {M}
+move LEFT one character position {h} or {^H}
+move RIGHT one character position {l} or { }
+move UP in the same column {k} or {^P}
+move DOWN in the same column {j} or {^N}
+mark the current position, name it x {mx}
+move to the line marked/named x {'x}
+move to the character position named x {`x}
+move to the beginning of the file {1G}
+move to the end of the file {G}
+move to line 23 in the file {23G}
+repaint the screen with the cursor line
+ at the top of the screen {z^M}
+ in the middle of the screen {z.}
+ at the bottom of the screen {z-}
+
+More information on vi can be found in the file vi.advanced, which you can
+peruse at your leisure. From UNIX, type {vi.tut advanced^M}.
diff --git a/contrib/nvi/docs/tutorial/vi.tut.csh b/contrib/nvi/docs/tutorial/vi.tut.csh
new file mode 100755
index 000000000000..01554bc4e5fd
--- /dev/null
+++ b/contrib/nvi/docs/tutorial/vi.tut.csh
@@ -0,0 +1,24 @@
+#!/bin/csh -f
+#
+# This makes the user's EXINIT variable set to the 'correct' things.
+# I don't know what will happen if they also have a .exrc file!
+#
+# XXX
+# Make sure that user is using a 24 line window!!!
+#
+if ($1 != "beginner" && $1 != "advanced") then
+ echo Usage: $0 beginner or $0 advanced
+ exit
+endif
+
+if ($?EXINIT) then
+ set oexinit="$EXINIT"
+ setenv EXINIT 'se ts=4 wm=8 sw=4'
+endif
+
+vi vi.{$1}
+
+onintr:
+ if ($?oexinit) then
+ setenv EXINIT "$oexinit"
+endif
diff --git a/contrib/nvi/ex/ex.awk b/contrib/nvi/ex/ex.awk
new file mode 100644
index 000000000000..3ee372e14aac
--- /dev/null
+++ b/contrib/nvi/ex/ex.awk
@@ -0,0 +1,6 @@
+# @(#)ex.awk 10.1 (Berkeley) 6/8/95
+
+/^\/\* C_[0-9A-Z_]* \*\/$/ {
+ printf("#define %s %d\n", $2, cnt++);
+ next;
+}
diff --git a/contrib/nvi/ex/ex.c b/contrib/nvi/ex/ex.c
new file mode 100644
index 000000000000..f92d8f7c4f9a
--- /dev/null
+++ b/contrib/nvi/ex/ex.c
@@ -0,0 +1,2370 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex.c 10.57 (Berkeley) 10/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+#if defined(DEBUG) && defined(COMLOG)
+static void ex_comlog __P((SCR *, EXCMD *));
+#endif
+static EXCMDLIST const *
+ ex_comm_search __P((char *, size_t));
+static int ex_discard __P((SCR *));
+static int ex_line __P((SCR *, EXCMD *, MARK *, int *, int *));
+static int ex_load __P((SCR *));
+static void ex_unknown __P((SCR *, char *, size_t));
+
+/*
+ * ex --
+ * Main ex loop.
+ *
+ * PUBLIC: int ex __P((SCR **));
+ */
+int
+ex(spp)
+ SCR **spp;
+{
+ EX_PRIVATE *exp;
+ GS *gp;
+ MSGS *mp;
+ SCR *sp;
+ TEXT *tp;
+ u_int32_t flags;
+
+ sp = *spp;
+ gp = sp->gp;
+ exp = EXP(sp);
+
+ /* Start the ex screen. */
+ if (ex_init(sp))
+ return (1);
+
+ /* Flush any saved messages. */
+ while ((mp = gp->msgq.lh_first) != NULL) {
+ gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
+ LIST_REMOVE(mp, q);
+ free(mp->buf);
+ free(mp);
+ }
+
+ /* If reading from a file, errors should have name and line info. */
+ if (F_ISSET(gp, G_SCRIPTED)) {
+ gp->excmd.if_lno = 1;
+ gp->excmd.if_name = "script";
+ }
+
+ /*
+ * !!!
+ * Initialize the text flags. The beautify edit option historically
+ * applied to ex command input read from a file. In addition, the
+ * first time a ^H was discarded from the input, there was a message,
+ * "^H discarded", that was displayed. We don't bother.
+ */
+ LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR);
+ for (;; ++gp->excmd.if_lno) {
+ /* Display status line and flush. */
+ if (F_ISSET(sp, SC_STATUS)) {
+ if (!F_ISSET(sp, SC_EX_SILENT))
+ msgq_status(sp, sp->lno, 0);
+ F_CLR(sp, SC_STATUS);
+ }
+ (void)ex_fflush(sp);
+
+ /* Set the flags the user can reset. */
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+ if (O_ISSET(sp, O_PROMPT))
+ LF_SET(TXT_PROMPT);
+
+ /* Clear any current interrupts, and get a command. */
+ CLR_INTERRUPT(sp);
+ if (ex_txt(sp, &sp->tiq, ':', flags))
+ return (1);
+ if (INTERRUPTED(sp)) {
+ (void)ex_puts(sp, "\n");
+ (void)ex_fflush(sp);
+ continue;
+ }
+
+ /* Initialize the command structure. */
+ CLEAR_EX_PARSER(&gp->excmd);
+
+ /*
+ * If the user entered a single carriage return, send
+ * ex_cmd() a separator -- it discards single newlines.
+ */
+ tp = sp->tiq.cqh_first;
+ if (tp->len == 0) {
+ gp->excmd.cp = " "; /* __TK__ why not |? */
+ gp->excmd.clen = 1;
+ } else {
+ gp->excmd.cp = tp->lb;
+ gp->excmd.clen = tp->len;
+ }
+ F_INIT(&gp->excmd, E_NRSEP);
+
+ if (ex_cmd(sp) && F_ISSET(gp, G_SCRIPTED))
+ return (1);
+
+ if (INTERRUPTED(sp)) {
+ CLR_INTERRUPT(sp);
+ msgq(sp, M_ERR, "170|Interrupted");
+ }
+
+ /*
+ * If the last command caused a restart, or switched screens
+ * or into vi, return.
+ */
+ if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_SSWITCH | SC_VI)) {
+ *spp = sp;
+ break;
+ }
+
+ /* If the last command switched files, we don't care. */
+ F_CLR(sp, SC_FSWITCH);
+
+ /*
+ * If we're exiting this screen, move to the next one. By
+ * definition, this means returning into vi, so return to the
+ * main editor loop. The ordering is careful, don't discard
+ * the contents of sp until the end.
+ */
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
+ if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
+ return (1);
+ *spp = screen_next(sp);
+ return (screen_end(sp));
+ }
+ }
+ return (0);
+}
+
+/*
+ * ex_cmd --
+ * The guts of the ex parser: parse and execute a string containing
+ * ex commands.
+ *
+ * !!!
+ * This code MODIFIES the string that gets passed in, to delete quoting
+ * characters, etc. The string cannot be readonly/text space, nor should
+ * you expect to use it again after ex_cmd() returns.
+ *
+ * !!!
+ * 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
+ *
+ * or: vi
+ * :set|file|append|set|file
+ *
+ * For extra credit, try them in a startup .exrc file.
+ *
+ * PUBLIC: int ex_cmd __P((SCR *));
+ */
+int
+ex_cmd(sp)
+ SCR *sp;
+{
+ enum nresult nret;
+ EX_PRIVATE *exp;
+ EXCMD *ecp;
+ GS *gp;
+ MARK cur;
+ recno_t lno;
+ size_t arg1_len, discard, len;
+ u_int32_t flags;
+ long ltmp;
+ int at_found, gv_found;
+ int ch, cnt, delim, isaddr, namelen;
+ int newscreen, notempty, tmp, vi_address;
+ char *arg1, *p, *s, *t;
+
+ gp = sp->gp;
+ exp = EXP(sp);
+
+ /*
+ * We always start running the command on the top of the stack.
+ * This means that *everything* must be resolved when we leave
+ * this function for any reason.
+ */
+loop: ecp = gp->ecq.lh_first;
+
+ /* If we're reading a command from a file, set up error information. */
+ if (ecp->if_name != NULL) {
+ gp->if_lno = ecp->if_lno;
+ gp->if_name = ecp->if_name;
+ }
+
+ /*
+ * If a move to the end of the file is scheduled for this command,
+ * do it now.
+ */
+ if (F_ISSET(ecp, E_MOVETOEND)) {
+ if (db_last(sp, &sp->lno))
+ goto rfail;
+ sp->cno = 0;
+ F_CLR(ecp, E_MOVETOEND);
+ }
+
+ /* If we found a newline, increment the count now. */
+ if (F_ISSET(ecp, E_NEWLINE)) {
+ ++gp->if_lno;
+ ++ecp->if_lno;
+ F_CLR(ecp, E_NEWLINE);
+ }
+
+ /* (Re)initialize the EXCMD structure, preserving some flags. */
+ CLEAR_EX_CMD(ecp);
+
+ /* Initialize the argument structures. */
+ if (argv_init(sp, ecp))
+ goto err;
+
+ /* Initialize +cmd, saved command information. */
+ arg1 = NULL;
+ ecp->save_cmdlen = 0;
+
+ /* Skip <blank>s, empty lines. */
+ for (notempty = 0; ecp->clen > 0; ++ecp->cp, --ecp->clen)
+ if ((ch = *ecp->cp) == '\n') {
+ ++gp->if_lno;
+ ++ecp->if_lno;
+ } else if (isblank(ch))
+ notempty = 1;
+ else
+ break;
+
+ /*
+ * !!!
+ * 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 (ecp->clen != 0 && ch == ':') {
+ notempty = 1;
+ while (--ecp->clen > 0 && (ch = *++ecp->cp) == ':');
+ }
+
+ /*
+ * Command lines that start with a double-quote are comments.
+ *
+ * !!!
+ * Historically, there was no escape or delimiter for a comment, e.g.
+ * :"foo|set was a single comment and nothing was output. Since nvi
+ * permits users to escape <newline> characters into command lines, we
+ * have to check for that case.
+ */
+ if (ecp->clen != 0 && ch == '"') {
+ while (--ecp->clen > 0 && *++ecp->cp != '\n');
+ if (*ecp->cp == '\n') {
+ F_SET(ecp, E_NEWLINE);
+ ++ecp->cp;
+ --ecp->clen;
+ }
+ goto loop;
+ }
+
+ /* Skip whitespace. */
+ for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
+ ch = *ecp->cp;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * The last point at which an empty line can mean do nothing.
+ *
+ * !!!
+ * Historically, in ex mode, lines containing only <blank> characters
+ * were the same as a single <carriage-return>, i.e. a default command.
+ * In vi mode, they were ignored. In .exrc files this was a serious
+ * annoyance, as vi kept trying to treat them as print commands. We
+ * ignore backward compatibility in this case, discarding lines that
+ * contain only <blank> characters from .exrc files.
+ *
+ * !!!
+ * This is where you end up when you're done a command, i.e. clen has
+ * gone to zero. Continue if there are more commands to run.
+ */
+ if (ecp->clen == 0 &&
+ (!notempty || F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_BLIGNORE))) {
+ if (ex_load(sp))
+ goto rfail;
+ ecp = gp->ecq.lh_first;
+ if (ecp->clen == 0)
+ goto rsuccess;
+ goto loop;
+ }
+
+ /*
+ * Check to see if this is a command for which we may want to move
+ * the cursor back up to the previous line. (The command :1<CR>
+ * wants a <newline> separator, but the command :<CR> wants to erase
+ * the command line.) If the line is empty except for <blank>s,
+ * <carriage-return> or <eof>, we'll probably want to move up. I
+ * don't think there's any way to get <blank> characters *after* the
+ * command character, but this is the ex parser, and I've been wrong
+ * before.
+ */
+ if (F_ISSET(ecp, E_NRSEP) &&
+ ecp->clen != 0 && (ecp->clen != 1 || ecp->cp[0] != '\004'))
+ F_CLR(ecp, E_NRSEP);
+
+ /* Parse command addresses. */
+ if (ex_range(sp, ecp, &tmp))
+ goto rfail;
+ if (tmp)
+ goto err;
+
+ /*
+ * Skip <blank>s and any more colons (the command :3,5:print
+ * worked, historically).
+ */
+ for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
+ ch = *ecp->cp;
+ if (!isblank(ch) && 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.
+ *
+ * !!!
+ * Historically, lines with multiple adjacent (or <blank> separated)
+ * command separators were very strange. For example, the command
+ * |||<carriage-return>, when the cursor was on line 1, displayed
+ * lines 2, 3 and 5 of the file. In addition, the command " | "
+ * would only display the line after the next line, instead of the
+ * next two lines. No ideas why. It worked reasonably when executed
+ * from vi mode, and displayed lines 2, 3, and 4, so we do a default
+ * command for each separator.
+ */
+#define SINGLE_CHAR_COMMANDS "\004!#&*<=>@~"
+ newscreen = 0;
+ if (ecp->clen != 0 && ecp->cp[0] != '|' && ecp->cp[0] != '\n') {
+ if (strchr(SINGLE_CHAR_COMMANDS, *ecp->cp)) {
+ p = ecp->cp;
+ ++ecp->cp;
+ --ecp->clen;
+ namelen = 1;
+ } else {
+ for (p = ecp->cp;
+ ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (!isalpha(*ecp->cp))
+ break;
+ if ((namelen = ecp->cp - p) == 0) {
+ msgq(sp, M_ERR, "080|Unknown command name");
+ goto err;
+ }
+ }
+
+ /*
+ * !!!
+ * Historic vi permitted flags to immediately follow any
+ * subset of the 'delete' command, but then did not permit
+ * further arguments (flag, buffer, count). Make it work.
+ * Permit further arguments for the few shreds of dignity
+ * it offers.
+ *
+ * Adding commands that start with 'd', and match "delete"
+ * up to a l, p, +, - or # character can break this code.
+ *
+ * !!!
+ * Capital letters beginning the command names ex, edit,
+ * next, previous, tag and visual (in vi mode) indicate the
+ * command should happen in a new screen.
+ */
+ switch (p[0]) {
+ case 'd':
+ for (s = p,
+ t = cmds[C_DELETE].name; *s == *t; ++s, ++t);
+ if (s[0] == 'l' || s[0] == 'p' || s[0] == '+' ||
+ s[0] == '-' || s[0] == '^' || s[0] == '#') {
+ len = (ecp->cp - p) - (s - p);
+ ecp->cp -= len;
+ ecp->clen += len;
+ ecp->rcmd = cmds[C_DELETE];
+ ecp->rcmd.syntax = "1bca1";
+ ecp->cmd = &ecp->rcmd;
+ goto skip_srch;
+ }
+ break;
+ case 'E': case 'F': case 'N': case 'P': case 'T': case 'V':
+ newscreen = 1;
+ p[0] = tolower(p[0]);
+ break;
+ }
+
+ /*
+ * 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 any flag to follow the s command, e.g.
+ * "s/e/E/|s|sgc3p" was legal. Make the command "sgc" work.
+ * Since the following characters all have to be flags, i.e.
+ * alphabetics, we can let the s command routine return errors
+ * if it was some illegal command string. This code will break
+ * if an "sg" or similar command is ever added. The substitute
+ * code doesn't care if it's a "cgr" flag or a "#lp" flag that
+ * follows the 's', but we limit the choices here to "cgr" so
+ * that we get unknown command messages for wrong combinations.
+ */
+ if ((ecp->cmd = ex_comm_search(p, namelen)) == NULL)
+ switch (p[0]) {
+ case 'k':
+ if (namelen == 2) {
+ ecp->cp -= namelen - 1;
+ ecp->clen += namelen - 1;
+ ecp->cmd = &cmds[C_K];
+ break;
+ }
+ goto unknown;
+ case 's':
+ for (s = p + 1, cnt = namelen; --cnt; ++s)
+ if (s[0] != 'c' &&
+ s[0] != 'g' && s[0] != 'r')
+ break;
+ if (cnt == 0) {
+ ecp->cp -= namelen - 1;
+ ecp->clen += namelen - 1;
+ ecp->rcmd = cmds[C_SUBSTITUTE];
+ ecp->rcmd.fn = ex_subagain;
+ ecp->cmd = &ecp->rcmd;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+unknown: if (newscreen)
+ p[0] = toupper(p[0]);
+ ex_unknown(sp, p, namelen);
+ goto err;
+ }
+
+ /*
+ * The visual command has a different syntax when called
+ * from ex than when called from a vi colon command. FMH.
+ * Make the change now, before we test for the newscreen
+ * semantic, so that we're testing the right one.
+ */
+skip_srch: if (ecp->cmd == &cmds[C_VISUAL_EX] && F_ISSET(sp, SC_VI))
+ ecp->cmd = &cmds[C_VISUAL_VI];
+
+ /*
+ * !!!
+ * Historic vi permitted a capital 'P' at the beginning of
+ * any command that started with 'p'. Probably wanted the
+ * P[rint] command for backward compatibility, and the code
+ * just made Preserve and Put work by accident. Nvi uses
+ * Previous to mean previous-in-a-new-screen, so be careful.
+ */
+ if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN) &&
+ (ecp->cmd == &cmds[C_PRINT] ||
+ ecp->cmd == &cmds[C_PRESERVE]))
+ newscreen = 0;
+
+ /* Test for a newscreen associated with this command. */
+ if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN))
+ goto unknown;
+
+ /* Secure means no shell access. */
+ if (F_ISSET(ecp->cmd, E_SECURE) && O_ISSET(sp, O_SECURE)) {
+ ex_emsg(sp, ecp->cmd->name, EXM_SECURE);
+ goto err;
+ }
+
+ /*
+ * Multiple < and > characters; another "feature". Note,
+ * The string passed to the underlying function may not be
+ * nul terminated in this case.
+ */
+ if ((ecp->cmd == &cmds[C_SHIFTL] && *p == '<') ||
+ (ecp->cmd == &cmds[C_SHIFTR] && *p == '>')) {
+ for (ch = *p;
+ ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (*ecp->cp != ch)
+ break;
+ if (argv_exp0(sp, ecp, p, ecp->cp - p))
+ goto err;
+ }
+
+ /* Set the format style flags for the next command. */
+ if (ecp->cmd == &cmds[C_HASH])
+ exp->fdef = E_C_HASH;
+ else if (ecp->cmd == &cmds[C_LIST])
+ exp->fdef = E_C_LIST;
+ else if (ecp->cmd == &cmds[C_PRINT])
+ exp->fdef = E_C_PRINT;
+ F_CLR(ecp, E_USELASTCMD);
+ } else {
+ /* Print is the default command. */
+ ecp->cmd = &cmds[C_PRINT];
+
+ /* Set the saved format flags. */
+ F_SET(ecp, exp->fdef);
+
+ /*
+ * !!!
+ * If no address was specified, and it's not a global command,
+ * we up the address by one. (I have no idea why globals are
+ * exempted, but it's (ahem) historic practice.)
+ */
+ if (ecp->addrcnt == 0 && !F_ISSET(sp, SC_EX_GLOBAL)) {
+ ecp->addrcnt = 1;
+ ecp->addr1.lno = sp->lno + 1;
+ ecp->addr1.cno = sp->cno;
+ }
+
+ F_SET(ecp, E_USELASTCMD);
+ }
+
+ /*
+ * !!!
+ * Historically, the number option applied to both ex and vi. One
+ * strangeness was that ex didn't switch display formats until a
+ * command was entered, e.g. <CR>'s after the set didn't change to
+ * the new format, but :1p would.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ F_SET(ecp, E_OPTNUM);
+ FL_SET(ecp->iflags, E_C_HASH);
+ } else
+ F_CLR(ecp, E_OPTNUM);
+
+ /* Check for ex mode legality. */
+ if (F_ISSET(sp, SC_EX) && (F_ISSET(ecp->cmd, E_VIONLY) || newscreen)) {
+ msgq(sp, M_ERR,
+ "082|%s: command not available in ex mode", ecp->cmd->name);
+ goto err;
+ }
+
+ /* Add standard command flags. */
+ F_SET(ecp, ecp->cmd->flags);
+ if (!newscreen)
+ F_CLR(ecp, E_NEWSCREEN);
+
+ /*
+ * There are three normal termination cases for an ex command. They
+ * are the end of the string (ecp->clen), or unescaped (by <literal
+ * next> characters) <newline> or '|' characters. As we're now past
+ * possible addresses, we can determine how long the command is, so we
+ * don't have to look for all the possible terminations. Naturally,
+ * there are some exciting special cases:
+ *
+ * 1: The bang, global, v and the filter versions of the read and
+ * write commands are delimited by <newline>s (they can contain
+ * shell pipes).
+ * 2: The ex, edit, next and visual in vi mode commands all take ex
+ * commands as their first arguments.
+ * 3: The s 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,
+ * next, vi visual, and s commands didn't delimit the command. And,
+ * in the filter cases for read and write, and the bang, global and v
+ * commands, they did not delimit the command at all.
+ *
+ * For example, the following commands were legal:
+ *
+ * :edit +25|s/abc/ABC/ file.c
+ * :s/|/PIPE/
+ * :read !spell % | columnate
+ * :global/pattern/p|l
+ *
+ * It's not quite as simple as it sounds, however. The command:
+ *
+ * :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 of syntax) delimited the RE's
+ * based on its delimiter and not anything so irretrievably vulgar as a
+ * command syntax.
+ *
+ * Anyhow, the following code makes this all work. First, for the
+ * special cases we move past their special argument(s). Then, we
+ * do normal command processing on whatever is left. Barf-O-Rama.
+ */
+ discard = 0; /* Characters discarded from the command. */
+ arg1_len = 0;
+ ecp->save_cmd = ecp->cp;
+ if (ecp->cmd == &cmds[C_EDIT] || ecp->cmd == &cmds[C_EX] ||
+ ecp->cmd == &cmds[C_NEXT] || ecp->cmd == &cmds[C_VISUAL_VI]) {
+ /*
+ * Move to the next non-whitespace character. A '!'
+ * immediately following the command is eaten as a
+ * force flag.
+ */
+ if (ecp->clen > 0 && *ecp->cp == '!') {
+ ++ecp->cp;
+ --ecp->clen;
+ FL_SET(ecp->iflags, E_C_FORCE);
+
+ /* Reset, don't reparse. */
+ ecp->save_cmd = ecp->cp;
+ }
+ for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (!isblank(*ecp->cp))
+ 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. The escaping
+ * characters are stripped as no longer useful.
+ */
+ if (ecp->clen > 0 && *ecp->cp == '+') {
+ ++ecp->cp;
+ --ecp->clen;
+ for (arg1 = p = ecp->cp;
+ ecp->clen > 0; --ecp->clen, ++ecp->cp) {
+ ch = *ecp->cp;
+ if (IS_ESCAPE(sp, ecp, ch) &&
+ ecp->clen > 1) {
+ ++discard;
+ --ecp->clen;
+ ch = *++ecp->cp;
+ } else if (isblank(ch))
+ break;
+ *p++ = ch;
+ }
+ arg1_len = ecp->cp - arg1;
+
+ /* Reset, so the first argument isn't reparsed. */
+ ecp->save_cmd = ecp->cp;
+ }
+ } else if (ecp->cmd == &cmds[C_BANG] ||
+ ecp->cmd == &cmds[C_GLOBAL] || ecp->cmd == &cmds[C_V]) {
+ /*
+ * QUOTING NOTE:
+ *
+ * We use backslashes to escape <newline> characters, although
+ * this wasn't historic practice for the bang command. It was
+ * for the global and v commands, and it's common usage when
+ * doing text insert during the command. Escaping characters
+ * are stripped as no longer useful.
+ */
+ for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
+ ch = *ecp->cp;
+ if (ch == '\\' && ecp->clen > 1 && ecp->cp[1] == '\n') {
+ ++discard;
+ --ecp->clen;
+ ch = *++ecp->cp;
+
+ ++gp->if_lno;
+ ++ecp->if_lno;
+ } else if (ch == '\n')
+ break;
+ *p++ = ch;
+ }
+ } else if (ecp->cmd == &cmds[C_READ] || ecp->cmd == &cmds[C_WRITE]) {
+ /*
+ * For write commands, if the next character is a <blank>, and
+ * the next non-blank character is a '!', it's a filter command
+ * and we want to eat everything up to the <newline>. For read
+ * commands, if the next non-blank character is a '!', it's a
+ * filter command and we want to eat everything up to the next
+ * <newline>. Otherwise, we're done.
+ */
+ for (tmp = 0; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
+ ch = *ecp->cp;
+ if (isblank(ch))
+ tmp = 1;
+ else
+ break;
+ }
+ if (ecp->clen > 0 && ch == '!' &&
+ (ecp->cmd == &cmds[C_READ] || tmp))
+ for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (ecp->cp[0] == '\n')
+ break;
+ } else if (ecp->cmd == &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 s command.
+ */
+ for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (!isblank(ecp->cp[0]))
+ break;
+
+ if (isalnum(ecp->cp[0]) || ecp->cp[0] == '|') {
+ ecp->rcmd = cmds[C_SUBSTITUTE];
+ ecp->rcmd.fn = ex_subagain;
+ ecp->cmd = &ecp->rcmd;
+ } else if (ecp->clen > 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 = *ecp->cp;
+ ++ecp->cp;
+ --ecp->clen;
+ for (cnt = 2; ecp->clen > 0 &&
+ cnt != 0; --ecp->clen, ++ecp->cp)
+ if (ecp->cp[0] == '\\' &&
+ ecp->clen > 1) {
+ ++ecp->cp;
+ --ecp->clen;
+ } else if (ecp->cp[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 they're
+ * no longer useful.
+ */
+ vi_address = ecp->clen != 0 && ecp->cp[0] != '\n';
+ for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
+ ch = ecp->cp[0];
+ if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
+ tmp = ecp->cp[1];
+ if (tmp == '\n' || tmp == '|') {
+ if (tmp == '\n') {
+ ++gp->if_lno;
+ ++ecp->if_lno;
+ }
+ ++discard;
+ --ecp->clen;
+ ++ecp->cp;
+ ch = tmp;
+ }
+ } else if (ch == '\n' || ch == '|') {
+ if (ch == '\n')
+ F_SET(ecp, E_NEWLINE);
+ --ecp->clen;
+ break;
+ }
+ *p++ = ch;
+ }
+
+ /*
+ * Save off the next command information, go back to the
+ * original start of the command.
+ */
+ p = ecp->cp + 1;
+ ecp->cp = ecp->save_cmd;
+ ecp->save_cmd = p;
+ ecp->save_cmdlen = ecp->clen;
+ ecp->clen = ((ecp->save_cmd - ecp->cp) - 1) - discard;
+
+ /*
+ * QUOTING NOTE:
+ *
+ * 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 (ecp->cmd == &cmds[C_SET])
+ for (p = ecp->cp, len = ecp->clen; len > 0; --len, ++p)
+ if (*p == '\\')
+ *p = CH_LITERAL;
+
+ /*
+ * 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_ADDR_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 or if we just used
+ * the current cursor.
+ */
+ switch (F_ISSET(ecp, E_ADDR1 | E_ADDR2 | E_ADDR2_ALL | E_ADDR2_NONE)) {
+ case E_ADDR1: /* One address: */
+ switch (ecp->addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ ecp->addrcnt = 1;
+ F_SET(ecp, E_ADDR_DEF);
+ if (F_ISSET(ecp, E_ADDR_ZERODEF)) {
+ if (db_last(sp, &lno))
+ goto err;
+ if (lno == 0) {
+ ecp->addr1.lno = 0;
+ F_SET(ecp, E_ADDR_ZERO);
+ } else
+ ecp->addr1.lno = sp->lno;
+ } else
+ ecp->addr1.lno = sp->lno;
+ ecp->addr1.cno = sp->cno;
+ break;
+ case 1:
+ break;
+ case 2: /* Lose the first address. */
+ ecp->addrcnt = 1;
+ ecp->addr1 = ecp->addr2;
+ }
+ break;
+ case E_ADDR2_NONE: /* Zero/two addresses: */
+ if (ecp->addrcnt == 0) /* Default to nothing. */
+ break;
+ goto two_addr;
+ case E_ADDR2_ALL: /* Zero/two addresses: */
+ if (ecp->addrcnt == 0) { /* Default entire/empty file. */
+ F_SET(ecp, E_ADDR_DEF);
+ ecp->addrcnt = 2;
+ if (sp->ep == NULL)
+ ecp->addr2.lno = 0;
+ else if (db_last(sp, &ecp->addr2.lno))
+ goto err;
+ if (F_ISSET(ecp, E_ADDR_ZERODEF) &&
+ ecp->addr2.lno == 0) {
+ ecp->addr1.lno = 0;
+ F_SET(ecp, E_ADDR_ZERO);
+ } else
+ ecp->addr1.lno = 1;
+ ecp->addr1.cno = ecp->addr2.cno = 0;
+ F_SET(ecp, E_ADDR2_ALL);
+ break;
+ }
+ /* FALLTHROUGH */
+ case E_ADDR2: /* Two addresses: */
+two_addr: switch (ecp->addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ ecp->addrcnt = 2;
+ F_SET(ecp, E_ADDR_DEF);
+ if (sp->lno == 1 &&
+ F_ISSET(ecp, E_ADDR_ZERODEF)) {
+ if (db_last(sp, &lno))
+ goto err;
+ if (lno == 0) {
+ ecp->addr1.lno = ecp->addr2.lno = 0;
+ F_SET(ecp, E_ADDR_ZERO);
+ } else
+ ecp->addr1.lno =
+ ecp->addr2.lno = sp->lno;
+ } else
+ ecp->addr1.lno = ecp->addr2.lno = sp->lno;
+ ecp->addr1.cno = ecp->addr2.cno = sp->cno;
+ break;
+ case 1: /* Default to first address. */
+ ecp->addrcnt = 2;
+ ecp->addr2 = ecp->addr1;
+ break;
+ case 2:
+ break;
+ }
+ break;
+ default:
+ if (ecp->addrcnt) /* Error. */
+ goto usage;
+ }
+
+ /*
+ * !!!
+ * The ^D scroll command historically scrolled the value of the scroll
+ * option or to EOF. It was an error if the cursor was already at EOF.
+ * (Leading addresses were permitted, but were then ignored.)
+ */
+ if (ecp->cmd == &cmds[C_SCROLL]) {
+ ecp->addrcnt = 2;
+ ecp->addr1.lno = sp->lno + 1;
+ ecp->addr2.lno = sp->lno + O_VAL(sp, O_SCROLL);
+ ecp->addr1.cno = ecp->addr2.cno = sp->cno;
+ if (db_last(sp, &lno))
+ goto err;
+ if (lno != 0 && lno > sp->lno && ecp->addr2.lno > lno)
+ ecp->addr2.lno = lno;
+ }
+
+ ecp->flagoff = 0;
+ for (p = ecp->cmd->syntax; *p != '\0'; ++p) {
+ /*
+ * The force flag is sensitive to leading whitespace, i.e.
+ * "next !" is different from "next!". Handle it before
+ * skipping leading <blank>s.
+ */
+ if (*p == '!') {
+ if (ecp->clen > 0 && *ecp->cp == '!') {
+ ++ecp->cp;
+ --ecp->clen;
+ FL_SET(ecp->iflags, E_C_FORCE);
+ }
+ continue;
+ }
+
+ /* Skip leading <blank>s. */
+ for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
+ if (!isblank(*ecp->cp))
+ break;
+ if (ecp->clen == 0)
+ break;
+
+ switch (*p) {
+ 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 (; ecp->clen; --ecp->clen, ++ecp->cp)
+ switch (*ecp->cp) {
+ case '+':
+ ++ecp->flagoff;
+ break;
+ case '-':
+ case '^':
+ --ecp->flagoff;
+ break;
+ case '#':
+ F_CLR(ecp, E_OPTNUM);
+ FL_SET(ecp->iflags, E_C_HASH);
+ exp->fdef |= E_C_HASH;
+ break;
+ case 'l':
+ FL_SET(ecp->iflags, E_C_LIST);
+ exp->fdef |= E_C_LIST;
+ break;
+ case 'p':
+ FL_SET(ecp->iflags, E_C_PRINT);
+ exp->fdef |= E_C_PRINT;
+ break;
+ default:
+ goto end_case1;
+ }
+end_case1: break;
+ case '2': /* -, ., +, ^ */
+ case '3': /* -, ., +, ^, = */
+ for (; ecp->clen; --ecp->clen, ++ecp->cp)
+ switch (*ecp->cp) {
+ case '-':
+ FL_SET(ecp->iflags, E_C_DASH);
+ break;
+ case '.':
+ FL_SET(ecp->iflags, E_C_DOT);
+ break;
+ case '+':
+ FL_SET(ecp->iflags, E_C_PLUS);
+ break;
+ case '^':
+ FL_SET(ecp->iflags, E_C_CARAT);
+ break;
+ case '=':
+ if (*p == '3') {
+ FL_SET(ecp->iflags, E_C_EQUAL);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ goto end_case23;
+ }
+end_case23: break;
+ case 'b': /* buffer */
+ /*
+ * !!!
+ * Historically, "d #" was a delete with a flag, not a
+ * delete into the '#' buffer. If the current command
+ * permits a flag, don't use one as a buffer. However,
+ * the 'l' and 'p' flags were legal buffer names in the
+ * historic ex, and were used as buffers, not flags.
+ */
+ if ((ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
+ ecp->cp[0] == '^' || ecp->cp[0] == '#') &&
+ strchr(p, '1') != NULL)
+ break;
+ /*
+ * !!!
+ * 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(ecp->cp[0])) {
+ ecp->buffer = *ecp->cp;
+ ++ecp->cp;
+ --ecp->clen;
+ FL_SET(ecp->iflags, E_C_BUFFER);
+ }
+ break;
+ case 'c': /* count [01+a] */
+ ++p;
+ /* Validate any signed value. */
+ if (!isdigit(*ecp->cp) && (*p != '+' ||
+ (*ecp->cp != '+' && *ecp->cp != '-')))
+ break;
+ /* If a signed value, set appropriate flags. */
+ if (*ecp->cp == '-')
+ FL_SET(ecp->iflags, E_C_COUNT_NEG);
+ else if (*ecp->cp == '+')
+ FL_SET(ecp->iflags, E_C_COUNT_POS);
+ if ((nret =
+ nget_slong(&ltmp, ecp->cp, &t, 10)) != NUM_OK) {
+ ex_badaddr(sp, NULL, A_NOTSET, nret);
+ goto err;
+ }
+ if (ltmp == 0 && *p != '0') {
+ msgq(sp, M_ERR, "083|Count may not be zero");
+ goto err;
+ }
+ ecp->clen -= (t - ecp->cp);
+ ecp->cp = t;
+
+ /*
+ * Counts 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') {
+ ecp->addr1 = ecp->addr2;
+ ecp->addr2.lno = ecp->addr1.lno + ltmp - 1;
+ } else
+ ecp->count = ltmp;
+ FL_SET(ecp->iflags, E_C_COUNT);
+ break;
+ case 'f': /* file */
+ if (argv_exp2(sp, ecp, ecp->cp, ecp->clen))
+ goto err;
+ goto arg_cnt_chk;
+ case 'l': /* line */
+ /*
+ * Get a line specification.
+ *
+ * If the line was a search expression, we may have
+ * changed state during the call, and we're now
+ * searching the file. Push ourselves onto the state
+ * stack.
+ */
+ if (ex_line(sp, ecp, &cur, &isaddr, &tmp))
+ goto rfail;
+ if (tmp)
+ goto err;
+
+ /* Line specifications are always required. */
+ if (!isaddr) {
+ msgq_str(sp, M_ERR, ecp->cp,
+ "084|%s: bad line specification");
+ goto err;
+ }
+ /*
+ * The target line should exist for these commands,
+ * but 0 is legal for them as well.
+ */
+ if (cur.lno != 0 && !db_exist(sp, cur.lno)) {
+ ex_badaddr(sp, NULL, A_EOF, NUM_OK);
+ goto err;
+ }
+ ecp->lineno = cur.lno;
+ break;
+ case 'S': /* string, file exp. */
+ if (ecp->clen != 0) {
+ if (argv_exp1(sp, ecp, ecp->cp,
+ ecp->clen, ecp->cmd == &cmds[C_BANG]))
+ goto err;
+ goto addr_verify;
+ }
+ /* FALLTHROUGH */
+ case 's': /* string */
+ if (argv_exp0(sp, ecp, ecp->cp, ecp->clen))
+ goto err;
+ goto addr_verify;
+ 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 = ecp->cp;
+ ecp->clen > 0; --ecp->clen, ++ecp->cp) {
+ ch = *ecp->cp;
+ if (IS_ESCAPE(sp,
+ ecp, ch) && ecp->clen > 1) {
+ --ecp->clen;
+ *p++ = *++ecp->cp;
+ } else if (isblank(ch)) {
+ ++ecp->cp;
+ --ecp->clen;
+ break;
+ } else
+ *p++ = ch;
+ }
+ if (argv_exp0(sp, ecp, t, p - t))
+ goto err;
+
+ /* Delete intervening whitespace. */
+ for (; ecp->clen > 0;
+ --ecp->clen, ++ecp->cp) {
+ ch = *ecp->cp;
+ if (!isblank(ch))
+ break;
+ }
+ if (ecp->clen == 0)
+ goto usage;
+
+ /* Followed by the string. */
+ for (p = t = ecp->cp; ecp->clen > 0;
+ --ecp->clen, ++ecp->cp, ++p) {
+ ch = *ecp->cp;
+ if (IS_ESCAPE(sp,
+ ecp, ch) && ecp->clen > 1) {
+ --ecp->clen;
+ *p = *++ecp->cp;
+ } else
+ *p = ch;
+ }
+ if (argv_exp0(sp, ecp, t, p - t))
+ goto err;
+ goto addr_verify;
+ case 'w': /* word */
+ if (argv_exp3(sp, ecp, ecp->cp, ecp->clen))
+ goto err;
+arg_cnt_chk: if (*++p != 'N') { /* N */
+ /*
+ * If a number is specified, must either be
+ * 0 or that number, if optional, and that
+ * number, if required.
+ */
+ tmp = *p - '0';
+ if ((*++p != 'o' || exp->argsoff != 0) &&
+ exp->argsoff != tmp)
+ goto usage;
+ }
+ goto addr_verify;
+ default:
+ msgq(sp, M_ERR,
+ "085|Internal syntax table error (%s: %s)",
+ ecp->cmd->name, KEY_NAME(sp, *p));
+ }
+ }
+
+ /* Skip trailing whitespace. */
+ for (; ecp->clen > 0; --ecp->clen) {
+ ch = *ecp->cp++;
+ 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 (ecp->clen != 0 || strpbrk(p, "lr")) {
+usage: msgq(sp, M_ERR, "086|Usage: %s", ecp->cmd->usage);
+ goto err;
+ }
+
+ /*
+ * Verify that the addresses are legal. Check the addresses here,
+ * because this is a place where all ex addresses pass through.
+ * (They don't all pass through ex_line(), for instance.) We're
+ * assuming that any non-existent line doesn't exist because it's
+ * past the end-of-file. That's a pretty good guess.
+ *
+ * If it's a "default vi command", an address of zero is okay.
+ */
+addr_verify:
+ switch (ecp->addrcnt) {
+ case 2:
+ /*
+ * 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 (ecp->addr2.lno == 0) {
+ if (!F_ISSET(ecp, E_ADDR_ZERO) &&
+ (F_ISSET(sp, SC_EX) ||
+ !F_ISSET(ecp, E_USELASTCMD))) {
+ ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
+ goto err;
+ }
+ } else if (!db_exist(sp, ecp->addr2.lno))
+ if (FL_ISSET(ecp->iflags, E_C_COUNT)) {
+ if (db_last(sp, &lno))
+ goto err;
+ ecp->addr2.lno = lno;
+ } else {
+ ex_badaddr(sp, NULL, A_EOF, NUM_OK);
+ goto err;
+ }
+ /* FALLTHROUGH */
+ case 1:
+ if (ecp->addr1.lno == 0) {
+ if (!F_ISSET(ecp, E_ADDR_ZERO) &&
+ (F_ISSET(sp, SC_EX) ||
+ !F_ISSET(ecp, E_USELASTCMD))) {
+ ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
+ goto err;
+ }
+ } else if (!db_exist(sp, ecp->addr1.lno)) {
+ ex_badaddr(sp, NULL, A_EOF, NUM_OK);
+ goto err;
+ }
+ break;
+ }
+
+ /*
+ * If doing a default command and there's nothing left on the line,
+ * vi just moves to the line. For example, ":3" and ":'a,'b" just
+ * move to line 3 and line 'b, respectively, but ":3|" prints line 3.
+ *
+ * !!!
+ * In addition, IF THE LINE CHANGES, move to the first nonblank of
+ * the line.
+ *
+ * !!!
+ * This is done before the absolute mark gets set; historically,
+ * "/a/,/b/" did NOT set vi's absolute mark, but "/a/,/b/d" did.
+ */
+ if ((F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_NOPRDEF)) &&
+ F_ISSET(ecp, E_USELASTCMD) && vi_address == 0) {
+ switch (ecp->addrcnt) {
+ case 2:
+ if (sp->lno !=
+ (ecp->addr2.lno ? ecp->addr2.lno : 1)) {
+ sp->lno =
+ ecp->addr2.lno ? ecp->addr2.lno : 1;
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ }
+ break;
+ case 1:
+ if (sp->lno !=
+ (ecp->addr1.lno ? ecp->addr1.lno : 1)) {
+ sp->lno =
+ ecp->addr1.lno ? ecp->addr1.lno : 1;
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ }
+ break;
+ }
+ ecp->cp = ecp->save_cmd;
+ ecp->clen = ecp->save_cmdlen;
+ goto loop;
+ }
+
+ /*
+ * Set the absolute mark -- we have to set it for vi here, in case
+ * it's a compound command, e.g. ":5p|6" should set the absolute
+ * mark for vi.
+ */
+ if (F_ISSET(ecp, E_ABSMARK)) {
+ cur.lno = sp->lno;
+ cur.cno = sp->cno;
+ F_CLR(ecp, E_ABSMARK);
+ if (mark_set(sp, ABSMARK1, &cur, 1))
+ goto err;
+ }
+
+#if defined(DEBUG) && defined(COMLOG)
+ ex_comlog(sp, ecp);
+#endif
+ /* Increment the command count if not called from vi. */
+ if (F_ISSET(sp, SC_EX))
+ ++sp->ccnt;
+
+ /*
+ * If file state available, and not doing a global command,
+ * log the start of an action.
+ */
+ if (sp->ep != NULL && !F_ISSET(sp, SC_EX_GLOBAL))
+ (void)log_cursor(sp);
+
+ /*
+ * !!!
+ * There are two special commands for the purposes of this code: the
+ * default command (<carriage-return>) or the scrolling commands (^D
+ * and <EOF>) as the first non-<blank> characters in the line.
+ *
+ * If this is the first command in the command line, we received the
+ * command from the ex command loop and we're talking to a tty, and
+ * and there's nothing else on the command line, and it's one of the
+ * special commands, we move back up to the previous line, and erase
+ * the prompt character with the output. Since ex runs in canonical
+ * mode, we don't have to do anything else, a <newline> has already
+ * been echoed by the tty driver. It's OK if vi calls us -- we won't
+ * be in ex mode so we'll do nothing.
+ */
+ if (F_ISSET(ecp, E_NRSEP)) {
+ if (sp->ep != NULL &&
+ F_ISSET(sp, SC_EX) && !F_ISSET(gp, G_SCRIPTED) &&
+ (F_ISSET(ecp, E_USELASTCMD) || ecp->cmd == &cmds[C_SCROLL]))
+ gp->scr_ex_adjust(sp, EX_TERM_SCROLL);
+ F_CLR(ecp, E_NRSEP);
+ }
+
+ /*
+ * Call the underlying function for the ex command.
+ *
+ * XXX
+ * Interrupts behave like errors, for now.
+ */
+ if (ecp->cmd->fn(sp, ecp) || INTERRUPTED(sp)) {
+ if (F_ISSET(gp, G_SCRIPTED))
+ F_SET(sp, SC_EXIT_FORCE);
+ goto err;
+ }
+
+#ifdef DEBUG
+ /* Make sure no function left global temporary space locked. */
+ if (F_ISSET(gp, G_TMP_INUSE)) {
+ F_CLR(gp, G_TMP_INUSE);
+ msgq(sp, M_ERR, "087|%s: temporary buffer not released",
+ ecp->cmd->name);
+ }
+#endif
+ /*
+ * Ex displayed the number of lines modified immediately after each
+ * command, so the command "1,10d|1,10d" would display:
+ *
+ * 10 lines deleted
+ * 10 lines deleted
+ * <autoprint line>
+ *
+ * Executing ex commands from vi only reported the final modified
+ * lines message -- that's wrong enough that we don't match it.
+ */
+ if (F_ISSET(sp, SC_EX))
+ mod_rpt(sp);
+
+ /*
+ * Integrate any offset parsed by the underlying command, and make
+ * sure the referenced line exists.
+ *
+ * XXX
+ * May not match historic practice (which I've never been able to
+ * completely figure out.) For example, the '=' command from vi
+ * mode often got the offset wrong, and complained it was too large,
+ * but didn't seem to have a problem with the cursor. If anyone
+ * complains, ask them how it's supposed to work, they might know.
+ */
+ if (sp->ep != NULL && ecp->flagoff) {
+ if (ecp->flagoff < 0) {
+ if (sp->lno <= -ecp->flagoff) {
+ msgq(sp, M_ERR,
+ "088|Flag offset to before line 1");
+ goto err;
+ }
+ } else {
+ if (!NPFITS(MAX_REC_NUMBER, sp->lno, ecp->flagoff)) {
+ ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
+ goto err;
+ }
+ if (!db_exist(sp, sp->lno + ecp->flagoff)) {
+ msgq(sp, M_ERR,
+ "089|Flag offset past end-of-file");
+ goto err;
+ }
+ }
+ sp->lno += ecp->flagoff;
+ }
+
+ /*
+ * If the command executed successfully, we may want to display a line
+ * based on the autoprint option or an explicit print flag. (Make sure
+ * that there's a line to display.) Also, the autoprint edit option is
+ * turned off for the duration of global commands.
+ */
+ if (F_ISSET(sp, SC_EX) && sp->ep != NULL && sp->lno != 0) {
+ /*
+ * The print commands have already handled the `print' flags.
+ * If so, clear them.
+ */
+ if (FL_ISSET(ecp->iflags, E_CLRFLAG))
+ FL_CLR(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT);
+
+ /* If hash set only because of the number option, discard it. */
+ if (F_ISSET(ecp, E_OPTNUM))
+ FL_CLR(ecp->iflags, E_C_HASH);
+
+ /*
+ * If there was an explicit flag to display the new cursor line,
+ * or autoprint is set and a change was made, display the line.
+ * If any print flags were set use them, else default to print.
+ */
+ LF_INIT(FL_ISSET(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT));
+ if (!LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT | E_NOAUTO) &&
+ !F_ISSET(sp, SC_EX_GLOBAL) &&
+ O_ISSET(sp, O_AUTOPRINT) && F_ISSET(ecp, E_AUTOPRINT))
+ LF_INIT(E_C_PRINT);
+
+ if (LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT)) {
+ cur.lno = sp->lno;
+ cur.cno = 0;
+ (void)ex_print(sp, ecp, &cur, &cur, flags);
+ }
+ }
+
+ /*
+ * If the command had an associated "+cmd", it has to be executed
+ * before we finish executing any more of this ex command. For
+ * example, consider a .exrc file that contains the following lines:
+ *
+ * :set all
+ * :edit +25 file.c|s/abc/ABC/|1
+ * :3,5 print
+ *
+ * This can happen more than once -- the historic vi simply hung or
+ * dropped core, of course. Prepend the + command back into the
+ * current command and continue. We may have to add an additional
+ * <literal next> character. We know that it will fit because we
+ * discarded at least one space and the + character.
+ */
+ if (arg1_len != 0) {
+ /*
+ * If the last character of the + command was a <literal next>
+ * character, it would be treated differently because of the
+ * append. Quote it, if necessary.
+ */
+ if (IS_ESCAPE(sp, ecp, arg1[arg1_len - 1])) {
+ *--ecp->save_cmd = CH_LITERAL;
+ ++ecp->save_cmdlen;
+ }
+
+ ecp->save_cmd -= arg1_len;
+ ecp->save_cmdlen += arg1_len;
+ memcpy(ecp->save_cmd, arg1, arg1_len);
+
+ /*
+ * Any commands executed from a +cmd are executed starting at
+ * the first column of the last line of the file -- NOT the
+ * first nonblank.) The main file startup code doesn't know
+ * that a +cmd was set, however, so it may have put us at the
+ * top of the file. (Note, this is safe because we must have
+ * switched files to get here.)
+ */
+ F_SET(ecp, E_MOVETOEND);
+ }
+
+ /* Update the current command. */
+ ecp->cp = ecp->save_cmd;
+ ecp->clen = ecp->save_cmdlen;
+
+ /*
+ * !!!
+ * If we've changed screens or underlying files, any pending global or
+ * v command, or @ buffer that has associated addresses, has to be
+ * discarded. This is historic practice for globals, and necessary for
+ * @ buffers that had associated addresses.
+ *
+ * Otherwise, if we've changed underlying files, it's not a problem,
+ * we continue with the rest of the ex command(s), operating on the
+ * new file. However, if we switch screens (either by exiting or by
+ * an explicit command), we have no way of knowing where to put output
+ * messages, and, since we don't control screens here, we could screw
+ * up the upper layers, (e.g. we could exit/reenter a screen multiple
+ * times). So, return and continue after we've got a new screen.
+ */
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_FSWITCH | SC_SSWITCH)) {
+ at_found = gv_found = 0;
+ for (ecp = sp->gp->ecq.lh_first;
+ ecp != NULL; ecp = ecp->q.le_next)
+ switch (ecp->agv_flags) {
+ case 0:
+ case AGV_AT_NORANGE:
+ break;
+ case AGV_AT:
+ if (!at_found) {
+ at_found = 1;
+ msgq(sp, M_ERR,
+ "090|@ with range running when the file/screen changed");
+ }
+ break;
+ case AGV_GLOBAL:
+ case AGV_V:
+ if (!gv_found) {
+ gv_found = 1;
+ msgq(sp, M_ERR,
+ "091|Global/v command running when the file/screen changed");
+ }
+ break;
+ default:
+ abort();
+ }
+ if (at_found || gv_found)
+ goto discard;
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_SSWITCH))
+ goto rsuccess;
+ }
+
+ goto loop;
+ /* NOTREACHED */
+
+err: /*
+ * On command failure, we discard keys and pending commands remaining,
+ * as well as any keys that were mapped and waiting. The save_cmdlen
+ * test is not necessarily correct. If we fail early enough we don't
+ * know if the entire string was a single command or not. Guess, as
+ * it's useful to know if commands other than the current one are being
+ * discarded.
+ */
+ if (ecp->save_cmdlen == 0)
+ for (; ecp->clen; --ecp->clen) {
+ ch = *ecp->cp++;
+ if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
+ --ecp->clen;
+ ++ecp->cp;
+ } else if (ch == '\n' || ch == '|') {
+ if (ecp->clen > 1)
+ ecp->save_cmdlen = 1;
+ break;
+ }
+ }
+ if (ecp->save_cmdlen != 0 || gp->ecq.lh_first != &gp->excmd) {
+discard: msgq(sp, M_BERR,
+ "092|Ex command failed: pending commands discarded");
+ ex_discard(sp);
+ }
+ if (v_event_flush(sp, CH_MAPPED))
+ msgq(sp, M_BERR,
+ "093|Ex command failed: mapped keys discarded");
+
+rfail: tmp = 1;
+ if (0)
+rsuccess: tmp = 0;
+
+ /* Turn off any file name error information. */
+ gp->if_name = NULL;
+
+ /* Turn off the global bit. */
+ F_CLR(sp, SC_EX_GLOBAL);
+
+ return (tmp);
+}
+
+/*
+ * ex_range --
+ * Get a line range for ex commands, or perform a vi ex address search.
+ *
+ * PUBLIC: int ex_range __P((SCR *, EXCMD *, int *));
+ */
+int
+ex_range(sp, ecp, errp)
+ SCR *sp;
+ EXCMD *ecp;
+ int *errp;
+{
+ enum { ADDR_FOUND, ADDR_NEED, ADDR_NONE } addr;
+ GS *gp;
+ EX_PRIVATE *exp;
+ MARK m;
+ int isaddr;
+
+ *errp = 0;
+
+ /*
+ * Parse comma or semi-colon delimited line specs.
+ *
+ * Semi-colon delimiters update the current address to be the last
+ * address. For example, the command
+ *
+ * :3;/pattern/ecp->cp
+ *
+ * will search for pattern from line 3. In addition, if ecp->cp
+ * is not a valid command, the current line will be left at 3, not
+ * at the original address.
+ *
+ * Extra addresses are discarded, starting with the first.
+ *
+ * !!!
+ * If any addresses are missing, they default to the current line.
+ * This was historically true for both leading and trailing comma
+ * delimited addresses as well as for trailing semicolon delimited
+ * addresses. For consistency, we make it true for leading semicolon
+ * addresses as well.
+ */
+ gp = sp->gp;
+ exp = EXP(sp);
+ for (addr = ADDR_NONE, ecp->addrcnt = 0; ecp->clen > 0;)
+ switch (*ecp->cp) {
+ case '%': /* Entire file. */
+ /* Vi ex address searches didn't permit % signs. */
+ if (F_ISSET(ecp, E_VISEARCH))
+ goto ret;
+
+ /* It's an error if the file is empty. */
+ if (sp->ep == NULL) {
+ ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
+ *errp = 1;
+ return (0);
+ }
+ /*
+ * !!!
+ * A percent character addresses all of the lines in
+ * the file. Historically, it couldn't be followed by
+ * any other address. We do it as a text substitution
+ * for simplicity. POSIX 1003.2 is expected to follow
+ * this practice.
+ *
+ * If it's an empty file, the first line is 0, not 1.
+ */
+ if (addr == ADDR_FOUND) {
+ ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
+ *errp = 1;
+ return (0);
+ }
+ if (db_last(sp, &ecp->addr2.lno))
+ return (1);
+ ecp->addr1.lno = ecp->addr2.lno == 0 ? 0 : 1;
+ ecp->addr1.cno = ecp->addr2.cno = 0;
+ ecp->addrcnt = 2;
+ addr = ADDR_FOUND;
+ ++ecp->cp;
+ --ecp->clen;
+ break;
+ case ',': /* Comma delimiter. */
+ /* Vi ex address searches didn't permit commas. */
+ if (F_ISSET(ecp, E_VISEARCH))
+ goto ret;
+ /* FALLTHROUGH */
+ case ';': /* Semi-colon delimiter. */
+ if (sp->ep == NULL) {
+ ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
+ *errp = 1;
+ return (0);
+ }
+ if (addr != ADDR_FOUND)
+ switch (ecp->addrcnt) {
+ case 0:
+ ecp->addr1.lno = sp->lno;
+ ecp->addr1.cno = sp->cno;
+ ecp->addrcnt = 1;
+ break;
+ case 2:
+ ecp->addr1 = ecp->addr2;
+ /* FALLTHROUGH */
+ case 1:
+ ecp->addr2.lno = sp->lno;
+ ecp->addr2.cno = sp->cno;
+ ecp->addrcnt = 2;
+ break;
+ }
+ if (*ecp->cp == ';')
+ switch (ecp->addrcnt) {
+ case 0:
+ abort();
+ /* NOTREACHED */
+ case 1:
+ sp->lno = ecp->addr1.lno;
+ sp->cno = ecp->addr1.cno;
+ break;
+ case 2:
+ sp->lno = ecp->addr2.lno;
+ sp->cno = ecp->addr2.cno;
+ break;
+ }
+ addr = ADDR_NEED;
+ /* FALLTHROUGH */
+ case ' ': /* Whitespace. */
+ case '\t': /* Whitespace. */
+ ++ecp->cp;
+ --ecp->clen;
+ break;
+ default:
+ /* Get a line specification. */
+ if (ex_line(sp, ecp, &m, &isaddr, errp))
+ return (1);
+ if (*errp)
+ return (0);
+ if (!isaddr)
+ goto ret;
+ if (addr == ADDR_FOUND) {
+ ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
+ *errp = 1;
+ return (0);
+ }
+ switch (ecp->addrcnt) {
+ case 0:
+ ecp->addr1 = m;
+ ecp->addrcnt = 1;
+ break;
+ case 1:
+ ecp->addr2 = m;
+ ecp->addrcnt = 2;
+ break;
+ case 2:
+ ecp->addr1 = ecp->addr2;
+ ecp->addr2 = m;
+ break;
+ }
+ addr = ADDR_FOUND;
+ break;
+ }
+
+ /*
+ * !!!
+ * Vi ex address searches are indifferent to order or trailing
+ * semi-colons.
+ */
+ret: if (F_ISSET(ecp, E_VISEARCH))
+ return (0);
+
+ if (addr == ADDR_NEED)
+ switch (ecp->addrcnt) {
+ case 0:
+ ecp->addr1.lno = sp->lno;
+ ecp->addr1.cno = sp->cno;
+ ecp->addrcnt = 1;
+ break;
+ case 2:
+ ecp->addr1 = ecp->addr2;
+ /* FALLTHROUGH */
+ case 1:
+ ecp->addr2.lno = sp->lno;
+ ecp->addr2.cno = sp->cno;
+ ecp->addrcnt = 2;
+ break;
+ }
+
+ if (ecp->addrcnt == 2 && ecp->addr2.lno < ecp->addr1.lno) {
+ msgq(sp, M_ERR,
+ "094|The second address is smaller than the first");
+ *errp = 1;
+ }
+ return (0);
+}
+
+/*
+ * ex_line --
+ * Get a single line address specifier.
+ *
+ * The way the "previous context" mark worked was that any "non-relative"
+ * motion set it. While ex/vi wasn't totally consistent about this, ANY
+ * numeric address, search pattern, '$', or mark reference in an address
+ * was considered non-relative, and set the value. Which should explain
+ * why we're hacking marks down here. The problem was that the mark was
+ * only set if the command was called, i.e. we have to set a flag and test
+ * it later.
+ *
+ * XXX
+ * This is probably still not exactly historic practice, although I think
+ * it's fairly close.
+ */
+static int
+ex_line(sp, ecp, mp, isaddrp, errp)
+ SCR *sp;
+ EXCMD *ecp;
+ MARK *mp;
+ int *isaddrp, *errp;
+{
+ enum nresult nret;
+ EX_PRIVATE *exp;
+ GS *gp;
+ long total, val;
+ int isneg;
+ int (*sf) __P((SCR *, MARK *, MARK *, char *, size_t, char **, u_int));
+ char *endp;
+
+ gp = sp->gp;
+ exp = EXP(sp);
+
+ *isaddrp = *errp = 0;
+ F_CLR(ecp, E_DELTA);
+
+ /* No addresses permitted until a file has been read in. */
+ if (sp->ep == NULL && strchr("$0123456789'\\/?.+-^", *ecp->cp)) {
+ ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
+ *errp = 1;
+ return (0);
+ }
+
+ switch (*ecp->cp) {
+ case '$': /* Last line in the file. */
+ *isaddrp = 1;
+ F_SET(ecp, E_ABSMARK);
+
+ mp->cno = 0;
+ if (db_last(sp, &mp->lno))
+ return (1);
+ ++ecp->cp;
+ --ecp->clen;
+ break; /* Absolute line number. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *isaddrp = 1;
+ F_SET(ecp, E_ABSMARK);
+
+ if ((nret = nget_slong(&val, ecp->cp, &endp, 10)) != NUM_OK) {
+ ex_badaddr(sp, NULL, A_NOTSET, nret);
+ *errp = 1;
+ return (0);
+ }
+ if (!NPFITS(MAX_REC_NUMBER, 0, val)) {
+ ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
+ *errp = 1;
+ return (0);
+ }
+ mp->lno = val;
+ mp->cno = 0;
+ ecp->clen -= (endp - ecp->cp);
+ ecp->cp = endp;
+ break;
+ case '\'': /* Use a mark. */
+ *isaddrp = 1;
+ F_SET(ecp, E_ABSMARK);
+
+ if (ecp->clen == 1) {
+ msgq(sp, M_ERR, "095|No mark name supplied");
+ *errp = 1;
+ return (0);
+ }
+ if (mark_get(sp, ecp->cp[1], mp, M_ERR)) {
+ *errp = 1;
+ return (0);
+ }
+ ecp->cp += 2;
+ ecp->clen -= 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 (ecp->clen < 2 ||
+ ecp->cp[1] != '/' && ecp->cp[1] != '?') {
+ msgq(sp, M_ERR, "096|\\ not followed by / or ?");
+ *errp = 1;
+ return (0);
+ }
+ ++ecp->cp;
+ --ecp->clen;
+ sf = ecp->cp[0] == '/' ? f_search : b_search;
+ goto search;
+ case '/': /* Search forward. */
+ sf = f_search;
+ goto search;
+ case '?': /* Search backward. */
+ sf = b_search;
+
+search: mp->lno = sp->lno;
+ mp->cno = sp->cno;
+ if (sf(sp, mp, mp, ecp->cp, ecp->clen, &endp,
+ SEARCH_MSG | SEARCH_PARSE | SEARCH_SET |
+ (F_ISSET(ecp, E_SEARCH_WMSG) ? SEARCH_WMSG : 0))) {
+ *errp = 1;
+ return (0);
+ }
+
+ /* Fix up the command pointers. */
+ ecp->clen -= (endp - ecp->cp);
+ ecp->cp = endp;
+
+ *isaddrp = 1;
+ F_SET(ecp, E_ABSMARK);
+ break;
+ case '.': /* Current position. */
+ *isaddrp = 1;
+ mp->cno = sp->cno;
+
+ /* If an empty file, then '.' is 0, not 1. */
+ if (sp->lno == 1) {
+ if (db_last(sp, &mp->lno))
+ return (1);
+ if (mp->lno != 0)
+ mp->lno = 1;
+ } else
+ mp->lno = sp->lno;
+
+ /*
+ * !!!
+ * Historically, .<number> was the same as .+<number>, i.e.
+ * the '+' could be omitted. (This feature is found in ed
+ * as well.)
+ */
+ if (ecp->clen > 1 && isdigit(ecp->cp[1]))
+ *ecp->cp = '+';
+ else {
+ ++ecp->cp;
+ --ecp->clen;
+ }
+ break;
+ }
+
+ /* Skip trailing <blank>s. */
+ for (; ecp->clen > 0 &&
+ isblank(ecp->cp[0]); ++ecp->cp, --ecp->clen);
+
+ /*
+ * Evaluate any offset. If no address yet found, the offset
+ * is relative to ".".
+ */
+ total = 0;
+ if (ecp->clen != 0 && (isdigit(ecp->cp[0]) ||
+ ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
+ ecp->cp[0] == '^')) {
+ if (!*isaddrp) {
+ *isaddrp = 1;
+ mp->lno = sp->lno;
+ mp->cno = sp->cno;
+ }
+ /*
+ * Evaluate an offset, defined as:
+ *
+ * [+-^<blank>]*[<blank>]*[0-9]*
+ *
+ * The rough translation is any number of signs, optionally
+ * followed by numbers, or a number by itself, all <blank>
+ * separated.
+ *
+ * !!!
+ * All address offsets were additive, e.g. "2 2 3p" was the
+ * same as "7p", or, "/ZZZ/ 2" was the same as "/ZZZ/+2".
+ * Note, however, "2 /ZZZ/" was an error. It was also legal
+ * to insert signs without numbers, so "3 - 2" was legal, and
+ * equal to 4.
+ *
+ * !!!
+ * Offsets were historically permitted for any line address,
+ * e.g. the command "1,2 copy 2 2 2 2" copied lines 1,2 after
+ * line 8.
+ *
+ * !!!
+ * Offsets were historically permitted for search commands,
+ * and handled as addresses: "/pattern/2 2 2" was legal, and
+ * referenced the 6th line after pattern.
+ */
+ F_SET(ecp, E_DELTA);
+ for (;;) {
+ for (; ecp->clen > 0 && isblank(ecp->cp[0]);
+ ++ecp->cp, --ecp->clen);
+ if (ecp->clen == 0 || !isdigit(ecp->cp[0]) &&
+ ecp->cp[0] != '+' && ecp->cp[0] != '-' &&
+ ecp->cp[0] != '^')
+ break;
+ if (!isdigit(ecp->cp[0]) &&
+ !isdigit(ecp->cp[1])) {
+ total += ecp->cp[0] == '+' ? 1 : -1;
+ --ecp->clen;
+ ++ecp->cp;
+ } else {
+ if (ecp->cp[0] == '-' ||
+ ecp->cp[0] == '^') {
+ ++ecp->cp;
+ --ecp->clen;
+ isneg = 1;
+ } else
+ isneg = 0;
+
+ /* Get a signed long, add it to the total. */
+ if ((nret = nget_slong(&val,
+ ecp->cp, &endp, 10)) != NUM_OK ||
+ (nret = NADD_SLONG(sp,
+ total, val)) != NUM_OK) {
+ ex_badaddr(sp, NULL, A_NOTSET, nret);
+ *errp = 1;
+ return (0);
+ }
+ total += isneg ? -val : val;
+ ecp->clen -= (endp - ecp->cp);
+ ecp->cp = endp;
+ }
+ }
+ }
+
+ /*
+ * Any value less than 0 is an error. Make sure that the new value
+ * will fit into a recno_t.
+ */
+ if (*isaddrp && total != 0) {
+ if (total < 0) {
+ if (-total > mp->lno) {
+ msgq(sp, M_ERR,
+ "097|Reference to a line number less than 0");
+ *errp = 1;
+ return (0);
+ }
+ } else
+ if (!NPFITS(MAX_REC_NUMBER, mp->lno, total)) {
+ ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
+ *errp = 1;
+ return (0);
+ }
+ mp->lno += total;
+ }
+ return (0);
+}
+
+
+/*
+ * ex_load --
+ * Load up the next command, which may be an @ buffer or global command.
+ */
+static int
+ex_load(sp)
+ SCR *sp;
+{
+ GS *gp;
+ EXCMD *ecp;
+ RANGE *rp;
+
+ F_CLR(sp, SC_EX_GLOBAL);
+
+ /*
+ * Lose any exhausted commands. We know that the first command
+ * can't be an AGV command, which makes things a bit easier.
+ */
+ for (gp = sp->gp;;) {
+ /*
+ * If we're back to the original structure, leave it around,
+ * but discard any allocated source name, we've returned to
+ * the beginning of the command stack.
+ */
+ if ((ecp = gp->ecq.lh_first) == &gp->excmd) {
+ if (F_ISSET(ecp, E_NAMEDISCARD)) {
+ free(ecp->if_name);
+ ecp->if_name = NULL;
+ }
+ return (0);
+ }
+
+ /*
+ * ecp->clen will be 0 for the first discarded command, but
+ * may not be 0 for subsequent ones, e.g. if the original
+ * command was ":g/xx/@a|s/b/c/", then when we discard the
+ * command pushed on the stack by the @a, we have to resume
+ * the global command which included the substitute command.
+ */
+ if (ecp->clen != 0)
+ return (0);
+
+ /*
+ * If it's an @, global or v command, we may need to continue
+ * the command on a different line.
+ */
+ if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
+ /* Discard any exhausted ranges. */
+ while ((rp = ecp->rq.cqh_first) != (void *)&ecp->rq)
+ if (rp->start > rp->stop) {
+ CIRCLEQ_REMOVE(&ecp->rq, rp, q);
+ free(rp);
+ } else
+ break;
+
+ /* If there's another range, continue with it. */
+ if (rp != (void *)&ecp->rq)
+ break;
+
+ /* If it's a global/v command, fix up the last line. */
+ if (FL_ISSET(ecp->agv_flags,
+ AGV_GLOBAL | AGV_V) && ecp->range_lno != OOBLNO)
+ if (db_exist(sp, ecp->range_lno))
+ sp->lno = ecp->range_lno;
+ else {
+ if (db_last(sp, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ }
+ free(ecp->o_cp);
+ }
+
+ /* Discard the EXCMD. */
+ LIST_REMOVE(ecp, q);
+ free(ecp);
+ }
+
+ /*
+ * We only get here if it's an active @, global or v command. Set
+ * the current line number, and get a new copy of the command for
+ * the parser. Note, the original pointer almost certainly moved,
+ * so we have play games.
+ */
+ ecp->cp = ecp->o_cp;
+ memcpy(ecp->cp, ecp->cp + ecp->o_clen, ecp->o_clen);
+ ecp->clen = ecp->o_clen;
+ ecp->range_lno = sp->lno = rp->start++;
+
+ if (FL_ISSET(ecp->agv_flags, AGV_GLOBAL | AGV_V))
+ F_SET(sp, SC_EX_GLOBAL);
+ return (0);
+}
+
+/*
+ * ex_discard --
+ * Discard any pending ex commands.
+ */
+static int
+ex_discard(sp)
+ SCR *sp;
+{
+ GS *gp;
+ EXCMD *ecp;
+ RANGE *rp;
+
+ /*
+ * We know the first command can't be an AGV command, so we don't
+ * process it specially. We do, however, nail the command itself.
+ */
+ for (gp = sp->gp; (ecp = gp->ecq.lh_first) != &gp->excmd;) {
+ if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
+ while ((rp = ecp->rq.cqh_first) != (void *)&ecp->rq) {
+ CIRCLEQ_REMOVE(&ecp->rq, rp, q);
+ free(rp);
+ }
+ free(ecp->o_cp);
+ }
+ LIST_REMOVE(ecp, q);
+ free(ecp);
+ }
+ gp->ecq.lh_first->clen = 0;
+ return (0);
+}
+
+/*
+ * ex_unknown --
+ * Display an unknown command name.
+ */
+static void
+ex_unknown(sp, cmd, len)
+ SCR *sp;
+ char *cmd;
+ size_t len;
+{
+ size_t blen;
+ char *bp;
+
+ GET_SPACE_GOTO(sp, bp, blen, len + 1);
+ bp[len] = '\0';
+ memcpy(bp, cmd, len);
+ msgq_str(sp, M_ERR, bp, "098|The %s command is unknown");
+ FREE_SPACE(sp, bp, blen);
+
+alloc_err:
+ return;
+}
+
+/*
+ * 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. See
+ * the usual ranting in the vi/v_txt_ev.c:txt_abbrev() routine.
+ *
+ * PUBLIC: int ex_is_abbrev __P((char *, size_t));
+ */
+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]));
+}
+
+/*
+ * ex_is_unmap -
+ * The vi text input routine needs to know if ex thinks this is an
+ * unmap command, so it can turn off input mapping. See the usual
+ * ranting in the vi/v_txt_ev.c:txt_unmap() routine.
+ *
+ * PUBLIC: int ex_is_unmap __P((char *, size_t));
+ */
+int
+ex_is_unmap(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ /*
+ * The command the vi input routines are really interested in
+ * is "unmap!", not just unmap.
+ */
+ if (name[len - 1] != '!')
+ return (0);
+ --len;
+ return ((cp = ex_comm_search(name, len)) != NULL &&
+ cp == &cmds[C_UNMAP]);
+}
+
+/*
+ * ex_comm_search --
+ * Search for a command name.
+ */
+static 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);
+}
+
+/*
+ * ex_badaddr --
+ * Display a bad address message.
+ *
+ * PUBLIC: void ex_badaddr
+ * PUBLIC: __P((SCR *, EXCMDLIST const *, enum badaddr, enum nresult));
+ */
+void
+ex_badaddr(sp, cp, ba, nret)
+ SCR *sp;
+ EXCMDLIST const *cp;
+ enum badaddr ba;
+ enum nresult nret;
+{
+ recno_t lno;
+
+ switch (nret) {
+ case NUM_OK:
+ break;
+ case NUM_ERR:
+ msgq(sp, M_SYSERR, NULL);
+ return;
+ case NUM_OVER:
+ msgq(sp, M_ERR, "099|Address value overflow");
+ return;
+ case NUM_UNDER:
+ msgq(sp, M_ERR, "100|Address value underflow");
+ return;
+ }
+
+ /*
+ * When encountering an address error, tell the user if there's no
+ * underlying file, that's the real problem.
+ */
+ if (sp->ep == NULL) {
+ ex_emsg(sp, cp->name, EXM_NOFILEYET);
+ return;
+ }
+
+ switch (ba) {
+ case A_COMBO:
+ msgq(sp, M_ERR, "101|Illegal address combination");
+ break;
+ case A_EOF:
+ if (db_last(sp, &lno))
+ return;
+ if (lno != 0) {
+ msgq(sp, M_ERR,
+ "102|Illegal address: only %lu lines in the file",
+ lno);
+ break;
+ }
+ /* FALLTHROUGH */
+ case A_EMPTY:
+ msgq(sp, M_ERR, "103|Illegal address: the file is empty");
+ break;
+ case A_NOTSET:
+ abort();
+ /* NOTREACHED */
+ case A_ZERO:
+ msgq(sp, M_ERR,
+ "104|The %s command doesn't permit an address of 0",
+ cp->name);
+ break;
+ }
+ return;
+}
+
+#if defined(DEBUG) && defined(COMLOG)
+/*
+ * ex_comlog --
+ * Log ex commands.
+ */
+static void
+ex_comlog(sp, ecp)
+ SCR *sp;
+ EXCMD *ecp;
+{
+ TRACE(sp, "ecmd: %s", ecp->cmd->name);
+ if (ecp->addrcnt > 0) {
+ TRACE(sp, " a1 %d", ecp->addr1.lno);
+ if (ecp->addrcnt > 1)
+ TRACE(sp, " a2: %d", ecp->addr2.lno);
+ }
+ if (ecp->lineno)
+ TRACE(sp, " line %d", ecp->lineno);
+ if (ecp->flags)
+ TRACE(sp, " flags 0x%x", ecp->flags);
+ if (F_ISSET(&exc, E_BUFFER))
+ TRACE(sp, " buffer %c", ecp->buffer);
+ if (ecp->argc)
+ for (cnt = 0; cnt < ecp->argc; ++cnt)
+ TRACE(sp, " arg %d: {%s}", cnt, ecp->argv[cnt]->bp);
+ TRACE(sp, "\n");
+}
+#endif
diff --git a/contrib/nvi/ex/ex.h b/contrib/nvi/ex/ex.h
new file mode 100644
index 000000000000..5870990b744e
--- /dev/null
+++ b/contrib/nvi/ex/ex.h
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)ex.h 10.24 (Berkeley) 8/12/96
+ */
+
+#define PROMPTCHAR ':' /* Prompt using a colon. */
+
+typedef struct _excmdlist { /* Ex command table structure. */
+ char *name; /* Command name, underlying function. */
+ int (*fn) __P((SCR *, EXCMD *));
+
+#define E_ADDR1 0x00000001 /* One address. */
+#define E_ADDR2 0x00000002 /* Two addresses. */
+#define E_ADDR2_ALL 0x00000004 /* Zero/two addresses; zero == all. */
+#define E_ADDR2_NONE 0x00000008 /* Zero/two addresses; zero == none. */
+#define E_ADDR_ZERO 0x00000010 /* 0 is a legal addr1. */
+#define E_ADDR_ZERODEF 0x00000020 /* 0 is default addr1 of empty files. */
+#define E_AUTOPRINT 0x00000040 /* Command always sets autoprint. */
+#define E_CLRFLAG 0x00000080 /* Clear the print (#, l, p) flags. */
+#define E_NEWSCREEN 0x00000100 /* Create a new screen. */
+#define E_SECURE 0x00000200 /* Permission denied if O_SECURE set. */
+#define E_VIONLY 0x00000400 /* Meaningful only in vi. */
+#define __INUSE1 0xfffff800 /* Same name space as EX_PRIVATE. */
+ u_int16_t flags;
+
+ char *syntax; /* Syntax script. */
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+} EXCMDLIST;
+
+#define MAXCMDNAMELEN 12 /* Longest command name. */
+extern EXCMDLIST const cmds[]; /* Table of ex commands. */
+
+/*
+ * !!!
+ * QUOTING NOTE:
+ *
+ * Historically, .exrc files and EXINIT variables could only use ^V as an
+ * escape character, neither ^Q or a user specified character worked. We
+ * enforce that here, just in case someone depends on it.
+ */
+#define IS_ESCAPE(sp, cmdp, ch) \
+ (F_ISSET(cmdp, E_VLITONLY) ? \
+ (ch) == CH_LITERAL : KEY_VAL(sp, ch) == K_VLNEXT)
+
+/*
+ * File state must be checked for each command -- any ex command may be entered
+ * at any time, and most of them won't work well if a file hasn't yet been read
+ * in. Historic vi generally took the easy way out and dropped core.
+ */
+#define NEEDFILE(sp, cmdp) { \
+ if ((sp)->ep == NULL) { \
+ ex_emsg(sp, (cmdp)->cmd->name, EXM_NOFILEYET); \
+ return (1); \
+ } \
+}
+
+/* Range structures for global and @ commands. */
+typedef struct _range RANGE;
+struct _range { /* Global command range. */
+ CIRCLEQ_ENTRY(_range) q; /* Linked list of ranges. */
+ recno_t start, stop; /* Start/stop of the range. */
+};
+
+/* Ex command structure. */
+struct _excmd {
+ LIST_ENTRY(_excmd) q; /* Linked list of commands. */
+
+ char *if_name; /* Associated file. */
+ recno_t if_lno; /* Associated line number. */
+
+ /* Clear the structure for the ex parser. */
+#define CLEAR_EX_PARSER(cmdp) \
+ memset(&((cmdp)->cp), 0, ((char *)&(cmdp)->flags - \
+ (char *)&((cmdp)->cp)) + sizeof((cmdp)->flags))
+
+ char *cp; /* Current command text. */
+ size_t clen; /* Current command length. */
+
+ char *save_cmd; /* Remaining command. */
+ size_t save_cmdlen; /* Remaining command length. */
+
+ EXCMDLIST const *cmd; /* Command: entry in command table. */
+ EXCMDLIST rcmd; /* Command: table entry/replacement. */
+
+ CIRCLEQ_HEAD(_rh, _range) rq; /* @/global range: linked list. */
+ recno_t range_lno; /* @/global range: set line number. */
+ char *o_cp; /* Original @/global command. */
+ size_t o_clen; /* Original @/global command length. */
+#define AGV_AT 0x01 /* @ buffer execution. */
+#define AGV_AT_NORANGE 0x02 /* @ buffer execution without range. */
+#define AGV_GLOBAL 0x04 /* global command. */
+#define AGV_V 0x08 /* v command. */
+#define AGV_ALL (AGV_AT | AGV_AT_NORANGE | AGV_GLOBAL | AGV_V)
+ u_int8_t agv_flags;
+
+ /* Clear the structure before each ex command. */
+#define CLEAR_EX_CMD(cmdp) { \
+ u_int32_t L__f = F_ISSET(cmdp, E_PRESERVE); \
+ memset(&((cmdp)->buffer), 0, ((char *)&(cmdp)->flags - \
+ (char *)&((cmdp)->buffer)) + sizeof((cmdp)->flags)); \
+ F_SET(cmdp, L__f); \
+}
+
+ CHAR_T buffer; /* Command: named buffer. */
+ recno_t lineno; /* Command: line number. */
+ long count; /* Command: signed count. */
+ long flagoff; /* Command: signed flag offset. */
+ int addrcnt; /* Command: addresses (0, 1 or 2). */
+ MARK addr1; /* Command: 1st address. */
+ MARK addr2; /* Command: 2nd address. */
+ ARGS **argv; /* Command: array of arguments. */
+ int argc; /* Command: count of arguments. */
+
+#define E_C_BUFFER 0x00001 /* Buffer name specified. */
+#define E_C_CARAT 0x00002 /* ^ flag. */
+#define E_C_COUNT 0x00004 /* Count specified. */
+#define E_C_COUNT_NEG 0x00008 /* Count was signed negative. */
+#define E_C_COUNT_POS 0x00010 /* Count was signed positive. */
+#define E_C_DASH 0x00020 /* - flag. */
+#define E_C_DOT 0x00040 /* . flag. */
+#define E_C_EQUAL 0x00080 /* = flag. */
+#define E_C_FORCE 0x00100 /* ! flag. */
+#define E_C_HASH 0x00200 /* # flag. */
+#define E_C_LIST 0x00400 /* l flag. */
+#define E_C_PLUS 0x00800 /* + flag. */
+#define E_C_PRINT 0x01000 /* p flag. */
+ u_int16_t iflags; /* User input information. */
+
+#define __INUSE2 0x000004ff /* Same name space as EXCMDLIST. */
+#define E_BLIGNORE 0x00000800 /* Ignore blank lines. */
+#define E_NAMEDISCARD 0x00001000 /* Free/discard the name. */
+#define E_NOAUTO 0x00002000 /* Don't do autoprint output. */
+#define E_NOPRDEF 0x00004000 /* Don't print as default. */
+#define E_NRSEP 0x00008000 /* Need to line adjust ex output. */
+#define E_OPTNUM 0x00010000 /* Number edit option affected. */
+#define E_VLITONLY 0x00020000 /* Use ^V quoting only. */
+#define E_PRESERVE 0x0003f800 /* Bits to preserve across commands. */
+
+#define E_ABSMARK 0x00040000 /* Set the absolute mark. */
+#define E_ADDR_DEF 0x00080000 /* Default addresses used. */
+#define E_DELTA 0x00100000 /* Search address with delta. */
+#define E_MODIFY 0x00200000 /* File name expansion modified arg. */
+#define E_MOVETOEND 0x00400000 /* Move to the end of the file first. */
+#define E_NEWLINE 0x00800000 /* Found ending <newline>. */
+#define E_SEARCH_WMSG 0x01000000 /* Display search-wrapped message. */
+#define E_USELASTCMD 0x02000000 /* Use the last command. */
+#define E_VISEARCH 0x04000000 /* It's really a vi search command. */
+ u_int32_t flags; /* Current flags. */
+};
+
+/* Ex private, per-screen memory. */
+typedef struct _ex_private {
+ CIRCLEQ_HEAD(_tqh, _tagq) tq; /* Tag queue. */
+ TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag file list. */
+ LIST_HEAD(_csch, _csc) cscq; /* Cscope connection list. */
+ char *tag_last; /* Saved last tag string. */
+
+ CHAR_T *lastbcomm; /* Last bang command. */
+
+ ARGS **args; /* Command: argument list. */
+ int argscnt; /* Command: argument list count. */
+ int argsoff; /* Command: offset into arguments. */
+
+ u_int32_t fdef; /* Saved E_C_* default command flags. */
+
+ char *ibp; /* File line input buffer. */
+ size_t ibp_len; /* File line input buffer length. */
+
+ /*
+ * Buffers for the ex output. The screen/vi support doesn't do any
+ * character buffering of any kind. We do it here so that we're not
+ * calling the screen output routines on every character.
+ *
+ * XXX
+ * Change to grow dynamically.
+ */
+ char obp[1024]; /* Ex output buffer. */
+ size_t obp_len; /* Ex output buffer length. */
+
+#define EXP_CSCINIT 0x01 /* Cscope initialized. */
+ u_int8_t flags;
+} EX_PRIVATE;
+#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private))
+
+/*
+ * Filter actions:
+ *
+ * FILTER_BANG !: filter text through the utility.
+ * FILTER_RBANG !: read from the utility (without stdin).
+ * FILTER_READ read: read from the utility (with stdin).
+ * FILTER_WRITE write: write to the utility, display its output.
+ */
+enum filtertype { FILTER_BANG, FILTER_RBANG, FILTER_READ, FILTER_WRITE };
+
+/* Ex common error messages. */
+typedef enum {
+ EXM_EMPTYBUF, /* Empty buffer. */
+ EXM_FILECOUNT, /* Too many file names. */
+ EXM_NOCANON, /* No terminal interface. */
+ EXM_NOCANON_F, /* EXM_NOCANO: filter version. */
+ EXM_NOFILEYET, /* Illegal until a file read in. */
+ EXM_NOPREVBUF, /* No previous buffer specified. */
+ EXM_NOPREVRE, /* No previous RE specified. */
+ EXM_NOSUSPEND, /* No suspension. */
+ EXM_SECURE, /* Illegal if secure edit option set. */
+ EXM_SECURE_F, /* EXM_SECURE: filter version */
+ EXM_USAGE /* Standard usage message. */
+} exm_t;
+
+/* Ex address error types. */
+enum badaddr { A_COMBO, A_EMPTY, A_EOF, A_NOTSET, A_ZERO };
+
+/* Ex common tag error messages. */
+typedef enum {
+ TAG_BADLNO, /* Tag line doesn't exist. */
+ TAG_EMPTY, /* Tags stack is empty. */
+ TAG_SEARCH /* Tags search pattern wasn't found. */
+} tagmsg_t;
+
+#include "ex_def.h"
+#include "ex_extern.h"
diff --git a/contrib/nvi/ex/ex_abbrev.c b/contrib/nvi/ex/ex_abbrev.c
new file mode 100644
index 000000000000..231098ce7d25
--- /dev/null
+++ b/contrib/nvi/ex/ex_abbrev.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_abbrev.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+/*
+ * ex_abbr -- :abbreviate [key replacement]
+ * Create an abbreviation or display abbreviations.
+ *
+ * PUBLIC: int ex_abbr __P((SCR *, EXCMD *));
+ */
+int
+ex_abbr(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ CHAR_T *p;
+ size_t len;
+
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, SEQ_ABBREV, 0) == 0)
+ msgq(sp, M_INFO, "105|No abbreviations to display");
+ return (0);
+ case 2:
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * Check for illegal characters.
+ *
+ * !!!
+ * Another fun one, historically. See vi/v_ntext.c:txt_abbrev() for
+ * details. The bottom line is that all abbreviations have to end
+ * with a "word" character, because it's the transition from word to
+ * non-word characters that triggers the test for an abbreviation. In
+ * addition, because of the way the test is done, there can't be any
+ * transitions from word to non-word character (or vice-versa) other
+ * than between the next-to-last and last characters of the string,
+ * and there can't be any <blank> characters. Warn the user.
+ */
+ if (!inword(cmdp->argv[0]->bp[cmdp->argv[0]->len - 1])) {
+ msgq(sp, M_ERR,
+ "106|Abbreviations must end with a \"word\" character");
+ return (1);
+ }
+ for (p = cmdp->argv[0]->bp; *p != '\0'; ++p)
+ if (isblank(p[0])) {
+ msgq(sp, M_ERR,
+ "107|Abbreviations may not contain tabs or spaces");
+ return (1);
+ }
+ if (cmdp->argv[0]->len > 2)
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len - 2; len; --len, ++p)
+ if (inword(p[0]) != inword(p[1])) {
+ msgq(sp, M_ERR,
+"108|Abbreviations may not mix word/non-word characters, except at the end");
+ return (1);
+ }
+
+ if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, SEQ_USERDEF))
+ return (1);
+
+ F_SET(sp->gp, G_ABBREV);
+ return (0);
+}
+
+/*
+ * ex_unabbr -- :unabbreviate key
+ * Delete an abbreviation.
+ *
+ * PUBLIC: int ex_unabbr __P((SCR *, EXCMD *));
+ */
+int
+ex_unabbr(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ ARGS *ap;
+
+ ap = cmdp->argv[0];
+ if (!F_ISSET(sp->gp, G_ABBREV) ||
+ seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) {
+ msgq_str(sp, M_ERR, ap->bp,
+ "109|\"%s\" is not an abbreviation");
+ return (1);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_append.c b/contrib/nvi/ex/ex_append.c
new file mode 100644
index 000000000000..8d89e125f51f
--- /dev/null
+++ b/contrib/nvi/ex/ex_append.c
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_append.c 10.30 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+enum which {APPEND, CHANGE, INSERT};
+
+static int ex_aci __P((SCR *, EXCMD *, 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.
+ *
+ * PUBLIC: int ex_append __P((SCR *, EXCMD *));
+ */
+int
+ex_append(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, cmdp, APPEND));
+}
+
+/*
+ * ex_change -- :[line[,line]] c[hange][!] [count]
+ * Change one or more lines to the input text.
+ *
+ * PUBLIC: int ex_change __P((SCR *, EXCMD *));
+ */
+int
+ex_change(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, 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.
+ *
+ * PUBLIC: int ex_insert __P((SCR *, EXCMD *));
+ */
+int
+ex_insert(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_aci(sp, cmdp, INSERT));
+}
+
+/*
+ * ex_aci --
+ * Append, change, insert in ex.
+ */
+static int
+ex_aci(sp, cmdp, cmd)
+ SCR *sp;
+ EXCMD *cmdp;
+ enum which cmd;
+{
+ CHAR_T *p, *t;
+ GS *gp;
+ TEXT *tp;
+ TEXTH tiq;
+ recno_t cnt, lno;
+ size_t len;
+ u_int32_t flags;
+ int need_newline;
+
+ gp = sp->gp;
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * If doing a change, replace lines for as long as possible. Then,
+ * append more lines or delete remaining lines. Changes to an empty
+ * file are appends, inserts are the same as appends to the previous
+ * line.
+ *
+ * !!!
+ * Set the address to which we'll append. We set sp->lno to this
+ * address as well so that autoindent works correctly when get text
+ * from the user.
+ */
+ lno = cmdp->addr1.lno;
+ sp->lno = lno;
+ if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
+ --lno;
+
+ /*
+ * !!!
+ * If the file isn't empty, cut changes into the unnamed buffer.
+ */
+ if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
+ (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
+ del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
+ return (1);
+
+ /*
+ * !!!
+ * Anything that was left after the command separator becomes part
+ * of the inserted text. Apparently, it was common usage to enter:
+ *
+ * :g/pattern/append|stuff1
+ *
+ * and append the line of text "stuff1" to the lines containing the
+ * pattern. It was also historically legal to enter:
+ *
+ * :append|stuff1
+ * stuff2
+ * .
+ *
+ * and the text on the ex command line would be appended as well as
+ * the text inserted after it. There was an historic bug however,
+ * that the user had to enter *two* terminating lines (the '.' lines)
+ * to terminate text input mode, in this case. This whole thing
+ * could be taken too far, however. Entering:
+ *
+ * :append|stuff1\
+ * stuff2
+ * stuff3
+ * .
+ *
+ * i.e. mixing and matching the forms confused the historic vi, and,
+ * not only did it take two terminating lines to terminate text input
+ * mode, but the trailing backslashes were retained on the input. We
+ * match historic practice except that we discard the backslashes.
+ *
+ * Input lines specified on the ex command line lines are separated by
+ * <newline>s. If there is a trailing delimiter an empty line was
+ * inserted. There may also be a leading delimiter, which is ignored
+ * unless it's also a trailing delimiter. It is possible to encounter
+ * a termination line, i.e. a single '.', in a global command, but not
+ * necessary if the text insert command was the last of the global
+ * commands.
+ */
+ if (cmdp->save_cmdlen != 0) {
+ for (p = cmdp->save_cmd,
+ len = cmdp->save_cmdlen; len > 0; p = t) {
+ for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
+ if (t != p || len == 0) {
+ if (F_ISSET(sp, SC_EX_GLOBAL) &&
+ t - p == 1 && p[0] == '.') {
+ ++t;
+ if (len > 0)
+ --len;
+ break;
+ }
+ if (db_append(sp, 1, lno++, p, t - p))
+ return (1);
+ }
+ if (len != 0) {
+ ++t;
+ if (--len == 0 &&
+ db_append(sp, 1, lno++, "", 0))
+ return (1);
+ }
+ }
+ /*
+ * If there's any remaining text, we're in a global, and
+ * there's more command to parse.
+ *
+ * !!!
+ * We depend on the fact that non-global commands will eat the
+ * rest of the command line as text input, and before getting
+ * any text input from the user. Otherwise, we'd have to save
+ * off the command text before or during the call to the text
+ * input function below.
+ */
+ if (len != 0)
+ cmdp->save_cmd = t;
+ cmdp->save_cmdlen = len;
+ }
+
+ if (F_ISSET(sp, SC_EX_GLOBAL)) {
+ if ((sp->lno = lno) == 0 && db_exist(sp, 1))
+ sp->lno = 1;
+ return (0);
+ }
+
+ /*
+ * If not in a global command, read from the terminal.
+ *
+ * If this code is called by vi, we want to reset the terminal and use
+ * ex's line get routine. It actually works fine if we use vi's get
+ * routine, but it doesn't look as nice. Maybe if we had a separate
+ * window or something, but getting a line at a time looks awkward.
+ * However, depending on the screen that we're using, that may not
+ * be possible.
+ */
+ if (F_ISSET(sp, SC_VI)) {
+ if (gp->scr_screen(sp, SC_EX)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
+ return (1);
+ }
+
+ /* If we're still in the vi screen, move out explicitly. */
+ need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
+ F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
+ if (need_newline)
+ (void)ex_puts(sp, "\n");
+
+ /*
+ * !!!
+ * Users of historical versions of vi sometimes get confused
+ * when they enter append mode, and can't seem to get out of
+ * it. Give them an informational message.
+ */
+ (void)ex_puts(sp,
+ msg_cat(sp, "273|Entering ex input mode.", NULL));
+ (void)ex_puts(sp, "\n");
+ (void)ex_fflush(sp);
+ }
+
+ /*
+ * Set input flags; the ! flag turns off autoindent for append,
+ * change and insert.
+ */
+ LF_INIT(TXT_DOTTERM | TXT_NUMBER);
+ if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
+ LF_SET(TXT_AUTOINDENT);
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+
+ /*
+ * This code can't use the common screen TEXTH structure (sp->tiq),
+ * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
+ * as we are only halfway through the text when the append code fires.
+ * Use a local structure instead. (The ex code would have to use a
+ * local structure except that we're guaranteed to finish remaining
+ * characters in the common TEXTH structure when they were inserted
+ * into the file, above.)
+ */
+ memset(&tiq, 0, sizeof(TEXTH));
+ CIRCLEQ_INIT(&tiq);
+
+ if (ex_txt(sp, &tiq, 0, flags))
+ return (1);
+
+ for (cnt = 0, tp = tiq.cqh_first;
+ tp != (TEXT *)&tiq; ++cnt, tp = tp->q.cqe_next)
+ if (db_append(sp, 1, lno++, tp->lb, tp->len))
+ return (1);
+
+ /*
+ * Set sp->lno to the final line number value (correcting for a
+ * possible 0 value) as that's historically correct for the final
+ * line value, whether or not the user entered any text.
+ */
+ if ((sp->lno = lno) == 0 && db_exist(sp, 1))
+ sp->lno = 1;
+
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_args.c b/contrib/nvi/ex/ex_args.c
new file mode 100644
index 000000000000..bc37109fc130
--- /dev/null
+++ b/contrib/nvi/ex/ex_args.c
@@ -0,0 +1,327 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_args.c 10.16 (Berkeley) 7/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+static int ex_N_next __P((SCR *, EXCMD *));
+
+/*
+ * ex_next -- :next [+cmd] [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.
+ *
+ * PUBLIC: int ex_next __P((SCR *, EXCMD *));
+ */
+int
+ex_next(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ ARGS **argv;
+ FREF *frp;
+ int noargs;
+ char **ap;
+
+ /* Check for file to move to. */
+ if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) {
+ msgq(sp, M_ERR, "111|No more files to edit");
+ return (1);
+ }
+
+ if (F_ISSET(cmdp, E_NEWSCREEN)) {
+ /* By default, edit the next file in the old argument list. */
+ if (cmdp->argc == 0) {
+ if (argv_exp0(sp,
+ cmdp, sp->cargv[1], strlen(sp->cargv[1])))
+ return (1);
+ return (ex_edit(sp, cmdp));
+ }
+ return (ex_N_next(sp, cmdp));
+ }
+
+ /* Check modification. */
+ if (file_m1(sp,
+ FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /* Any arguments are a replacement file list. */
+ if (cmdp->argc) {
+ /* Free the current list. */
+ if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) {
+ for (ap = sp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(sp->argv);
+ }
+ F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER);
+ sp->cargv = NULL;
+
+ /* Create a new list. */
+ CALLOC_RET(sp,
+ sp->argv, char **, cmdp->argc + 1, sizeof(char *));
+ for (ap = sp->argv,
+ argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ return (1);
+ *ap = NULL;
+
+ /* Switch to the first file. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ noargs = 0;
+
+ /* Display a file count with the welcome message. */
+ F_SET(sp, SC_STATUS_CNT);
+ } else {
+ if ((frp = file_add(sp, sp->cargv[1])) == NULL)
+ return (1);
+ if (F_ISSET(sp, SC_ARGRECOVER))
+ F_SET(frp, FR_RECOVER);
+ noargs = 1;
+ }
+
+ if (file_init(sp, frp, NULL, FS_SETALT |
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
+ return (1);
+ if (noargs)
+ ++sp->cargv;
+
+ F_SET(sp, SC_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_N_next --
+ * New screen version of ex_next.
+ */
+static int
+ex_N_next(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ SCR *new;
+ FREF *frp;
+
+ /* Get a new screen. */
+ if (screen_init(sp->gp, sp, &new))
+ return (1);
+ if (vs_split(sp, new, 0)) {
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Get a backing file. */
+ if ((frp = file_add(new, cmdp->argv[0]->bp)) == NULL ||
+ file_init(new, frp, NULL,
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0))) {
+ (void)vs_discard(new, NULL);
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* The arguments are a replacement file list. */
+ new->cargv = new->argv = ex_buildargv(sp, cmdp, NULL);
+
+ /* Display a file count with the welcome message. */
+ F_SET(new, SC_STATUS_CNT);
+
+ /* Set up the switch. */
+ sp->nextdisp = new;
+ F_SET(sp, SC_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * ex_prev -- :prev
+ * Edit the previous file.
+ *
+ * PUBLIC: int ex_prev __P((SCR *, EXCMD *));
+ */
+int
+ex_prev(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ FREF *frp;
+
+ if (sp->cargv == sp->argv) {
+ msgq(sp, M_ERR, "112|No previous files to edit");
+ return (1);
+ }
+
+ if (F_ISSET(cmdp, E_NEWSCREEN)) {
+ if (argv_exp0(sp, cmdp, sp->cargv[-1], strlen(sp->cargv[-1])))
+ return (1);
+ return (ex_edit(sp, cmdp));
+ }
+
+ if (file_m1(sp,
+ FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ if ((frp = file_add(sp, sp->cargv[-1])) == NULL)
+ return (1);
+
+ if (file_init(sp, frp, NULL, FS_SETALT |
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
+ return (1);
+ --sp->cargv;
+
+ F_SET(sp, SC_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_rew -- :rew
+ * Re-edit the list of files.
+ *
+ * !!!
+ * Historic practice was that all files would start editing at the beginning
+ * of the file. We don't get this right because we may have multiple screens
+ * and we can't clear the FR_CURSORSET bit for a single screen. I don't see
+ * anyone noticing, but if they do, we'll have to put information into the SCR
+ * structure so we can keep track of it.
+ *
+ * PUBLIC: int ex_rew __P((SCR *, EXCMD *));
+ */
+int
+ex_rew(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ FREF *frp;
+
+ /*
+ * !!!
+ * Historic practice -- you can rewind to the current file.
+ */
+ if (sp->argv == NULL) {
+ msgq(sp, M_ERR, "113|No previous files to rewind");
+ return (1);
+ }
+
+ if (file_m1(sp,
+ FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /* Switch to the first one. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, FS_SETALT |
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
+ return (1);
+
+ /* Switch and display a file count with the welcome message. */
+ F_SET(sp, SC_FSWITCH | SC_STATUS_CNT);
+
+ return (0);
+}
+
+/*
+ * ex_args -- :args
+ * Display the list of files.
+ *
+ * PUBLIC: int ex_args __P((SCR *, EXCMD *));
+ */
+int
+ex_args(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ GS *gp;
+ int cnt, col, len, sep;
+ char **ap;
+
+ if (sp->argv == NULL) {
+ (void)msgq(sp, M_ERR, "114|No file list to display");
+ return (0);
+ }
+
+ gp = sp->gp;
+ col = len = sep = 0;
+ for (cnt = 1, ap = sp->argv; *ap != NULL; ++ap) {
+ col += len = strlen(*ap) + sep + (ap == sp->cargv ? 2 : 0);
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_puts(sp, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_puts(sp, " ");
+ }
+ ++cnt;
+
+ (void)ex_printf(sp, "%s%s%s", ap == sp->cargv ? "[" : "",
+ *ap, ap == sp->cargv ? "]" : "");
+ if (INTERRUPTED(sp))
+ break;
+ }
+ (void)ex_puts(sp, "\n");
+ return (0);
+}
+
+/*
+ * ex_buildargv --
+ * Build a new file argument list.
+ *
+ * PUBLIC: char **ex_buildargv __P((SCR *, EXCMD *, char *));
+ */
+char **
+ex_buildargv(sp, cmdp, name)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *name;
+{
+ ARGS **argv;
+ int argc;
+ char **ap, **s_argv;
+
+ argc = cmdp == NULL ? 1 : cmdp->argc;
+ CALLOC(sp, s_argv, char **, argc + 1, sizeof(char *));
+ if ((ap = s_argv) == NULL)
+ return (NULL);
+
+ if (cmdp == NULL) {
+ if ((*ap = v_strdup(sp, name, strlen(name))) == NULL)
+ return (NULL);
+ ++ap;
+ } else
+ for (argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ return (NULL);
+ *ap = NULL;
+ return (s_argv);
+}
diff --git a/contrib/nvi/ex/ex_argv.c b/contrib/nvi/ex/ex_argv.c
new file mode 100644
index 000000000000..cc5a201bea26
--- /dev/null
+++ b/contrib/nvi/ex/ex_argv.c
@@ -0,0 +1,756 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_argv.c 10.26 (Berkeley) 9/20/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+static int argv_alloc __P((SCR *, size_t));
+static int argv_comp __P((const void *, const void *));
+static int argv_fexp __P((SCR *, EXCMD *,
+ char *, size_t, char *, size_t *, char **, size_t *, int));
+static int argv_lexp __P((SCR *, EXCMD *, char *));
+static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
+
+/*
+ * argv_init --
+ * Build a prototype arguments list.
+ *
+ * PUBLIC: int argv_init __P((SCR *, EXCMD *));
+ */
+int
+argv_init(sp, excp)
+ SCR *sp;
+ EXCMD *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 --
+ * Append a string to the argument list.
+ *
+ * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, char *, size_t));
+ */
+int
+argv_exp0(sp, excp, cmd, cmdlen)
+ SCR *sp;
+ EXCMD *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ argv_alloc(sp, cmdlen);
+ memcpy(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 append it to the
+ * argument list.
+ *
+ * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, char *, size_t, int));
+ */
+int
+argv_exp1(sp, excp, cmd, cmdlen, is_bang)
+ SCR *sp;
+ EXCMD *excp;
+ char *cmd;
+ size_t cmdlen;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ size_t blen, len;
+ char *bp, *p, *t;
+
+ 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);
+ }
+
+ /* If it's empty, we're done. */
+ if (len != 0) {
+ for (p = bp, t = bp + len; p < t; ++p)
+ if (!isblank(*p))
+ break;
+ if (p == t)
+ goto ret;
+ } else
+ goto ret;
+
+ (void)argv_exp0(sp, excp, bp, len);
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (0);
+}
+
+/*
+ * argv_exp2 --
+ * Do file name and shell expansion on a string, and append it to
+ * the argument list.
+ *
+ * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, char *, size_t));
+ */
+int
+argv_exp2(sp, excp, cmd, cmdlen)
+ SCR *sp;
+ EXCMD *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ size_t blen, len, n;
+ int rval;
+ char *bp, *mp, *p;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+
+#define SHELLECHO "echo "
+#define SHELLOFFSET (sizeof(SHELLECHO) - 1)
+ memcpy(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, 0)) {
+ 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. Historically, it was a
+ * union of v7 shell and csh meta characters. We match that practice
+ * by default, so ":read \%" tries to read a file named '%'. It would
+ * make more sense to pass any special characters through the shell,
+ * but then, if your shell was csh, the above example will behave
+ * differently in nvi than in vi. If you want to get other characters
+ * passed through to your shell, change the "meta" option.
+ *
+ * To avoid a function call per character, we do a first pass through
+ * the meta characters looking for characters that aren't expected
+ * to be there, and then we can ignore them in the user's argument.
+ */
+ if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
+ n = 0;
+ else {
+ for (p = mp = O_STR(sp, O_SHELLMETA); *p != '\0'; ++p)
+ if (isblank(*p) || isalnum(*p))
+ break;
+ p = bp + SHELLOFFSET;
+ n = len - SHELLOFFSET;
+ if (*p != '\0') {
+ for (; n > 0; --n, ++p)
+ if (strchr(mp, *p) != NULL)
+ break;
+ } else
+ for (; n > 0; --n, ++p)
+ if (!isblank(*p) &&
+ !isalnum(*p) && strchr(mp, *p) != NULL)
+ break;
+ }
+
+ /*
+ * If we found a meta character in the string, fork a shell to expand
+ * it. Unfortunately, this is comparatively slow. Historically, it
+ * didn't matter much, since users don't enter meta characters as part
+ * of pathnames that frequently. The addition of filename completion
+ * broke that assumption because it's easy to use. As a result, lots
+ * folks have complained that the expansion code is too slow. So, we
+ * detect filename completion as a special case, and do it internally.
+ * Note that this code assumes that the <asterisk> character is the
+ * match-anything meta character. That feels safe -- if anyone writes
+ * a shell that doesn't follow that convention, I'd suggest giving them
+ * a festive hot-lead enema.
+ */
+ switch (n) {
+ case 0:
+ p = bp + SHELLOFFSET;
+ len -= SHELLOFFSET;
+ rval = argv_exp3(sp, excp, p, len);
+ break;
+ case 1:
+ if (*p == '*') {
+ *p = '\0';
+ rval = argv_lexp(sp, excp, bp + SHELLOFFSET);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (argv_sexp(sp, &bp, &blen, &len)) {
+ rval = 1;
+ goto err;
+ }
+ p = bp;
+ rval = argv_exp3(sp, excp, p, len);
+ break;
+ }
+
+err: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * argv_exp3 --
+ * Take a string and break it up into an argv, which is appended
+ * to the argument list.
+ *
+ * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, char *, size_t));
+ */
+int
+argv_exp3(sp, excp, cmd, cmdlen)
+ SCR *sp;
+ EXCMD *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+ size_t len;
+ int ch, off;
+ char *ap, *p;
+
+ 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) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, excp, ch) && 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 (IS_ESCAPE(sp, excp, *ap))
+ ++ap;
+ *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;
+ EXCMD *excp;
+ char *cmd, *p, **bpp;
+ size_t cmdlen, *lenp, *blenp;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ char *bp, *t;
+ size_t blen, len, off, 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,
+ "115|No previous command to replace \"!\"");
+ return (1);
+ }
+ len += tlen = strlen(exp->lastbcomm);
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ p = bp + off;
+ memcpy(p, exp->lastbcomm, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '%':
+ if ((t = sp->frp->name) == NULL) {
+ msgq(sp, M_ERR,
+ "116|No filename to substitute for %%");
+ return (1);
+ }
+ tlen = strlen(t);
+ len += tlen;
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ p = bp + off;
+ memcpy(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '#':
+ if ((t = sp->alt_name) == NULL) {
+ msgq(sp, M_ERR,
+ "117|No filename to substitute for #");
+ return (1);
+ }
+ len += tlen = strlen(t);
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ p = bp + off;
+ memcpy(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[1] == '!')) {
+ ++cmd;
+ --cmdlen;
+ }
+ /* FALLTHROUGH */
+ default:
+ins_ch: ++len;
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ p = bp + off;
+ *p++ = *cmd;
+ }
+
+ /* Nul termination. */
+ ++len;
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ p = bp + off;
+ *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[exp->argscnt], 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.
+ *
+ * PUBLIC: int argv_free __P((SCR *));
+ */
+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]);
+ }
+ free(exp->args);
+ }
+ exp->args = NULL;
+ exp->argscnt = 0;
+ exp->argsoff = 0;
+ return (0);
+}
+
+/*
+ * argv_lexp --
+ * Find all file names matching the prefix and append them to the
+ * buffer.
+ */
+static int
+argv_lexp(sp, excp, path)
+ SCR *sp;
+ EXCMD *excp;
+ char *path;
+{
+ struct dirent *dp;
+ DIR *dirp;
+ EX_PRIVATE *exp;
+ int off;
+ size_t dlen, len, nlen;
+ char *dname, *name, *p;
+
+ exp = EXP(sp);
+
+ /* Set up the name and length for comparison. */
+ if ((p = strrchr(path, '/')) == NULL) {
+ dname = ".";
+ dlen = 0;
+ name = path;
+ } else {
+ if (p == path) {
+ dname = "/";
+ dlen = 1;
+ } else {
+ *p = '\0';
+ dname = path;
+ dlen = strlen(path);
+ }
+ name = p + 1;
+ }
+ nlen = strlen(name);
+
+ /*
+ * XXX
+ * We don't use the d_namlen field, it's not portable enough; we
+ * assume that d_name is nul terminated, instead.
+ */
+ if ((dirp = opendir(dname)) == NULL) {
+ msgq_str(sp, M_SYSERR, dname, "%s");
+ return (1);
+ }
+ for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
+ if (nlen == 0) {
+ if (dp->d_name[0] == '.')
+ continue;
+ len = strlen(dp->d_name);
+ } else {
+ len = strlen(dp->d_name);
+ if (len < nlen || memcmp(dp->d_name, name, nlen))
+ continue;
+ }
+
+ /* Directory + name + slash + null. */
+ argv_alloc(sp, dlen + len + 2);
+ p = exp->args[exp->argsoff]->bp;
+ if (dlen != 0) {
+ memcpy(p, dname, dlen);
+ p += dlen;
+ if (dlen > 1 || dname[0] != '/')
+ *p++ = '/';
+ }
+ memcpy(p, dp->d_name, len + 1);
+ exp->args[exp->argsoff]->len = dlen + len + 1;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ }
+ closedir(dirp);
+
+ if (off == exp->argsoff) {
+ /*
+ * If we didn't find a match, complain that the expansion
+ * failed. We can't know for certain that's the error, but
+ * it's a good guess, and it matches historic practice.
+ */
+ msgq(sp, M_ERR, "304|Shell expansion failed");
+ return (1);
+ }
+ qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
+ return (0);
+}
+
+/*
+ * argv_comp --
+ * Alphabetic comparison.
+ */
+static int
+argv_comp(a, b)
+ const void *a, *b;
+{
+ return (strcmp((char *)(*(ARGS **)a)->bp, (char *)(*(ARGS **)b)->bp));
+}
+
+/*
+ * 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;
+{
+ enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
+ FILE *ifp;
+ pid_t pid;
+ size_t blen, len;
+ int ch, std_output[2];
+ char *bp, *p, *sh, *sh_path;
+
+ /* Secure means no shell access. */
+ if (O_ISSET(sp, O_SECURE)) {
+ msgq(sp, M_ERR,
+"289|Shell expansions not supported when the secure edit option is set");
+ return (1);
+ }
+
+ sh_path = O_STR(sp, O_SHELL);
+ if ((sh = strrchr(sh_path, '/')) == NULL)
+ sh = sh_path;
+ else
+ ++sh;
+
+ /* Local copies of the buffer variables. */
+ bp = *bpp;
+ blen = *blenp;
+
+ /*
+ * There are two different processes running through this code, named
+ * the utility (the shell) and the parent. The utility reads standard
+ * input and writes standard output and standard error output. The
+ * parent writes to the utility, reads its standard output and ignores
+ * its standard error output. Historically, the standard error output
+ * was discarded by vi, as it produces a lot of noise when file patterns
+ * don't match.
+ *
+ * The parent reads std_output[0], and the utility writes std_output[1].
+ */
+ ifp = NULL;
+ std_output[0] = std_output[1] = -1;
+ if (pipe(std_output) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ return (1);
+ }
+ if ((ifp = fdopen(std_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. We sincerely hope.
+ */
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "vfork");
+err: if (ifp != NULL)
+ (void)fclose(ifp);
+ else if (std_output[0] != -1)
+ close(std_output[0]);
+ if (std_output[1] != -1)
+ close(std_output[0]);
+ return (1);
+ case 0: /* Utility. */
+ /* Redirect stdout to the write end of the pipe. */
+ (void)dup2(std_output[1], STDOUT_FILENO);
+
+ /* Close the utility's file descriptors. */
+ (void)close(std_output[0]);
+ (void)close(std_output[1]);
+ (void)close(STDERR_FILENO);
+
+ /*
+ * XXX
+ * Assume that all shells have -c.
+ */
+ execl(sh_path, sh, "-c", bp, NULL);
+ msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
+ _exit(127);
+ default: /* Parent. */
+ /* Close the pipe ends the parent won't use. */
+ (void)close(std_output[1]);
+ break;
+ }
+
+ /*
+ * Copy process standard 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, *blenp, *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')) {
+ --p;
+ --len;
+ }
+ *p = '\0';
+ *lenp = len;
+ *bpp = bp; /* *blenp is already updated. */
+
+ if (ferror(ifp))
+ goto ioerr;
+ if (fclose(ifp)) {
+ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
+alloc_err: rval = SEXP_ERR;
+ } else
+ rval = SEXP_OK;
+
+ /*
+ * Wait for the process. If the shell process fails (e.g., "echo $q"
+ * where q wasn't a defined variable) or if the returned string has
+ * no characters or only blank characters, (e.g., "echo $5"), complain
+ * that the shell expansion failed. We can't know for certain that's
+ * the error, but it's a good guess, and it matches historic practice.
+ * This won't catch "echo foo_$5", but that's not a common error and
+ * historic vi didn't catch it either.
+ */
+ if (proc_wait(sp, (long)pid, sh, 1, 0))
+ rval = SEXP_EXPANSION_ERR;
+
+ for (p = bp; len; ++p, --len)
+ if (!isblank(*p))
+ break;
+ if (len == 0)
+ rval = SEXP_EXPANSION_ERR;
+
+ if (rval == SEXP_EXPANSION_ERR)
+ msgq(sp, M_ERR, "304|Shell expansion failed");
+
+ return (rval == SEXP_OK ? 0 : 1);
+}
diff --git a/contrib/nvi/ex/ex_at.c b/contrib/nvi/ex/ex_at.c
new file mode 100644
index 000000000000..e9c6c592d60b
--- /dev/null
+++ b/contrib/nvi/ex/ex_at.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_at.c 10.12 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_at -- :@[@ | buffer]
+ * :*[* | buffer]
+ *
+ * Execute the contents of the buffer.
+ *
+ * PUBLIC: int ex_at __P((SCR *, EXCMD *));
+ */
+int
+ex_at(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ CB *cbp;
+ CHAR_T name;
+ EXCMD *ecp;
+ RANGE *rp;
+ TEXT *tp;
+ size_t len;
+ char *p;
+
+ /*
+ * !!!
+ * Historically, [@*]<carriage-return> and [@*][@*] executed the most
+ * recently executed buffer in ex mode.
+ */
+ name = FL_ISSET(cmdp->iflags, E_C_BUFFER) ? cmdp->buffer : '@';
+ if (name == '@' || name == '*') {
+ if (!F_ISSET(sp, SC_AT_SET)) {
+ ex_emsg(sp, NULL, EXM_NOPREVBUF);
+ return (1);
+ }
+ name = sp->at_lbuf;
+ }
+ sp->at_lbuf = name;
+ F_SET(sp, SC_AT_SET);
+
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ ex_emsg(sp, KEY_NAME(sp, name), EXM_EMPTYBUF);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically the @ command took a range of lines, and the @ buffer
+ * was executed once per line. The historic vi could be trashed by
+ * this because it didn't notice if the underlying file changed, or,
+ * for that matter, if there were no more lines on which to operate.
+ * For example, take a 10 line file, load "%delete" into a buffer,
+ * and enter :8,10@<buffer>.
+ *
+ * The solution is a bit tricky. If the user specifies a range, take
+ * the same approach as for global commands, and discard the command
+ * if exit or switch to a new file/screen. If the user doesn't specify
+ * the range, continue to execute after a file/screen switch, which
+ * means @ buffers are still useful in a multi-screen environment.
+ */
+ CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
+ CIRCLEQ_INIT(&ecp->rq);
+ CALLOC_RET(sp, rp, RANGE *, 1, sizeof(RANGE));
+ rp->start = cmdp->addr1.lno;
+ if (F_ISSET(cmdp, E_ADDR_DEF)) {
+ rp->stop = rp->start;
+ FL_SET(ecp->agv_flags, AGV_AT_NORANGE);
+ } else {
+ rp->stop = cmdp->addr2.lno;
+ FL_SET(ecp->agv_flags, AGV_AT);
+ }
+ CIRCLEQ_INSERT_HEAD(&ecp->rq, rp, q);
+
+ /*
+ * Buffers executed in ex mode or from the colon command line in vi
+ * were ex commands. We can't push it on the terminal queue, since
+ * it has to be executed immediately, and we may be in the middle of
+ * an ex command already. Push the command on the ex command stack.
+ * Build two copies of the command. We need two copies because the
+ * ex parser may step on the command string when it's parsing it.
+ */
+ for (len = 0, tp = cbp->textq.cqh_last;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+ len += tp->len + 1;
+
+ MALLOC_RET(sp, ecp->cp, char *, len * 2);
+ ecp->o_cp = ecp->cp;
+ ecp->o_clen = len;
+ ecp->cp[len] = '\0';
+
+ /* Copy the buffer into the command space. */
+ for (p = ecp->cp + len, tp = cbp->textq.cqh_last;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_prev) {
+ memcpy(p, tp->lb, tp->len);
+ p += tp->len;
+ *p++ = '\n';
+ }
+
+ LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_bang.c b/contrib/nvi/ex/ex_bang.c
new file mode 100644
index 000000000000..25f3f7732ab0
--- /dev/null
+++ b/contrib/nvi/ex/ex_bang.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_bang.c 10.33 (Berkeley) 9/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.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 it right (wrong?), so be careful.
+ *
+ * PUBLIC: int ex_bang __P((SCR *, EXCMD *));
+ */
+int
+ex_bang(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ enum filtertype ftype;
+ ARGS *ap;
+ EX_PRIVATE *exp;
+ MARK rm;
+ recno_t lno;
+ int rval;
+ const char *msg;
+
+ ap = cmdp->argv[0];
+ if (ap->len == 0) {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+
+ /* Set the "last bang command" remembered value. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ if ((exp->lastbcomm = strdup(ap->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /*
+ * If the command was modified by the expansion, it was historically
+ * redisplayed.
+ */
+ if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
+ /*
+ * Display the command if modified. Historic ex/vi displayed
+ * the command if it was modified due to file name and/or bang
+ * expansion. If piping lines in vi, it would be immediately
+ * overwritten by any error or line change reporting.
+ */
+ if (F_ISSET(sp, SC_VI))
+ vs_update(sp, "!", ap->bp);
+ else {
+ (void)ex_printf(sp, "!%s\n", ap->bp);
+ (void)ex_fflush(sp);
+ }
+ }
+
+ /*
+ * If no addresses were specified, run the command. If there's an
+ * underlying file, it's 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.
+ */
+ if (cmdp->addrcnt == 0) {
+ msg = NULL;
+ if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_aw(sp, FS_ALL))
+ return (0);
+ } else if (O_ISSET(sp, O_WARN) &&
+ !F_ISSET(sp, SC_EX_SILENT))
+ msg = msg_cat(sp,
+ "303|File modified since last write.",
+ NULL);
+
+ /* If we're still in a vi screen, move out explicitly. */
+ (void)ex_exec_proc(sp,
+ cmdp, ap->bp, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
+ }
+
+ /*
+ * If addresses were specified, pipe lines from the file through the
+ * command.
+ *
+ * Historically, vi lines were replaced by both the stdout and stderr
+ * lines of the command, but ex lines by only the stdout lines. This
+ * makes no sense to me, so nvi makes it consistent for both, and
+ * matches vi's historic behavior.
+ */
+ else {
+ NEEDFILE(sp, cmdp);
+
+ /* Autoprint is set historically, even if the command fails. */
+ F_SET(cmdp, E_AUTOPRINT);
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. When this
+ * happens, we arrive here with two addresses of 1,1 and a
+ * bad attitude. The simple solution is to turn it into a
+ * FILTER_READ operation, with the exception that stdin isn't
+ * opened for the utility, and the cursor position isn't the
+ * same. The only historic glitch (I think) is that we don't
+ * put an empty line into the default cut buffer, as historic
+ * vi did. Imagine, if you can, my disappointment.
+ */
+ ftype = FILTER_BANG;
+ if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno == 0) {
+ cmdp->addr1.lno = cmdp->addr2.lno = 0;
+ ftype = FILTER_RBANG;
+ }
+ }
+ rval = ex_filter(sp, cmdp,
+ &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
+
+ /*
+ * If in vi mode, move to the first nonblank.
+ *
+ * !!!
+ * Historic vi wasn't consistent in this area -- if you used
+ * a forward motion it moved to the first nonblank, but if you
+ * did a backward motion it didn't. And, if you followed a
+ * backward motion with a forward motion, it wouldn't move to
+ * the nonblank for either. Going to the nonblank generally
+ * seems more useful and consistent, so we do it.
+ */
+ sp->lno = rm.lno;
+ if (F_ISSET(sp, SC_VI)) {
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ } else
+ sp->cno = rm.cno;
+ }
+
+ /* Ex terminates with a bang, even if the command fails. */
+ if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
+ (void)ex_puts(sp, "!\n");
+
+ /*
+ * XXX
+ * The ! commands never return an error, so that autoprint always
+ * happens in the ex parser.
+ */
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_cd.c b/contrib/nvi/ex/ex_cd.c
new file mode 100644
index 000000000000..3307c7b6306b
--- /dev/null
+++ b/contrib/nvi/ex/ex_cd.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_cd.c 10.10 (Berkeley) 8/12/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_cd -- :cd[!] [directory]
+ * Change directories.
+ *
+ * PUBLIC: int ex_cd __P((SCR *, EXCMD *));
+ */
+int
+ex_cd(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ struct passwd *pw;
+ ARGS *ap;
+ CHAR_T savech;
+ char *dir, *p, *t; /* XXX: END OF THE STACK, DON'T TRUST GETCWD. */
+ char buf[MAXPATHLEN * 2];
+
+ /*
+ * !!!
+ * Historic practice is that the cd isn't attempted if the file has
+ * been modified, unless its name begins with a leading '/' or the
+ * force flag is set.
+ */
+ if (F_ISSET(sp->ep, F_MODIFIED) &&
+ !FL_ISSET(cmdp->iflags, E_C_FORCE) && sp->frp->name[0] != '/') {
+ msgq(sp, M_ERR,
+ "120|File modified since last complete write; write or use ! to override");
+ return (1);
+ }
+
+ switch (cmdp->argc) {
+ case 0:
+ /* If no argument, change to the user's home directory. */
+ if ((dir = getenv("HOME")) == NULL) {
+ if ((pw = getpwuid(getuid())) == NULL ||
+ pw->pw_dir == NULL || pw->pw_dir[0] == '\0') {
+ msgq(sp, M_ERR,
+ "121|Unable to find home directory location");
+ return (1);
+ }
+ dir = pw->pw_dir;
+ }
+ break;
+ case 1:
+ dir = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * Try the current directory first. If this succeeds, don't display
+ * a message, vi didn't historically, and it should be obvious to the
+ * user where they are.
+ */
+ if (!chdir(dir))
+ return (0);
+
+ /*
+ * If moving to the user's home directory, or, the path begins with
+ * "/", "./" or "../", it's the only place we try.
+ */
+ if (cmdp->argc == 0 ||
+ (ap = cmdp->argv[0])->bp[0] == '/' ||
+ ap->len == 1 && ap->bp[0] == '.' ||
+ ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' &&
+ (ap->bp[2] == '/' || ap->bp[2] == '\0'))
+ goto err;
+
+ /* Try the O_CDPATH option values. */
+ for (p = t = O_STR(sp, O_CDPATH);; ++p)
+ if (*p == '\0' || *p == ':') {
+ /*
+ * Empty strings specify ".". The only way to get an
+ * empty string is a leading colon, colons in a row,
+ * or a trailing colon. Or, to put it the other way,
+ * if the length is 1 or less, then we're dealing with
+ * ":XXX", "XXX::XXXX" , "XXX:", or "". Since we've
+ * already tried dot, we ignore tham all.
+ */
+ if (t < p - 1) {
+ savech = *p;
+ *p = '\0';
+ (void)snprintf(buf,
+ sizeof(buf), "%s/%s", t, dir);
+ *p = savech;
+ if (!chdir(buf)) {
+ if (getcwd(buf, sizeof(buf)) != NULL)
+ msgq_str(sp, M_INFO, buf, "122|New current directory: %s");
+ return (0);
+ }
+ }
+ t = p + 1;
+ if (*p == '\0')
+ break;
+ }
+
+err: msgq_str(sp, M_SYSERR, dir, "%s");
+ return (1);
+}
diff --git a/contrib/nvi/ex/ex_cmd.c b/contrib/nvi/ex/ex_cmd.c
new file mode 100644
index 000000000000..8f7fc8da7a13
--- /dev/null
+++ b/contrib/nvi/ex/ex_cmd.c
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_cmd.c 10.20 (Berkeley) 10/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.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_SCROLL */
+ {"\004", ex_pr, E_ADDR2,
+ "",
+ "^D",
+ "scroll lines"},
+/* C_BANG */
+ {"!", ex_bang, E_ADDR2_NONE | E_SECURE,
+ "S",
+ "[line [,line]] ! command",
+ "filter lines through commands or run commands"},
+/* C_HASH */
+ {"#", ex_number, E_ADDR2|E_CLRFLAG,
+ "ca1",
+ "[line [,line]] # [count] [l]",
+ "display numbered lines"},
+/* C_SUBAGAIN */
+ {"&", ex_subagain, E_ADDR2,
+ "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,
+ "ca1",
+ "[line [,line]] <[<...] [count] [flags]",
+ "shift lines left"},
+/* C_EQUAL */
+ {"=", ex_equal, E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
+ "1",
+ "[line] = [flags]",
+ "display line number"},
+/* C_SHIFTR */
+ {">", ex_shiftr, E_ADDR2|E_AUTOPRINT,
+ "ca1",
+ "[line [,line]] >[>...] [count] [flags]",
+ "shift lines right"},
+/* C_AT */
+ {"@", ex_at, E_ADDR2,
+ "b",
+ "@ [buffer]",
+ "execute a buffer"},
+/* C_APPEND */
+ {"append", ex_append, E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
+ "!",
+ "[line] a[ppend][!]",
+ "append input to a line"},
+/* C_ABBR */
+ {"abbreviate", ex_abbr, 0,
+ "W",
+ "ab[brev] [word replace]",
+ "specify an input abbreviation"},
+/* C_ARGS */
+ {"args", ex_args, 0,
+ "",
+ "ar[gs]",
+ "display file argument list"},
+/* C_BG */
+ {"bg", ex_bg, E_VIONLY,
+ "",
+ "bg",
+ "put a foreground screen into the background"},
+/* C_CHANGE */
+ {"change", ex_change, E_ADDR2|E_ADDR_ZERODEF,
+ "!ca",
+ "[line [,line]] c[hange][!] [count]",
+ "change lines to input"},
+/* C_CD */
+ {"cd", ex_cd, 0,
+ "!f1o",
+ "cd[!] [directory]",
+ "change the current directory"},
+/* C_CHDIR */
+ {"chdir", ex_cd, 0,
+ "!f1o",
+ "chd[ir][!] [directory]",
+ "change the current directory"},
+/* C_COPY */
+ {"copy", ex_copy, E_ADDR2|E_AUTOPRINT,
+ "l1",
+ "[line [,line]] co[py] line [flags]",
+ "copy lines elsewhere in the file"},
+/* C_CSCOPE */
+ {"cscope", ex_cscope, 0,
+ "!s",
+ "cs[cope] command [args]",
+ "create a set of tags using a cscope command"},
+/*
+ * !!!
+ * Adding new commands starting with 'd' may break the delete command code
+ * in ex_cmd() (the ex parser). Read through the comments there, first.
+ */
+/* C_DELETE */
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT,
+ "bca1",
+ "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
+ "delete lines from the file"},
+/* C_DISPLAY */
+ {"display", ex_display, 0,
+ "w1r",
+ "display b[uffers] | c[onnections] | s[creens] | t[ags]",
+ "display buffers, connections, screens or tags"},
+/* C_EDIT */
+ {"edit", ex_edit, E_NEWSCREEN,
+ "f1o",
+ "[Ee][dit][!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EX */
+ {"ex", ex_edit, E_NEWSCREEN,
+ "f1o",
+ "[Ee]x[!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EXUSAGE */
+ {"exusage", ex_usage, 0,
+ "w1o",
+ "[exu]sage [command]",
+ "display ex command usage statement"},
+/* C_FILE */
+ {"file", ex_file, 0,
+ "f1o",
+ "f[ile] [name]",
+ "display (and optionally set) file name"},
+/* C_FG */
+ {"fg", ex_fg, E_NEWSCREEN|E_VIONLY,
+ "f1o",
+ "[Ff]g [file]",
+ "bring a backgrounded screen into the foreground"},
+/* C_GLOBAL */
+ {"global", ex_global, E_ADDR2_ALL,
+ "!s",
+ "[line [,line]] g[lobal][!] [;/]RE[;/] [commands]",
+ "execute a global command on lines matching an RE"},
+/* C_HELP */
+ {"help", ex_help, 0,
+ "",
+ "he[lp]",
+ "display help statement"},
+/* C_INSERT */
+ {"insert", ex_insert, E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
+ "!",
+ "[line] i[nsert][!]",
+ "insert input before a line"},
+/* C_JOIN */
+ {"join", ex_join, E_ADDR2|E_AUTOPRINT,
+ "!ca1",
+ "[line [,line]] j[oin][!] [count] [flags]",
+ "join lines into a single line"},
+/* C_K */
+ {"k", ex_mark, E_ADDR1,
+ "w1r",
+ "[line] k key",
+ "mark a line position"},
+/* C_LIST */
+ {"list", ex_list, E_ADDR2|E_CLRFLAG,
+ "ca1",
+ "[line [,line]] l[ist] [count] [#]",
+ "display lines in an unambiguous form"},
+/* C_MOVE */
+ {"move", ex_move, E_ADDR2|E_AUTOPRINT,
+ "l",
+ "[line [,line]] m[ove] line",
+ "move lines elsewhere in the file"},
+/* C_MARK */
+ {"mark", ex_mark, E_ADDR1,
+ "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, 0,
+ "!f1r",
+ "mkexrc[!] file",
+ "write a .exrc file"},
+/* C_NEXT */
+ {"next", ex_next, E_NEWSCREEN,
+ "!fN",
+ "[Nn][ext][!] [+cmd] [file ...]",
+ "edit (and optionally specify) the next file"},
+/* C_NUMBER */
+ {"number", ex_number, E_ADDR2|E_CLRFLAG,
+ "ca1",
+ "[line [,line]] nu[mber] [count] [l]",
+ "change display to number lines"},
+/* C_OPEN */
+ {"open", ex_open, E_ADDR1,
+ "s",
+ "[line] o[pen] [/RE/] [flags]",
+ "enter \"open\" mode (not implemented)"},
+/* C_PRINT */
+ {"print", ex_pr, E_ADDR2|E_CLRFLAG,
+ "ca1",
+ "[line [,line]] p[rint] [count] [#l]",
+ "display lines"},
+/* C_PERLCMD */
+ {"perl", ex_perl, E_ADDR2_ALL|E_ADDR_ZERO|
+ E_ADDR_ZERODEF|E_SECURE,
+ "s",
+ "pe[rl] cmd",
+ "run the perl interpreter with the command"},
+/* C_PERLDOCMD */
+ {"perldo", ex_perl, E_ADDR2_ALL|E_ADDR_ZERO|
+ E_ADDR_ZERODEF|E_SECURE,
+ "s",
+ "perld[o] cmd",
+ "run the perl interpreter with the command, on each line"},
+/* C_PRESERVE */
+ {"preserve", ex_preserve, 0,
+ "",
+ "pre[serve]",
+ "preserve an edit session for recovery"},
+/* C_PREVIOUS */
+ {"previous", ex_prev, E_NEWSCREEN,
+ "!",
+ "[Pp]rev[ious][!]",
+ "edit the previous file in the file argument list"},
+/* C_PUT */
+ {"put", ex_put,
+ E_ADDR1|E_AUTOPRINT|E_ADDR_ZERO|E_ADDR_ZERODEF,
+ "b",
+ "[line] pu[t] [buffer]",
+ "append a cut buffer to the line"},
+/* C_QUIT */
+ {"quit", ex_quit, 0,
+ "!",
+ "q[uit][!]",
+ "exit ex/vi"},
+/* C_READ */
+ {"read", ex_read, E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
+ "s",
+ "[line] r[ead] [!cmd | [file]]",
+ "append input from a command or file to the line"},
+/* C_RECOVER */
+ {"recover", ex_recover, 0,
+ "!f1r",
+ "recover[!] file",
+ "recover a saved file"},
+/* C_RESIZE */
+ {"resize", ex_resize, E_VIONLY,
+ "c+",
+ "resize [+-]rows",
+ "grow or shrink the current screen"},
+/* C_REWIND */
+ {"rewind", ex_rew, 0,
+ "!",
+ "rew[ind][!]",
+ "re-edit all the files in the file argument list"},
+/*
+ * !!!
+ * Adding new commands starting with 's' may break the substitute command code
+ * in ex_cmd() (the ex parser). Read through the comments there, first.
+ */
+/* C_SUBSTITUTE */
+ {"s", ex_s, E_ADDR2,
+ "s",
+ "[line [,line]] s [[/;]RE[/;]repl[/;] [cgr] [count] [#lp]]",
+ "substitute on lines matching an RE"},
+/* C_SCRIPT */
+ {"script", ex_script, E_SECURE,
+ "!f1o",
+ "sc[ript][!] [file]",
+ "run a shell in a screen"},
+/* C_SET */
+ {"set", ex_set, 0,
+ "wN",
+ "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
+ "set options (use \":set all\" to see all options)"},
+/* C_SHELL */
+ {"shell", ex_shell, E_SECURE,
+ "",
+ "sh[ell]",
+ "suspend editing and run a shell"},
+/* C_SOURCE */
+ {"source", ex_source, 0,
+ "f1r",
+ "so[urce] file",
+ "read a file of ex commands"},
+/* C_STOP */
+ {"stop", ex_stop, E_SECURE,
+ "!",
+ "st[op][!]",
+ "suspend the edit session"},
+/* C_SUSPEND */
+ {"suspend", ex_stop, E_SECURE,
+ "!",
+ "su[spend][!]",
+ "suspend the edit session"},
+/* C_T */
+ {"t", ex_copy, E_ADDR2|E_AUTOPRINT,
+ "l1",
+ "[line [,line]] t line [flags]",
+ "copy lines elsewhere in the file"},
+/* C_TAG */
+ {"tag", ex_tag_push, E_NEWSCREEN,
+ "!w1o",
+ "[Tt]a[g][!] [string]",
+ "edit the file containing the tag"},
+/* C_TAGNEXT */
+ {"tagnext", ex_tag_next, 0,
+ "!",
+ "tagn[ext][!]",
+ "move to the next tag"},
+/* C_TAGPOP */
+ {"tagpop", ex_tag_pop, 0,
+ "!w1o",
+ "tagp[op][!] [number | file]",
+ "return to the previous group of tags"},
+/* C_TAGPREV */
+ {"tagprev", ex_tag_prev, 0,
+ "!",
+ "tagpr[ev][!]",
+ "move to the previous tag"},
+/* C_TAGTOP */
+ {"tagtop", ex_tag_top, 0,
+ "!",
+ "tagt[op][!]",
+ "discard all tags"},
+/* C_TCLCMD */
+ {"tcl", ex_tcl, E_ADDR2_ALL|E_ADDR_ZERO|
+ E_ADDR_ZERODEF|E_SECURE,
+ "s",
+ "tc[l] cmd",
+ "run the tcl interpreter with the command"},
+/* C_UNDO */
+ {"undo", ex_undo, E_AUTOPRINT,
+ "",
+ "u[ndo]",
+ "undo the most recent change"},
+/* C_UNABBREVIATE */
+ {"unabbreviate",ex_unabbr, 0,
+ "w1r",
+ "una[bbrev] word",
+ "delete an abbreviation"},
+/* C_UNMAP */
+ {"unmap", ex_unmap, 0,
+ "!w1r",
+ "unm[ap][!] word",
+ "delete an input or command map"},
+/* C_V */
+ {"v", ex_v, E_ADDR2_ALL,
+ "s",
+ "[line [,line]] v [;/]RE[;/] [commands]",
+ "execute a global command on lines NOT matching an RE"},
+/* C_VERSION */
+ {"version", ex_version, 0,
+ "",
+ "version",
+ "display the program version information"},
+/* C_VISUAL_EX */
+ {"visual", ex_visual, E_ADDR1|E_ADDR_ZERODEF,
+ "2c11",
+ "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
+ "enter visual (vi) mode from ex mode"},
+/* C_VISUAL_VI */
+ {"visual", ex_edit, E_NEWSCREEN,
+ "f1o",
+ "[Vv]i[sual][!] [+cmd] [file]",
+ "edit another file (from vi mode only)"},
+/* C_VIUSAGE */
+ {"viusage", ex_viusage, 0,
+ "w1o",
+ "[viu]sage [key]",
+ "display vi key usage statement"},
+/* C_WRITE */
+ {"write", ex_write, E_ADDR2_ALL|E_ADDR_ZERODEF,
+ "!s",
+ "[line [,line]] w[rite][!] [ !cmd | [>>] [file]]",
+ "write the file"},
+/* C_WN */
+ {"wn", ex_wn, E_ADDR2_ALL|E_ADDR_ZERODEF,
+ "!s",
+ "[line [,line]] wn[!] [>>] [file]",
+ "write the file and switch to the next file"},
+/* C_WQ */
+ {"wq", ex_wq, E_ADDR2_ALL|E_ADDR_ZERODEF,
+ "!s",
+ "[line [,line]] wq[!] [>>] [file]",
+ "write the file and exit"},
+/* C_XIT */
+ {"xit", ex_xit, E_ADDR2_ALL|E_ADDR_ZERODEF,
+ "!f1o",
+ "[line [,line]] x[it][!] [file]",
+ "exit"},
+/* C_YANK */
+ {"yank", ex_yank, E_ADDR2,
+ "bca",
+ "[line [,line]] ya[nk] [buffer] [count]",
+ "copy lines to a cut buffer"},
+/* C_Z */
+ {"z", ex_z, E_ADDR1,
+ "3c01",
+ "[line] z [-|.|+|^|=] [count] [flags]",
+ "display different screens of the file"},
+/* C_SUBTILDE */
+ {"~", ex_subtilde, E_ADDR2,
+ "s",
+ "[line [,line]] ~ [cgr] [count] [#lp]",
+ "replace previous RE with previous replacement string,"},
+ {NULL},
+};
diff --git a/contrib/nvi/ex/ex_cscope.c b/contrib/nvi/ex/ex_cscope.c
new file mode 100644
index 000000000000..c2fa0a5c454b
--- /dev/null
+++ b/contrib/nvi/ex/ex_cscope.c
@@ -0,0 +1,1057 @@
+/*-
+ * Copyright (c) 1994, 1996
+ * Rob Mayoff. All rights reserved.
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_cscope.c 10.13 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "pathnames.h"
+#include "tag.h"
+
+#define CSCOPE_DBFILE "cscope.out"
+#define CSCOPE_PATHS "cscope.tpath"
+
+/*
+ * 0name find all uses of name
+ * 1name find definition of name
+ * 2name find all function calls made from name
+ * 3name find callers of name
+ * 4string find text string (cscope 12.9)
+ * 4name find assignments to name (cscope 13.3)
+ * 5pattern change pattern -- NOT USED
+ * 6pattern find pattern
+ * 7name find files with name as substring
+ * 8name find files #including name
+ */
+#define FINDHELP "\
+find c|d|e|f|g|i|s|t buffer|pattern\n\
+ c: find callers of name\n\
+ d: find all function calls made from name\n\
+ e: find pattern\n\
+ f: find files with name as substring\n\
+ g: find definition of name\n\
+ i: find files #including name\n\
+ s: find all uses of name\n\
+ t: find assignments to name"
+
+static int cscope_add __P((SCR *, EXCMD *, char *));
+static int cscope_find __P((SCR *, EXCMD*, char *));
+static int cscope_help __P((SCR *, EXCMD *, char *));
+static int cscope_kill __P((SCR *, EXCMD *, char *));
+static int cscope_reset __P((SCR *, EXCMD *, char *));
+
+typedef struct _cc {
+ char *name;
+ int (*function) __P((SCR *, EXCMD *, char *));
+ char *help_msg;
+ char *usage_msg;
+} CC;
+
+static CC const cscope_cmds[] = {
+ { "add", cscope_add,
+ "Add a new cscope database", "add file | directory" },
+ { "find", cscope_find,
+ "Query the databases for a pattern", FINDHELP },
+ { "help", cscope_help,
+ "Show help for cscope commands", "help [command]" },
+ { "kill", cscope_kill,
+ "Kill a cscope connection", "kill number" },
+ { "reset", cscope_reset,
+ "Discard all current cscope connections", "reset" },
+ { NULL }
+};
+
+static TAGQ *create_cs_cmd __P((SCR *, char *, size_t *));
+static int csc_help __P((SCR *, char *));
+static void csc_file __P((SCR *,
+ CSC *, char *, char **, size_t *, int *));
+static int get_paths __P((SCR *, CSC *));
+static CC const *lookup_ccmd __P((char *));
+static int parse __P((SCR *, CSC *, TAGQ *, int *));
+static int read_prompt __P((SCR *, CSC *));
+static int run_cscope __P((SCR *, CSC *, char *));
+static int start_cscopes __P((SCR *, EXCMD *));
+static int terminate __P((SCR *, CSC *, int));
+
+/*
+ * ex_cscope --
+ * Perform an ex cscope.
+ *
+ * PUBLIC: int ex_cscope __P((SCR *, EXCMD *));
+ */
+int
+ex_cscope(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ CC const *ccp;
+ EX_PRIVATE *exp;
+ int i;
+ char *cmd, *p;
+
+ /* Initialize the default cscope directories. */
+ exp = EXP(sp);
+ if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp))
+ return (1);
+ F_SET(exp, EXP_CSCINIT);
+
+ /* Skip leading whitespace. */
+ for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p)
+ if (!isspace(*p))
+ break;
+ if (i == 0)
+ goto usage;
+
+ /* Skip the command to any arguments. */
+ for (cmd = p; i > 0; --i, ++p)
+ if (isspace(*p))
+ break;
+ if (*p != '\0') {
+ *p++ = '\0';
+ for (; *p && isspace(*p); ++p);
+ }
+
+ if ((ccp = lookup_ccmd(cmd)) == NULL) {
+usage: msgq(sp, M_ERR, "309|Use \"cscope help\" for help");
+ return (1);
+ }
+
+ /* Call the underlying function. */
+ return (ccp->function(sp, cmdp, p));
+}
+
+/*
+ * start_cscopes --
+ * Initialize the cscope package.
+ */
+static int
+start_cscopes(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ size_t blen, len;
+ char *bp, *cscopes, *p, *t;
+
+ /*
+ * EXTENSION #1:
+ *
+ * If the CSCOPE_DIRS environment variable is set, we treat it as a
+ * list of cscope directories that we're using, similar to the tags
+ * edit option.
+ *
+ * XXX
+ * This should probably be an edit option, although that implies that
+ * we start/stop cscope processes periodically, instead of once when
+ * the editor starts.
+ */
+ if ((cscopes = getenv("CSCOPE_DIRS")) == NULL)
+ return (0);
+ len = strlen(cscopes);
+ GET_SPACE_RET(sp, bp, blen, len);
+ memcpy(bp, cscopes, len + 1);
+
+ for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;)
+ if (*p != '\0')
+ (void)cscope_add(sp, cmdp, p);
+
+ FREE_SPACE(sp, bp, blen);
+ return (0);
+}
+
+/*
+ * cscope_add --
+ * The cscope add command.
+ */
+static int
+cscope_add(sp, cmdp, dname)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *dname;
+{
+ struct stat sb;
+ EX_PRIVATE *exp;
+ CSC *csc;
+ size_t len;
+ int cur_argc;
+ char *dbname, path[MAXPATHLEN];
+
+ exp = EXP(sp);
+
+ /*
+ * 0 additional args: usage.
+ * 1 additional args: matched a file.
+ * >1 additional args: object, too many args.
+ */
+ cur_argc = cmdp->argc;
+ if (argv_exp2(sp, cmdp, dname, strlen(dname)))
+ return (1);
+ if (cmdp->argc == cur_argc) {
+ (void)csc_help(sp, "add");
+ return (1);
+ }
+ if (cmdp->argc == cur_argc + 1)
+ dname = cmdp->argv[cur_argc]->bp;
+ else {
+ ex_emsg(sp, dname, EXM_FILECOUNT);
+ return (1);
+ }
+
+ /*
+ * The user can specify a specific file (so they can have multiple
+ * Cscope databases in a single directory) or a directory. If the
+ * file doesn't exist, we're done. If it's a directory, append the
+ * standard database file name and try again. Store the directory
+ * name regardless so that we can use it as a base for searches.
+ */
+ if (stat(dname, &sb)) {
+ msgq(sp, M_SYSERR, dname);
+ return (1);
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ (void)snprintf(path, sizeof(path),
+ "%s/%s", dname, CSCOPE_DBFILE);
+ if (stat(path, &sb)) {
+ msgq(sp, M_SYSERR, path);
+ return (1);
+ }
+ dbname = CSCOPE_DBFILE;
+ } else if ((dbname = strrchr(dname, '/')) != NULL)
+ *dbname++ = '\0';
+
+ /* Allocate a cscope connection structure and initialize its fields. */
+ len = strlen(dname);
+ CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len);
+ csc->dname = csc->buf;
+ csc->dlen = len;
+ memcpy(csc->dname, dname, len);
+ csc->mtime = sb.st_mtime;
+
+ /* Get the search paths for the cscope. */
+ if (get_paths(sp, csc))
+ goto err;
+
+ /* Start the cscope process. */
+ if (run_cscope(sp, csc, dbname))
+ goto err;
+
+ /*
+ * Add the cscope connection to the screen's list. From now on,
+ * on error, we have to call terminate, which expects the csc to
+ * be on the chain.
+ */
+ LIST_INSERT_HEAD(&exp->cscq, csc, q);
+
+ /* Read the initial prompt from the cscope to make sure it's okay. */
+ if (read_prompt(sp, csc)) {
+ terminate(sp, csc, 0);
+ return (1);
+ }
+
+ return (0);
+
+err: free(csc);
+ return (1);
+}
+
+/*
+ * get_paths --
+ * Get the directories to search for the files associated with this
+ * cscope database.
+ */
+static int
+get_paths(sp, csc)
+ SCR *sp;
+ CSC *csc;
+{
+ struct stat sb;
+ int fd, nentries;
+ size_t len;
+ char *p, **pathp, buf[MAXPATHLEN * 2];
+
+ /*
+ * EXTENSION #2:
+ *
+ * If there's a cscope directory with a file named CSCOPE_PATHS, it
+ * contains a colon-separated list of paths in which to search for
+ * files returned by cscope.
+ *
+ * XXX
+ * These paths are absolute paths, and not relative to the cscope
+ * directory. To fix this, rewrite the each path using the cscope
+ * directory as a prefix.
+ */
+ (void)snprintf(buf, sizeof(buf), "%s/%s", csc->dname, CSCOPE_PATHS);
+ if (stat(buf, &sb) == 0) {
+ /* Read in the CSCOPE_PATHS file. */
+ len = sb.st_size;
+ MALLOC_RET(sp, csc->pbuf, char *, len + 1);
+ if ((fd = open(buf, O_RDONLY, 0)) < 0 ||
+ read(fd, csc->pbuf, len) != len) {
+ msgq_str(sp, M_SYSERR, buf, "%s");
+ if (fd >= 0)
+ (void)close(fd);
+ return (1);
+ }
+ (void)close(fd);
+ csc->pbuf[len] = '\0';
+
+ /* Count up the entries. */
+ for (nentries = 0, p = csc->pbuf; *p != '\0'; ++p)
+ if (p[0] == ':' && p[1] != '\0')
+ ++nentries;
+
+ /* Build an array of pointers to the paths. */
+ CALLOC_GOTO(sp,
+ csc->paths, char **, nentries + 1, sizeof(char **));
+ for (pathp = csc->paths, p = strtok(csc->pbuf, ":");
+ p != NULL; p = strtok(NULL, ":"))
+ *pathp++ = p;
+ return (0);
+ }
+
+ /*
+ * If the CSCOPE_PATHS file doesn't exist, we look for files
+ * relative to the cscope directory.
+ */
+ if ((csc->pbuf = strdup(csc->dname)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ CALLOC_GOTO(sp, csc->paths, char **, 2, sizeof(char *));
+ csc->paths[0] = csc->pbuf;
+ return (0);
+
+alloc_err:
+ if (csc->pbuf != NULL) {
+ free(csc->pbuf);
+ csc->pbuf = NULL;
+ }
+ return (1);
+}
+
+/*
+ * run_cscope --
+ * Fork off the cscope process.
+ */
+static int
+run_cscope(sp, csc, dbname)
+ SCR *sp;
+ CSC *csc;
+ char *dbname;
+{
+ int to_cs[2], from_cs[2];
+ char cmd[MAXPATHLEN * 2];
+
+ /*
+ * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
+ * from_cs[0] and writes to to_cs[1].
+ */
+ to_cs[0] = to_cs[1] = from_cs[0] = from_cs[0] = -1;
+ if (pipe(to_cs) < 0 || pipe(from_cs) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ goto err;
+ }
+ switch (csc->pid = vfork()) {
+ case -1:
+ msgq(sp, M_SYSERR, "vfork");
+err: if (to_cs[0] != -1)
+ (void)close(to_cs[0]);
+ if (to_cs[1] != -1)
+ (void)close(to_cs[1]);
+ if (from_cs[0] != -1)
+ (void)close(from_cs[0]);
+ if (from_cs[1] != -1)
+ (void)close(from_cs[1]);
+ return (1);
+ case 0: /* child: run cscope. */
+ (void)dup2(to_cs[0], STDIN_FILENO);
+ (void)dup2(from_cs[1], STDOUT_FILENO);
+ (void)dup2(from_cs[1], STDERR_FILENO);
+
+ /* Close unused file descriptors. */
+ (void)close(to_cs[1]);
+ (void)close(from_cs[0]);
+
+ /* Run the cscope command. */
+#define CSCOPE_CMD_FMT "cd '%s' && exec cscope -dl -f %s"
+ (void)snprintf(cmd, sizeof(cmd),
+ CSCOPE_CMD_FMT, csc->dname, dbname);
+ (void)execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+ msgq_str(sp, M_SYSERR, cmd, "execl: %s");
+ _exit (127);
+ /* NOTREACHED */
+ default: /* parent. */
+ /* Close unused file descriptors. */
+ (void)close(to_cs[0]);
+ (void)close(from_cs[1]);
+
+ /*
+ * Save the file descriptors for later duplication, and
+ * reopen as streams.
+ */
+ csc->to_fd = to_cs[1];
+ csc->to_fp = fdopen(to_cs[1], "w");
+ csc->from_fd = from_cs[0];
+ csc->from_fp = fdopen(from_cs[0], "r");
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cscope_find --
+ * The cscope find command.
+ */
+static int
+cscope_find(sp, cmdp, pattern)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *pattern;
+{
+ CSC *csc, *csc_next;
+ EX_PRIVATE *exp;
+ FREF *frp;
+ TAGQ *rtqp, *tqp;
+ TAG *rtp;
+ recno_t lno;
+ size_t cno, search;
+ int force, istmp, matches;
+
+ exp = EXP(sp);
+
+ /* Check for connections. */
+ if (exp->cscq.lh_first == NULL) {
+ msgq(sp, M_ERR, "310|No cscope connections running");
+ return (1);
+ }
+
+ /*
+ * Allocate all necessary memory before doing anything hard. If the
+ * tags stack is empty, we'll need the `local context' TAGQ structure
+ * later.
+ */
+ rtp = NULL;
+ rtqp = NULL;
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ /* Initialize the `local context' tag queue structure. */
+ CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
+ CIRCLEQ_INIT(&rtqp->tagq);
+
+ /* Initialize and link in its tag structure. */
+ CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
+ CIRCLEQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
+ rtqp->current = rtp;
+ }
+
+ /* Create the cscope command. */
+ if ((tqp = create_cs_cmd(sp, pattern, &search)) == NULL)
+ goto err;
+
+ /*
+ * Stick the current context in a convenient place, we'll lose it
+ * when we switch files.
+ */
+ frp = sp->frp;
+ lno = sp->lno;
+ cno = sp->cno;
+ istmp = F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN);
+
+ /* Search all open connections for a match. */
+ matches = 0;
+ for (csc = exp->cscq.lh_first; csc != NULL; csc = csc_next) {
+ /* Copy csc->q.lh_next here in case csc is killed. */
+ csc_next = csc->q.le_next;
+
+ /*
+ * Send the command to the cscope program. (We skip the
+ * first two bytes of the command, because we stored the
+ * search cscope command character and a leading space
+ * there.)
+ */
+ (void)fprintf(csc->to_fp, "%d%s\n", search, tqp->tag + 2);
+ (void)fflush(csc->to_fp);
+
+ /* Read the output. */
+ if (parse(sp, csc, tqp, &matches)) {
+ if (rtqp != NULL)
+ free(rtqp);
+ tagq_free(sp, tqp);
+ return (1);
+ }
+ }
+
+ if (matches == 0) {
+ msgq(sp, M_INFO, "278|No matches for query");
+ return (0);
+ }
+
+ tqp->current = tqp->tagq.cqh_first;
+
+ /* Try to switch to the first tag. */
+ force = FL_ISSET(cmdp->iflags, E_C_FORCE);
+ if (F_ISSET(cmdp, E_NEWSCREEN)) {
+ if (ex_tag_Nswitch(sp, tqp->current, force))
+ goto err;
+
+ /* Everything else gets done in the new screen. */
+ sp = sp->nextdisp;
+ exp = EXP(sp);
+ } else
+ if (ex_tag_nswitch(sp, tqp->current, force))
+ goto err;
+
+ /*
+ * If this is the first tag, put a `current location' queue entry
+ * in place, so we can pop all the way back to the current mark.
+ * Note, it doesn't point to much of anything, it's a placeholder.
+ */
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ CIRCLEQ_INSERT_HEAD(&exp->tq, rtqp, q);
+ } else
+ rtqp = exp->tq.cqh_first;
+
+ /* Link the current TAGQ structure into place. */
+ CIRCLEQ_INSERT_HEAD(&exp->tq, tqp, q);
+
+ (void)cscope_search(sp, tqp, tqp->current);
+
+ /*
+ * Move the current context from the temporary save area into the
+ * right structure.
+ *
+ * If we were in a temporary file, we don't have a context to which
+ * we can return, so just make it be the same as what we're moving
+ * to. It will be a little odd that ^T doesn't change anything, but
+ * I don't think it's a big deal.
+ */
+ if (istmp) {
+ rtqp->current->frp = sp->frp;
+ rtqp->current->lno = sp->lno;
+ rtqp->current->cno = sp->cno;
+ } else {
+ rtqp->current->frp = frp;
+ rtqp->current->lno = lno;
+ rtqp->current->cno = cno;
+ }
+
+ return (0);
+
+err:
+alloc_err:
+ if (rtqp != NULL)
+ free(rtqp);
+ if (rtp != NULL)
+ free(rtp);
+ return (1);
+}
+
+/*
+ * create_cs_cmd --
+ * Build a cscope command, creating and initializing the base TAGQ.
+ */
+static TAGQ *
+create_cs_cmd(sp, pattern, searchp)
+ SCR *sp;
+ char *pattern;
+ size_t *searchp;
+{
+ CB *cbp;
+ TAGQ *tqp;
+ size_t tlen;
+ char *p;
+
+ /*
+ * Cscope supports a "change pattern" command which we never use,
+ * cscope command 5. Set CSCOPE_QUERIES[5] to " " since the user
+ * can't pass " " as the first character of pattern. That way the
+ * user can't ask for pattern 5 so we don't need any special-case
+ * code.
+ */
+#define CSCOPE_QUERIES "sgdct efi"
+
+ if (pattern == NULL)
+ goto usage;
+
+ /* Skip leading blanks, check for command character. */
+ for (; isblank(pattern[0]); ++pattern);
+ if (pattern[0] == '\0' || !isblank(pattern[1]))
+ goto usage;
+ for (*searchp = 0, p = CSCOPE_QUERIES;
+ *p != '\0' && *p != pattern[0]; ++*searchp, ++p);
+ if (*p == '\0') {
+ msgq(sp, M_ERR,
+ "311|%s: unknown search type: use one of %s",
+ KEY_NAME(sp, pattern[0]), CSCOPE_QUERIES);
+ return (NULL);
+ }
+
+ /* Skip <blank> characters to the pattern. */
+ for (p = pattern + 1; *p != '\0' && isblank(*p); ++p);
+ if (*p == '\0') {
+usage: (void)csc_help(sp, "find");
+ return (NULL);
+ }
+
+ /* The user can specify the contents of a buffer as the pattern. */
+ cbp = NULL;
+ if (p[0] == '"' && p[1] != '\0' && p[2] == '\0')
+ CBNAME(sp, cbp, p[1]);
+ if (cbp != NULL) {
+ p = cbp->textq.cqh_first->lb;
+ tlen = cbp->textq.cqh_first->len;
+ } else
+ tlen = strlen(p);
+
+ /* Allocate and initialize the TAGQ structure. */
+ CALLOC(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + tlen + 3);
+ if (tqp == NULL)
+ return (NULL);
+ CIRCLEQ_INIT(&tqp->tagq);
+ tqp->tag = tqp->buf;
+ tqp->tag[0] = pattern[0];
+ tqp->tag[1] = ' ';
+ tqp->tlen = tlen + 2;
+ memcpy(tqp->tag + 2, p, tlen);
+ tqp->tag[tlen + 2] = '\0';
+ F_SET(tqp, TAG_CSCOPE);
+
+ return (tqp);
+}
+
+/*
+ * parse --
+ * Parse the cscope output.
+ */
+static int
+parse(sp, csc, tqp, matchesp)
+ SCR *sp;
+ CSC *csc;
+ TAGQ *tqp;
+ int *matchesp;
+{
+ TAG *tp;
+ recno_t slno;
+ size_t dlen, nlen, slen;
+ int ch, i, isolder, nlines;
+ char *dname, *name, *search, *p, *t, dummy[2], buf[2048];
+
+ for (;;) {
+ if (!fgets(buf, sizeof(buf), csc->from_fp))
+ goto io_err;
+
+ /*
+ * If the database is out of date, or there's some other
+ * problem, cscope will output error messages before the
+ * number-of-lines output. Display/discard any output
+ * that doesn't match what we want.
+ */
+#define CSCOPE_NLINES_FMT "cscope: %d lines%1[\n]"
+ if (sscanf(buf, CSCOPE_NLINES_FMT, &nlines, dummy) == 2)
+ break;
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+ msgq(sp, M_ERR, "%s: \"%s\"", csc->dname, buf);
+ }
+
+ while (nlines--) {
+ if (fgets(buf, sizeof(buf), csc->from_fp) == NULL)
+ goto io_err;
+
+ /* If the line's too long for the buffer, discard it. */
+ if ((p = strchr(buf, '\n')) == NULL) {
+ while ((ch = getc(csc->from_fp)) != EOF && ch != '\n');
+ continue;
+ }
+ *p = '\0';
+
+ /*
+ * The cscope output is in the following format:
+ *
+ * <filename> <context> <line number> <pattern>
+ *
+ * Figure out how long everything is so we can allocate in one
+ * swell foop, but discard anything that looks wrong.
+ */
+ for (p = buf, i = 0;
+ i < 3 && (t = strsep(&p, "\t ")) != NULL; ++i)
+ switch (i) {
+ case 0: /* Filename. */
+ name = t;
+ nlen = strlen(name);
+ break;
+ case 1: /* Context. */
+ break;
+ case 2: /* Line number. */
+ slno = (recno_t)atol(t);
+ break;
+ }
+ if (i != 3 || p == NULL || t == NULL)
+ continue;
+
+ /* The rest of the string is the search pattern. */
+ search = p;
+ slen = strlen(p);
+
+ /* Resolve the file name. */
+ csc_file(sp, csc, name, &dname, &dlen, &isolder);
+
+ /*
+ * If the file is older than the cscope database, that is,
+ * the database was built since the file was last modified,
+ * or there wasn't a search string, use the line number.
+ */
+ if (isolder || strcmp(search, "<unknown>") == 0) {
+ search = NULL;
+ slen = 0;
+ }
+
+ /*
+ * Allocate and initialize a tag structure plus the variable
+ * length cscope information that follows it.
+ */
+ CALLOC_RET(sp, tp,
+ TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1);
+ tp->fname = tp->buf;
+ if (dlen != 0) {
+ memcpy(tp->fname, dname, dlen);
+ tp->fname[dlen] = '/';
+ ++dlen;
+ }
+ memcpy(tp->fname + dlen, name, nlen + 1);
+ tp->fnlen = dlen + nlen;
+ tp->slno = slno;
+ if (slen != 0) {
+ tp->search = tp->fname + tp->fnlen + 1;
+ memcpy(tp->search, search, (tp->slen = slen) + 1);
+ }
+ CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
+
+ ++*matchesp;
+ }
+
+ (void)read_prompt(sp, csc);
+ return (0);
+
+io_err: if (feof(csc->from_fp))
+ errno = EIO;
+ msgq_str(sp, M_SYSERR, "%s", csc->dname);
+ terminate(sp, csc, 0);
+ return (1);
+}
+
+/*
+ * csc_file --
+ * Search for the right path to this file.
+ */
+static void
+csc_file(sp, csc, name, dirp, dlenp, isolderp)
+ SCR *sp;
+ CSC *csc;
+ char *name, **dirp;
+ size_t *dlenp;
+ int *isolderp;
+{
+ struct stat sb;
+ char **pp, buf[MAXPATHLEN];
+
+ /*
+ * Check for the file in all of the listed paths. If we don't
+ * find it, we simply return it unchanged. We have to do this
+ * now, even though it's expensive, because if the user changes
+ * directories, we can't change our minds as to where the file
+ * lives.
+ */
+ for (pp = csc->paths; *pp != NULL; ++pp) {
+ (void)snprintf(buf, sizeof(buf), "%s/%s", *pp, name);
+ if (stat(buf, &sb) == 0) {
+ *dirp = *pp;
+ *dlenp = strlen(*pp);
+ *isolderp = sb.st_mtime < csc->mtime;
+ return;
+ }
+ }
+ *dlenp = 0;
+}
+
+/*
+ * cscope_help --
+ * The cscope help command.
+ */
+static int
+cscope_help(sp, cmdp, subcmd)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *subcmd;
+{
+ return (csc_help(sp, subcmd));
+}
+
+/*
+ * csc_help --
+ * Display help/usage messages.
+ */
+static int
+csc_help(sp, cmd)
+ SCR *sp;
+ char *cmd;
+{
+ CC const *ccp;
+
+ if (cmd != NULL && *cmd != '\0')
+ if ((ccp = lookup_ccmd(cmd)) == NULL) {
+ ex_printf(sp,
+ "%s doesn't match any cscope command\n", cmd);
+ return (1);
+ } else {
+ ex_printf(sp,
+ "Command: %s (%s)\n", ccp->name, ccp->help_msg);
+ ex_printf(sp, " Usage: %s\n", ccp->usage_msg);
+ return (0);
+ }
+
+ ex_printf(sp, "cscope commands:\n");
+ for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
+ ex_printf(sp, " %*s: %s\n", 5, ccp->name, ccp->help_msg);
+ return (0);
+}
+
+/*
+ * cscope_kill --
+ * The cscope kill command.
+ */
+static int
+cscope_kill(sp, cmdp, cn)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *cn;
+{
+ return (terminate(sp, NULL, atoi(cn)));
+}
+
+/*
+ * terminate --
+ * Detach from a cscope process.
+ */
+static int
+terminate(sp, csc, n)
+ SCR *sp;
+ CSC *csc;
+ int n;
+{
+ EX_PRIVATE *exp;
+ int i, pstat;
+
+ exp = EXP(sp);
+
+ /*
+ * We either get a csc structure or a number. If not provided a
+ * csc structure, find the right one.
+ */
+ if (csc == NULL) {
+ if (n < 1)
+ goto badno;
+ for (i = 1, csc = exp->cscq.lh_first;
+ csc != NULL; csc = csc->q.le_next, i++)
+ if (i == n)
+ break;
+ if (csc == NULL) {
+badno: msgq(sp, M_ERR, "312|%d: no such cscope session", n);
+ return (1);
+ }
+ }
+
+ /*
+ * XXX
+ * Theoretically, we have the only file descriptors to the process,
+ * so closing them should let it exit gracefully, deleting temporary
+ * files, etc. The original vi cscope integration sent the cscope
+ * connection a SIGTERM signal, so I'm not sure if closing the file
+ * descriptors is sufficient.
+ */
+ if (csc->from_fp != NULL)
+ (void)fclose(csc->from_fp);
+ if (csc->to_fp != NULL)
+ (void)fclose(csc->to_fp);
+ (void)waitpid(csc->pid, &pstat, 0);
+
+ /* Discard cscope connection information. */
+ LIST_REMOVE(csc, q);
+ if (csc->pbuf != NULL)
+ free(csc->pbuf);
+ if (csc->paths != NULL)
+ free(csc->paths);
+ free(csc);
+ return (0);
+}
+
+/*
+ * cscope_reset --
+ * The cscope reset command.
+ */
+static int
+cscope_reset(sp, cmdp, notusedp)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *notusedp;
+{
+ EX_PRIVATE *exp;
+
+ for (exp = EXP(sp); exp->cscq.lh_first != NULL;)
+ if (cscope_kill(sp, cmdp, "1"))
+ return (1);
+ return (0);
+}
+
+/*
+ * cscope_display --
+ * Display current connections.
+ *
+ * PUBLIC: int cscope_display __P((SCR *));
+ */
+int
+cscope_display(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ CSC *csc;
+ int i;
+
+ exp = EXP(sp);
+ if (exp->cscq.lh_first == NULL) {
+ ex_printf(sp, "No cscope connections.\n");
+ return (0);
+ }
+ for (i = 1,
+ csc = exp->cscq.lh_first; csc != NULL; ++i, csc = csc->q.le_next)
+ ex_printf(sp,
+ "%2d %s (process %lu)\n", i, csc->dname, (u_long)csc->pid);
+ return (0);
+}
+
+/*
+ * cscope_search --
+ * Search a file for a cscope entry.
+ *
+ * PUBLIC: int cscope_search __P((SCR *, TAGQ *, TAG *));
+ */
+int
+cscope_search(sp, tqp, tp)
+ SCR *sp;
+ TAGQ *tqp;
+ TAG *tp;
+{
+ MARK m;
+
+ /* If we don't have a search pattern, use the line number. */
+ if (tp->search == NULL) {
+ if (!db_exist(sp, tp->slno)) {
+ tag_msg(sp, TAG_BADLNO, tqp->tag);
+ return (1);
+ }
+ m.lno = tp->slno;
+ } 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;
+ if (f_search(sp, &m, &m,
+ tp->search, tp->slen, NULL, SEARCH_CSCOPE | SEARCH_FILE)) {
+ tag_msg(sp, TAG_SEARCH, tqp->tag);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically, tags set the search direction if it wasn't
+ * already set.
+ */
+ if (sp->searchdir == NOTSET)
+ sp->searchdir = FORWARD;
+ }
+
+ /*
+ * !!!
+ * Tags move to the first non-blank, NOT the search pattern start.
+ */
+ sp->lno = m.lno;
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ return (0);
+}
+
+
+/*
+ * lookup_ccmd --
+ * Return a pointer to the command structure.
+ */
+static CC const *
+lookup_ccmd(name)
+ char *name;
+{
+ CC const *ccp;
+ size_t len;
+
+ len = strlen(name);
+ for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
+ if (strncmp(name, ccp->name, len) == 0)
+ return (ccp);
+ return (NULL);
+}
+
+/*
+ * read_prompt --
+ * Read a prompt from cscope.
+ */
+static int
+read_prompt(sp, csc)
+ SCR *sp;
+ CSC *csc;
+{
+ int ch;
+
+#define CSCOPE_PROMPT ">> "
+ for (;;) {
+ while ((ch =
+ getc(csc->from_fp)) != EOF && ch != CSCOPE_PROMPT[0]);
+ if (ch == EOF) {
+ terminate(sp, csc, 0);
+ return (1);
+ }
+ if (getc(csc->from_fp) != CSCOPE_PROMPT[1])
+ continue;
+ if (getc(csc->from_fp) != CSCOPE_PROMPT[2])
+ continue;
+ break;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_delete.c b/contrib/nvi/ex/ex_delete.c
new file mode 100644
index 000000000000..58734bbcfce4
--- /dev/null
+++ b/contrib/nvi/ex/ex_delete.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_delete.c 10.9 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
+ *
+ * Delete lines from the file.
+ *
+ * PUBLIC: int ex_delete __P((SCR *, EXCMD *));
+ */
+int
+ex_delete(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ recno_t lno;
+
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * !!!
+ * Historically, lines deleted in ex were not placed in the numeric
+ * buffers. We follow historic practice so that we don't overwrite
+ * vi buffers accidentally.
+ */
+ if (cut(sp,
+ FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
+ return (1);
+
+ /* Delete the lines. */
+ if (del(sp, &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 (db_last(sp, &lno))
+ return (1);
+ if (sp->lno > lno)
+ sp->lno = lno;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_display.c b/contrib/nvi/ex/ex_display.c
new file mode 100644
index 000000000000..75315170f95e
--- /dev/null
+++ b/contrib/nvi/ex/ex_display.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_display.c 10.12 (Berkeley) 4/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "tag.h"
+
+static int bdisplay __P((SCR *));
+static void db __P((SCR *, CB *, CHAR_T *));
+
+/*
+ * ex_display -- :display b[uffers] | c[onnections] | s[creens] | t[ags]
+ *
+ * Display cscope connections, buffers, tags or screens.
+ *
+ * PUBLIC: int ex_display __P((SCR *, EXCMD *));
+ */
+int
+ex_display(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ switch (cmdp->argv[0]->bp[0]) {
+ case 'b':
+#undef ARG
+#define ARG "buffers"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (bdisplay(sp));
+ case 'c':
+#undef ARG
+#define ARG "connections"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (cscope_display(sp));
+ case 's':
+#undef ARG
+#define ARG "screens"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_sdisplay(sp));
+ case 't':
+#undef ARG
+#define ARG "tags"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_tag_display(sp));
+ }
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+}
+
+/*
+ * bdisplay --
+ *
+ * Display buffers.
+ */
+static int
+bdisplay(sp)
+ SCR *sp;
+{
+ CB *cbp;
+
+ if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) {
+ msgq(sp, M_INFO, "123|No cut buffers to display");
+ return (0);
+ }
+
+ /* 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 (INTERRUPTED(sp))
+ 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 (INTERRUPTED(sp))
+ 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_T *name;
+{
+ CHAR_T *p;
+ GS *gp;
+ TEXT *tp;
+ size_t len;
+
+ gp = sp->gp;
+ (void)ex_printf(sp, "********** %s%s\n",
+ name == NULL ? KEY_NAME(sp, cbp->name) : name,
+ F_ISSET(cbp, CB_LMODE) ? " (line mode)" : " (character mode)");
+ for (tp = cbp->textq.cqh_first;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
+ for (len = tp->len, p = tp->lb; len--; ++p) {
+ (void)ex_puts(sp, KEY_NAME(sp, *p));
+ if (INTERRUPTED(sp))
+ return;
+ }
+ (void)ex_puts(sp, "\n");
+ }
+}
diff --git a/contrib/nvi/ex/ex_edit.c b/contrib/nvi/ex/ex_edit.c
new file mode 100644
index 000000000000..8b18e0f0bbca
--- /dev/null
+++ b/contrib/nvi/ex/ex_edit.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_edit.c 10.10 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+static int ex_N_edit __P((SCR *, EXCMD *, FREF *, int));
+
+/*
+ * ex_edit -- :e[dit][!] [+cmd] [file]
+ * :ex[!] [+cmd] [file]
+ * :vi[sual][!] [+cmd] [file]
+ *
+ * Edit a file; if none specified, re-edit the current file. The third
+ * 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. This seems unreasonable, so we support it
+ * regardless.
+ *
+ * PUBLIC: int ex_edit __P((SCR *, EXCMD *));
+ */
+int
+ex_edit(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ FREF *frp;
+ int attach, setalt;
+
+ 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
+ * (or wasn't editing any file), create another one. The
+ * reason for not reusing temporary files is that there is
+ * special exit processing of them, and reuse is tricky.
+ */
+ frp = sp->frp;
+ if (sp->ep == NULL || F_ISSET(frp, FR_TMPFILE)) {
+ if ((frp = file_add(sp, NULL)) == NULL)
+ return (1);
+ attach = 0;
+ } else
+ attach = 1;
+ setalt = 0;
+ break;
+ case 1:
+ if ((frp = file_add(sp, cmdp->argv[0]->bp)) == NULL)
+ return (1);
+ attach = 0;
+ setalt = 1;
+ set_alt_name(sp, cmdp->argv[0]->bp);
+ break;
+ default:
+ abort();
+ }
+
+ if (F_ISSET(cmdp, E_NEWSCREEN))
+ return (ex_N_edit(sp, cmdp, frp, attach));
+
+ /*
+ * Check for modifications.
+ *
+ * !!!
+ * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit.
+ */
+ if (file_m2(sp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
+ return (1);
+
+ /* Switch files. */
+ if (file_init(sp, frp, NULL, (setalt ? FS_SETALT : 0) |
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
+ return (1);
+
+ F_SET(sp, SC_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_N_edit --
+ * New screen version of ex_edit.
+ */
+static int
+ex_N_edit(sp, cmdp, frp, attach)
+ SCR *sp;
+ EXCMD *cmdp;
+ FREF *frp;
+ int attach;
+{
+ SCR *new;
+
+ /* Get a new screen. */
+ if (screen_init(sp->gp, sp, &new))
+ return (1);
+ if (vs_split(sp, new, 0)) {
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Get a backing file. */
+ if (attach) {
+ /* Copy file state, keep the screen and cursor the same. */
+ new->ep = sp->ep;
+ ++new->ep->refcnt;
+
+ new->frp = frp;
+ new->frp->flags = sp->frp->flags;
+
+ new->lno = sp->lno;
+ new->cno = sp->cno;
+ } else if (file_init(new, frp, NULL,
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0))) {
+ (void)vs_discard(new, NULL);
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Create the argument list. */
+ new->cargv = new->argv = ex_buildargv(sp, NULL, frp->name);
+
+ /* Set up the switch. */
+ sp->nextdisp = new;
+ F_SET(sp, SC_SSWITCH);
+
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_equal.c b/contrib/nvi/ex/ex_equal.c
new file mode 100644
index 000000000000..565df66bd0a9
--- /dev/null
+++ b/contrib/nvi/ex/ex_equal.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_equal.c 10.10 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_equal -- :address =
+ *
+ * PUBLIC: int ex_equal __P((SCR *, EXCMD *));
+ */
+int
+ex_equal(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ recno_t lno;
+
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * Print out the line number matching the specified address,
+ * or the number of the last line in the file if no address
+ * specified.
+ *
+ * !!!
+ * Historically, ":0=" displayed 0, and ":=" or ":1=" in an
+ * empty file displayed 1. Until somebody complains loudly,
+ * we're going to do it right. The tables in excmd.c permit
+ * lno to get away with any address from 0 to the end of the
+ * file, which, in an empty file, is 0.
+ */
+ if (F_ISSET(cmdp, E_ADDR_DEF)) {
+ if (db_last(sp, &lno))
+ return (1);
+ } else
+ lno = cmdp->addr1.lno;
+
+ (void)ex_printf(sp, "%ld\n", lno);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_file.c b/contrib/nvi/ex/ex_file.c
new file mode 100644
index 000000000000..3492f9c7bf0d
--- /dev/null
+++ b/contrib/nvi/ex/ex_file.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_file.c 10.12 (Berkeley) 7/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_file -- :f[ile] [name]
+ * Change the file's name and display the status line.
+ *
+ * PUBLIC: int ex_file __P((SCR *, EXCMD *));
+ */
+int
+ex_file(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ CHAR_T *p;
+ FREF *frp;
+
+ NEEDFILE(sp, cmdp);
+
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ frp = sp->frp;
+
+ /* Make sure can allocate enough space. */
+ if ((p = v_strdup(sp,
+ cmdp->argv[0]->bp, cmdp->argv[0]->len)) == NULL)
+ return (1);
+
+ /* If already have a file name, it becomes the alternate. */
+ if (!F_ISSET(frp, FR_TMPFILE))
+ set_alt_name(sp, frp->name);
+
+ /* Free the previous name. */
+ free(frp->name);
+ frp->name = p;
+
+ /*
+ * The file has a real name, it's no longer a temporary,
+ * clear the temporary file flags.
+ */
+ F_CLR(frp, FR_TMPEXIT | FR_TMPFILE);
+
+ /* Have to force a write if the file exists, next time. */
+ F_SET(frp, FR_NAMECHANGE);
+
+ /* Notify the screen. */
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+ break;
+ default:
+ abort();
+ }
+ msgq_status(sp, sp->lno, MSTAT_SHOWLAST);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_filter.c b/contrib/nvi/ex/ex_filter.c
new file mode 100644
index 000000000000..2e86e58b3ba2
--- /dev/null
+++ b/contrib/nvi/ex/ex_filter.c
@@ -0,0 +1,316 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_filter.c 10.34 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+static int filter_ldisplay __P((SCR *, FILE *));
+
+/*
+ * ex_filter --
+ * Run a range of lines through a filter utility and optionally
+ * replace the original text with the stdout/stderr output of
+ * the utility.
+ *
+ * PUBLIC: int ex_filter __P((SCR *,
+ * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, char *, enum filtertype));
+ */
+int
+ex_filter(sp, cmdp, fm, tm, rp, cmd, ftype)
+ SCR *sp;
+ EXCMD *cmdp;
+ MARK *fm, *tm, *rp;
+ char *cmd;
+ enum filtertype ftype;
+{
+ FILE *ifp, *ofp;
+ pid_t parent_writer_pid, utility_pid;
+ recno_t nread;
+ int input[2], output[2], rval;
+ char *name;
+
+ rval = 0;
+
+ /* Set return cursor position, which is never less than line 1. */
+ *rp = *fm;
+ if (rp->lno == 0)
+ rp->lno = 1;
+
+ /* We're going to need a shell. */
+ if (opts_empty(sp, O_SHELL, 0))
+ return (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].
+ *
+ * !!!
+ * Historically, in the FILTER_READ case, the utility reads from
+ * the terminal (e.g. :r! cat works). Otherwise open up utility
+ * input pipe.
+ */
+ ofp = NULL;
+ input[0] = input[1] = output[0] = output[1] = -1;
+ if (ftype != FILTER_READ && pipe(input) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ 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;
+ }
+
+ /* 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 (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]);
+ return (1);
+ case 0: /* Utility. */
+ /*
+ * Redirect stdin from the read end of the input pipe, and
+ * redirect stdout/stderr to the write end of the output pipe.
+ *
+ * !!!
+ * Historically, ex only directed stdout into the input pipe,
+ * letting stderr come out on the terminal as usual. Vi did
+ * not, directing both stdout and stderr into the input pipe.
+ * We match that practice in both ex and vi for consistency.
+ */
+ if (input[0] != -1)
+ (void)dup2(input[0], STDIN_FILENO);
+ (void)dup2(output[1], STDOUT_FILENO);
+ (void)dup2(output[1], STDERR_FILENO);
+
+ /* Close the utility's file descriptors. */
+ if (input[0] != -1)
+ (void)close(input[0]);
+ if (input[1] != -1)
+ (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_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
+ _exit (127);
+ /* NOTREACHED */
+ default: /* Parent-reader, parent-writer. */
+ /* Close the pipe ends neither parent will use. */
+ if (input[0] != -1)
+ (void)close(input[0]);
+ (void)close(output[1]);
+ break;
+ }
+
+ /*
+ * FILTER_RBANG, 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.
+ *
+ * For FILTER_RBANG, there is nothing to write to the utility.
+ * Make sure it doesn't wait forever by closing its standard
+ * input.
+ *
+ * !!!
+ * Set the return cursor to the last line read in for FILTER_READ.
+ * 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_RBANG || ftype == FILTER_READ) {
+ if (ftype == FILTER_RBANG)
+ (void)close(input[1]);
+
+ if (ex_readfp(sp, "filter", ofp, fm, &nread, 1))
+ rval = 1;
+ sp->rptlines[L_ADDED] += nread;
+ if (ftype == FILTER_READ)
+ if (fm->lno == 0)
+ rp->lno = nread;
+ else
+ rp->lno += nread;
+ goto uwait;
+ }
+
+ /*
+ * FILTER_BANG, 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_BANG:
+ * 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:db_get() code, based on the flag set
+ * here.
+ */
+ F_SET(sp->ep, F_MULTILOCK);
+ switch (parent_writer_pid = fork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "fork");
+ (void)close(input[1]);
+ (void)close(output[0]);
+ rval = 1;
+ break;
+ case 0: /* Parent-writer. */
+ /*
+ * Write the selected lines to the write end of the input
+ * pipe. This instance of ifp is closed by ex_writefp.
+ */
+ (void)close(output[0]);
+ if ((ifp = fdopen(input[1], "w")) == NULL)
+ _exit (1);
+ _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1));
+
+ /* 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.
+ */
+ if (filter_ldisplay(sp, ofp))
+ rval = 1;
+ } else {
+ /*
+ * Read the output from the read end of the output
+ * pipe. Ex_readfp appends to the MARK and closes
+ * ofp.
+ */
+ if (ex_readfp(sp, "filter", ofp, tm, &nread, 1))
+ rval = 1;
+ sp->rptlines[L_ADDED] += nread;
+ }
+
+ /* Wait for the parent-writer. */
+ if (proc_wait(sp,
+ (long)parent_writer_pid, "parent-writer", 0, 1))
+ rval = 1;
+
+ /* Delete any lines written to the utility. */
+ if (rval == 0 && ftype == FILTER_BANG &&
+ (cut(sp, NULL, fm, tm, CUT_LINEMODE) ||
+ del(sp, fm, tm, 1))) {
+ rval = 1;
+ break;
+ }
+
+ /*
+ * 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 && !db_exist(sp, rp->lno))
+ --rp->lno;
+ break;
+ }
+ F_CLR(sp->ep, F_MULTILOCK);
+
+ /*
+ * !!!
+ * Ignore errors on vi file reads, to make reads prettier. It's
+ * completely inconsistent, and historic practice.
+ */
+uwait: return (proc_wait(sp, (long)utility_pid, cmd,
+ ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval);
+}
+
+/*
+ * filter_ldisplay --
+ * Display output from a utility.
+ *
+ * !!!
+ * Historically, the characters were passed unmodified to the terminal.
+ * We use the ex print routines to make sure they're printable.
+ */
+static int
+filter_ldisplay(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ size_t len;
+
+ EX_PRIVATE *exp;
+
+ for (exp = EXP(sp); !ex_getline(sp, fp, &len) && !INTERRUPTED(sp);)
+ if (ex_ldisplay(sp, exp->ibp, len, 0, 0))
+ break;
+ if (ferror(fp))
+ msgq(sp, M_SYSERR, "filter read");
+ (void)fclose(fp);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_global.c b/contrib/nvi/ex/ex_global.c
new file mode 100644
index 000000000000..aba9dc5fabe3
--- /dev/null
+++ b/contrib/nvi/ex/ex_global.c
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_global.c 10.22 (Berkeley) 10/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+enum which {GLOBAL, V};
+
+static int ex_g_setup __P((SCR *, EXCMD *, enum which));
+
+/*
+ * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
+ * Exec on lines matching a pattern.
+ *
+ * PUBLIC: int ex_global __P((SCR *, EXCMD *));
+ */
+int
+ex_global(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_g_setup(sp,
+ cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
+}
+
+/*
+ * ex_v -- [line [,line]] v /pattern/ [commands]
+ * Exec on lines not matching a pattern.
+ *
+ * PUBLIC: int ex_v __P((SCR *, EXCMD *));
+ */
+int
+ex_v(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (ex_g_setup(sp, cmdp, V));
+}
+
+/*
+ * ex_g_setup --
+ * Ex global and v commands.
+ */
+static int
+ex_g_setup(sp, cmdp, cmd)
+ SCR *sp;
+ EXCMD *cmdp;
+ enum which cmd;
+{
+ CHAR_T *ptrn, *p, *t;
+ EXCMD *ecp;
+ MARK abs;
+ RANGE *rp;
+ busy_t btype;
+ recno_t start, end;
+ regex_t *re;
+ regmatch_t match[1];
+ size_t len;
+ int cnt, delim, eval;
+ char *dbp;
+
+ NEEDFILE(sp, cmdp);
+
+ if (F_ISSET(sp, SC_EX_GLOBAL)) {
+ msgq(sp, M_ERR,
+ "124|The %s command can't be used as part of a global or v command",
+ cmdp->cmd->name);
+ return (1);
+ }
+
+ /*
+ * Skip leading white space. Historic vi allowed any non-alphanumeric
+ * to serve as the global command delimiter.
+ */
+ if (cmdp->argc == 0)
+ goto usage;
+ for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
+ if (*p == '\0' || isalnum(*p) ||
+ *p == '\\' || *p == '|' || *p == '\n') {
+usage: ex_emsg(sp, cmdp->cmd->usage, EXM_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] == '\\')
+ if (p[1] == delim)
+ ++p;
+ else if (p[1] == '\\')
+ *t++ = *p++;
+ *t++ = *p++;
+ }
+
+ /* If the pattern string is empty, use the last one. */
+ if (*ptrn == '\0') {
+ if (sp->re == NULL) {
+ ex_emsg(sp, NULL, EXM_NOPREVRE);
+ return (1);
+ }
+
+ /* Re-compile the RE if necessary. */
+ if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
+ sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
+ return (1);
+ } else {
+ /* Compile the RE. */
+ if (re_compile(sp, ptrn, t - ptrn,
+ &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH))
+ return (1);
+
+ /*
+ * Set saved RE. Historic practice is that globals set
+ * direction as well as the RE.
+ */
+ sp->searchdir = FORWARD;
+ }
+ re = &sp->re_c;
+
+ /* The global commands always set the previous context mark. */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ if (mark_set(sp, ABSMARK1, &abs, 1))
+ return (1);
+
+ /* Get an EXCMD structure. */
+ CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
+ CIRCLEQ_INIT(&ecp->rq);
+
+ /*
+ * Get a copy of the command string; the default command is print.
+ * Don't worry about a set of <blank>s with no command, that will
+ * default to print in the ex parser. We need to have two copies
+ * because the ex parser may step on the command string when it's
+ * parsing it.
+ */
+ if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
+ p = "pp";
+ len = 1;
+ }
+
+ MALLOC_RET(sp, ecp->cp, char *, len * 2);
+ ecp->o_cp = ecp->cp;
+ ecp->o_clen = len;
+ memcpy(ecp->cp + len, p, len);
+ ecp->range_lno = OOBLNO;
+ FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
+ LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q);
+
+ /*
+ * 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.
+ */
+ btype = BUSY_ON;
+ cnt = INTERRUPT_CHECK;
+ for (start = cmdp->addr1.lno,
+ end = cmdp->addr2.lno; start <= end; ++start) {
+ if (cnt-- == 0) {
+ if (INTERRUPTED(sp)) {
+ LIST_REMOVE(ecp, q);
+ free(ecp->cp);
+ free(ecp);
+ break;
+ }
+ search_busy(sp, btype);
+ btype = BUSY_UPDATE;
+ cnt = INTERRUPT_CHECK;
+ }
+ if (db_get(sp, start, DBG_FATAL, &dbp, &len))
+ return (1);
+ match[0].rm_so = 0;
+ match[0].rm_eo = len;
+ switch (eval =
+ regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
+ case 0:
+ if (cmd == V)
+ continue;
+ break;
+ case REG_NOMATCH:
+ if (cmd == GLOBAL)
+ continue;
+ break;
+ default:
+ re_error(sp, eval, &sp->re_c);
+ break;
+ }
+
+ /* If follows the last entry, extend the last entry's range. */
+ if ((rp = ecp->rq.cqh_last) != (void *)&ecp->rq &&
+ rp->stop == start - 1) {
+ ++rp->stop;
+ continue;
+ }
+
+ /* Allocate a new range, and append it to the list. */
+ CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
+ if (rp == NULL)
+ return (1);
+ rp->start = rp->stop = start;
+ CIRCLEQ_INSERT_TAIL(&ecp->rq, rp, q);
+ }
+ search_busy(sp, BUSY_OFF);
+ return (0);
+}
+
+/*
+ * ex_g_insdel --
+ * Update the ranges based on an insertion or deletion.
+ *
+ * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t));
+ */
+int
+ex_g_insdel(sp, op, lno)
+ SCR *sp;
+ lnop_t op;
+ recno_t lno;
+{
+ EXCMD *ecp;
+ RANGE *nrp, *rp;
+
+ /* All insert/append operations are done as inserts. */
+ if (op == LINE_APPEND)
+ abort();
+
+ if (op == LINE_RESET)
+ return (0);
+
+ for (ecp = sp->gp->ecq.lh_first; ecp != NULL; ecp = ecp->q.le_next) {
+ if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
+ continue;
+ for (rp = ecp->rq.cqh_first; rp != (void *)&ecp->rq; 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 or
+ * increment the range.
+ */
+ if (rp->start > lno) {
+ if (op == LINE_DELETE) {
+ --rp->start;
+ --rp->stop;
+ } else {
+ ++rp->start;
+ ++rp->stop;
+ }
+ continue;
+ }
+
+ /*
+ * Lno is inside the range, decrement the end point
+ * for deletion, and split the range for insertion.
+ * In the latter case, since we're inserting a new
+ * element, neither range can be exhausted.
+ */
+ if (op == LINE_DELETE) {
+ if (rp->start > --rp->stop) {
+ CIRCLEQ_REMOVE(&ecp->rq, rp, q);
+ free(rp);
+ }
+ } else {
+ CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE));
+ nrp->start = lno + 1;
+ nrp->stop = rp->stop + 1;
+ rp->stop = lno - 1;
+ CIRCLEQ_INSERT_AFTER(&ecp->rq, rp, nrp, q);
+ rp = nrp;
+ }
+ }
+
+ /*
+ * If the command deleted/inserted lines, the cursor moves to
+ * the line after the deleted/inserted line.
+ */
+ ecp->range_lno = lno;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_init.c b/contrib/nvi/ex/ex_init.c
new file mode 100644
index 000000000000..6a78416a3344
--- /dev/null
+++ b/contrib/nvi/ex/ex_init.c
@@ -0,0 +1,417 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_init.c 10.26 (Berkeley) 8/12/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tag.h"
+#include "pathnames.h"
+
+enum rc { NOEXIST, NOPERM, RCOK };
+static enum rc exrc_isok __P((SCR *, struct stat *, char *, int, int));
+
+static int ex_run_file __P((SCR *, char *));
+
+/*
+ * ex_screen_copy --
+ * Copy ex screen.
+ *
+ * PUBLIC: int ex_screen_copy __P((SCR *, SCR *));
+ */
+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. */
+ CIRCLEQ_INIT(&nexp->tq);
+ TAILQ_INIT(&nexp->tagfq);
+ LIST_INIT(&nexp->cscq);
+
+ if (orig == NULL) {
+ } else {
+ oexp = EXP(orig);
+
+ if (oexp->lastbcomm != NULL &&
+ (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return(1);
+ }
+ if (ex_tag_copy(orig, sp))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * ex_screen_end --
+ * End a vi screen.
+ *
+ * PUBLIC: int ex_screen_end __P((SCR *));
+ */
+int
+ex_screen_end(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ int rval;
+
+ if ((exp = EXP(sp)) == NULL)
+ return (0);
+
+ rval = 0;
+
+ /* Close down script connections. */
+ if (F_ISSET(sp, SC_SCRIPT) && sscr_end(sp))
+ rval = 1;
+
+ if (argv_free(sp))
+ rval = 1;
+
+ if (exp->ibp != NULL)
+ free(exp->ibp);
+
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+
+ if (ex_tag_free(sp))
+ rval = 1;
+
+ /* Free private memory. */
+ free(exp);
+ sp->ex_private = NULL;
+
+ return (rval);
+}
+
+/*
+ * ex_optchange --
+ * Handle change of options for ex.
+ *
+ * PUBLIC: int ex_optchange __P((SCR *, int, char *, u_long *));
+ */
+int
+ex_optchange(sp, offset, str, valp)
+ SCR *sp;
+ int offset;
+ char *str;
+ u_long *valp;
+{
+ switch (offset) {
+ case O_TAGS:
+ return (ex_tagf_alloc(sp, str));
+ }
+ return (0);
+}
+
+/*
+ * ex_exrc --
+ * Read the EXINIT environment variable and the startup exrc files,
+ * and execute their commands.
+ *
+ * PUBLIC: int ex_exrc __P((SCR *));
+ */
+int
+ex_exrc(sp)
+ SCR *sp;
+{
+ struct stat hsb, lsb;
+ char *p, path[MAXPATHLEN];
+
+ /*
+ * Source the system, environment, $HOME and local .exrc values.
+ * Vi historically didn't check $HOME/.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 of both the $HOME and local startup files if they exist,
+ * otherwise the historic ones.
+ *
+ * !!!
+ * For a discussion of permissions and when what .exrc files are
+ * read, see the comment above the exrc_isok() function below.
+ *
+ * !!!
+ * If the user started the historic of vi in $HOME, vi read the user's
+ * .exrc file twice, as $HOME/.exrc and as ./.exrc. We avoid this, as
+ * it's going to make some commands behave oddly, and I can't imagine
+ * anyone depending on it.
+ */
+ switch (exrc_isok(sp, &hsb, _PATH_SYSEXRC, 1, 0)) {
+ case NOEXIST:
+ case NOPERM:
+ break;
+ case RCOK:
+ if (ex_run_file(sp, _PATH_SYSEXRC))
+ return (1);
+ break;
+ }
+
+ /* Run the commands. */
+ if (EXCMD_RUNNING(sp->gp))
+ (void)ex_cmd(sp);
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
+ return (0);
+
+ if ((p = getenv("NEXINIT")) != NULL) {
+ if (ex_run_str(sp, "NEXINIT", p, strlen(p), 1, 0))
+ return (1);
+ } else if ((p = getenv("EXINIT")) != NULL) {
+ if (ex_run_str(sp, "EXINIT", p, strlen(p), 1, 0))
+ return (1);
+ } else if ((p = getenv("HOME")) != NULL && *p) {
+ (void)snprintf(path, sizeof(path), "%s/%s", p, _PATH_NEXRC);
+ switch (exrc_isok(sp, &hsb, path, 0, 1)) {
+ case NOEXIST:
+ (void)snprintf(path,
+ sizeof(path), "%s/%s", p, _PATH_EXRC);
+ if (exrc_isok(sp,
+ &hsb, path, 0, 1) == RCOK && ex_run_file(sp, path))
+ return (1);
+ break;
+ case NOPERM:
+ break;
+ case RCOK:
+ if (ex_run_file(sp, path))
+ return (1);
+ break;
+ }
+ }
+
+ /* Run the commands. */
+ if (EXCMD_RUNNING(sp->gp))
+ (void)ex_cmd(sp);
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
+ return (0);
+
+ /* Previous commands may have set the exrc option. */
+ if (O_ISSET(sp, O_EXRC)) {
+ switch (exrc_isok(sp, &lsb, _PATH_NEXRC, 0, 0)) {
+ case NOEXIST:
+ if (exrc_isok(sp, &lsb, _PATH_EXRC, 0, 0) == RCOK &&
+ (lsb.st_dev != hsb.st_dev ||
+ lsb.st_ino != hsb.st_ino) &&
+ ex_run_file(sp, _PATH_EXRC))
+ return (1);
+ break;
+ case NOPERM:
+ break;
+ case RCOK:
+ if ((lsb.st_dev != hsb.st_dev ||
+ lsb.st_ino != hsb.st_ino) &&
+ ex_run_file(sp, _PATH_NEXRC))
+ return (1);
+ break;
+ }
+ /* Run the commands. */
+ if (EXCMD_RUNNING(sp->gp))
+ (void)ex_cmd(sp);
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
+ return (0);
+ }
+
+ return (0);
+}
+
+/*
+ * ex_run_file --
+ * Set up a file of ex commands to run.
+ */
+static int
+ex_run_file(sp, name)
+ SCR *sp;
+ char *name;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_SOURCE, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, name, strlen(name));
+ return (ex_source(sp, &cmd));
+}
+
+/*
+ * ex_run_str --
+ * Set up a string of ex commands to run.
+ *
+ * PUBLIC: int ex_run_str __P((SCR *, char *, char *, size_t, int, int));
+ */
+int
+ex_run_str(sp, name, str, len, ex_flags, nocopy)
+ SCR *sp;
+ char *name, *str;
+ size_t len;
+ int ex_flags, nocopy;
+{
+ GS *gp;
+ EXCMD *ecp;
+
+ gp = sp->gp;
+ if (EXCMD_RUNNING(gp)) {
+ CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
+ LIST_INSERT_HEAD(&gp->ecq, ecp, q);
+ } else
+ ecp = &gp->excmd;
+
+ F_INIT(ecp,
+ ex_flags ? E_BLIGNORE | E_NOAUTO | E_NOPRDEF | E_VLITONLY : 0);
+
+ if (nocopy)
+ ecp->cp = str;
+ else
+ if ((ecp->cp = v_strdup(sp, str, len)) == NULL)
+ return (1);
+ ecp->clen = len;
+
+ if (name == NULL)
+ ecp->if_name = NULL;
+ else {
+ if ((ecp->if_name = v_strdup(sp, name, strlen(name))) == NULL)
+ return (1);
+ ecp->if_lno = 1;
+ F_SET(ecp, E_NAMEDISCARD);
+ }
+
+ return (0);
+}
+
+/*
+ * exrc_isok --
+ * Check a .exrc file for source-ability.
+ *
+ * !!!
+ * Historically, vi read the $HOME and local .exrc files if they were owned
+ * by the user's real ID, or the "sourceany" option was set, regardless of
+ * any other considerations. We no longer support the sourceany option as
+ * it's a security problem of mammoth proportions. We require the system
+ * .exrc file to be owned by root, the $HOME .exrc file to be owned by the
+ * user's effective ID (or that the user's effective ID be root) and the
+ * local .exrc files to be owned by the user's effective ID. In all cases,
+ * the file cannot be writeable by anyone other than its owner.
+ *
+ * In O'Reilly ("Learning the VI Editor", Fifth Ed., May 1992, page 106),
+ * it notes that System V release 3.2 and later has an option "[no]exrc".
+ * The behavior is that local .exrc files are read only if the exrc option
+ * is set. The default for the exrc option was off, so, by default, local
+ * .exrc files were not read. The problem this was intended to solve was
+ * that System V permitted users to give away files, so there's no possible
+ * ownership or writeability test to ensure that the file is safe.
+ *
+ * POSIX 1003.2-1992 standardized exrc as an option. It required the exrc
+ * option to be off by default, thus local .exrc files are not to be read
+ * by default. The Rationale noted (incorrectly) that this was a change
+ * to historic practice, but correctly noted that a default of off improves
+ * system security. POSIX also required that vi check the effective user
+ * ID instead of the real user ID, which is why we've switched from historic
+ * practice.
+ *
+ * We initialize the exrc variable to off. If it's turned on by the system
+ * or $HOME .exrc files, and the local .exrc file passes the ownership and
+ * writeability tests, then we read it. This breaks historic 4BSD practice,
+ * but it gives us a measure of security on systems where users can give away
+ * files.
+ */
+static enum rc
+exrc_isok(sp, sbp, path, rootown, rootid)
+ SCR *sp;
+ struct stat *sbp;
+ char *path;
+ int rootown, rootid;
+{
+ enum { ROOTOWN, OWN, WRITER } etype;
+ uid_t euid;
+ int nf1, nf2;
+ char *a, *b, buf[MAXPATHLEN];
+
+ /* Check for the file's existence. */
+ if (stat(path, sbp))
+ return (NOEXIST);
+
+ /* Check ownership permissions. */
+ euid = geteuid();
+ if (!(rootown && sbp->st_uid == 0) &&
+ !(rootid && euid == 0) && sbp->st_uid != euid) {
+ etype = rootown ? ROOTOWN : OWN;
+ goto denied;
+ }
+
+ /* Check writeability. */
+ if (sbp->st_mode & (S_IWGRP | S_IWOTH)) {
+ etype = WRITER;
+ goto denied;
+ }
+ return (RCOK);
+
+denied: a = msg_print(sp, path, &nf1);
+ if (strchr(path, '/') == NULL && getcwd(buf, sizeof(buf)) != NULL) {
+ b = msg_print(sp, buf, &nf2);
+ switch (etype) {
+ case ROOTOWN:
+ msgq(sp, M_ERR,
+ "125|%s/%s: not sourced: not owned by you or root",
+ b, a);
+ break;
+ case OWN:
+ msgq(sp, M_ERR,
+ "126|%s/%s: not sourced: not owned by you", b, a);
+ break;
+ case WRITER:
+ msgq(sp, M_ERR,
+ "127|%s/%s: not sourced: writeable by a user other than the owner", b, a);
+ break;
+ }
+ if (nf2)
+ FREE_SPACE(sp, b, 0);
+ } else
+ switch (etype) {
+ case ROOTOWN:
+ msgq(sp, M_ERR,
+ "128|%s: not sourced: not owned by you or root", a);
+ break;
+ case OWN:
+ msgq(sp, M_ERR,
+ "129|%s: not sourced: not owned by you", a);
+ break;
+ case WRITER:
+ msgq(sp, M_ERR,
+ "130|%s: not sourced: writeable by a user other than the owner", a);
+ break;
+ }
+
+ if (nf1)
+ FREE_SPACE(sp, a, 0);
+ return (NOPERM);
+}
diff --git a/contrib/nvi/ex/ex_join.c b/contrib/nvi/ex/ex_join.c
new file mode 100644
index 000000000000..c26c4243178a
--- /dev/null
+++ b/contrib/nvi/ex/ex_join.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_join.c 10.10 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
+ * Join lines.
+ *
+ * PUBLIC: int ex_join __P((SCR *, EXCMD *));
+ */
+int
+ex_join(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ recno_t from, to;
+ size_t blen, clen, len, tlen;
+ int echar, extra, first;
+ char *bp, *p, *tbp;
+
+ NEEDFILE(sp, cmdp);
+
+ from = cmdp->addr1.lno;
+ to = cmdp->addr2.lno;
+
+ /* Check for no lines to join. */
+ if (!db_exist(sp, from + 1)) {
+ msgq(sp, M_ERR, "131|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 (FL_ISSET(cmdp->iflags, E_C_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 (db_get(sp, from, 0, &p, &len)) {
+ 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 ., insert two spaces.
+ * Else, insert one space.
+ *
+ * One change -- add ? and ! to the list of characters for
+ * which we insert two spaces. I expect that POSIX 1003.2
+ * will require this as well.
+ *
+ * Echar is the last character in the last line joined.
+ */
+ extra = 0;
+ if (!first && !FL_ISSET(cmdp->iflags, E_C_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) {
+ memcpy(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 (db_delete(sp, to))
+ goto err;
+
+ /* If the original line changed, reset it. */
+ if (!first && db_set(sp, 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/contrib/nvi/ex/ex_map.c b/contrib/nvi/ex/ex_map.c
new file mode 100644
index 000000000000..bc2cf0855919
--- /dev/null
+++ b/contrib/nvi/ex/ex_map.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_map.c 10.9 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.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.
+ *
+ * PUBLIC: int ex_map __P((SCR *, EXCMD *));
+ */
+int
+ex_map(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ seq_t stype;
+ CHAR_T *input, *p;
+
+ stype = FL_ISSET(cmdp->iflags, E_C_FORCE) ? SEQ_INPUT : SEQ_COMMAND;
+
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, stype, 1) == 0)
+ msgq(sp, M_INFO, stype == SEQ_INPUT ?
+ "132|No input map entries" :
+ "133|No command map entries");
+ return (0);
+ case 2:
+ input = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * If the mapped string is #[0-9]* (and wasn't quoted) then store the
+ * function key mapping. If the screen specific routine has been set,
+ * call it as well. Note, the SEQ_FUNCMAP type is persistent across
+ * screen types, maybe the next screen type will get it right.
+ */
+ if (input[0] == '#' && isdigit(input[1])) {
+ for (p = input + 2; isdigit(*p); ++p);
+ if (p[0] != '\0')
+ goto nofunc;
+
+ if (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype,
+ SEQ_FUNCMAP | SEQ_USERDEF))
+ return (1);
+ return (sp->gp->scr_fmap == NULL ? 0 :
+ sp->gp->scr_fmap(sp, stype, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len));
+ }
+
+ /* Some single keys may not be remapped in command mode. */
+nofunc: if (stype == SEQ_COMMAND && input[1] == '\0')
+ switch (KEY_VAL(sp, input[0])) {
+ case K_COLON:
+ case K_ESCAPE:
+ case K_NL:
+ msgq(sp, M_ERR,
+ "134|The %s character may not be remapped",
+ KEY_NAME(sp, input[0]));
+ return (1);
+ }
+ return (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_USERDEF));
+}
+
+/*
+ * ex_unmap -- (:unmap[!] key)
+ * Unmap a key.
+ *
+ * PUBLIC: int ex_unmap __P((SCR *, EXCMD *));
+ */
+int
+ex_unmap(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ FL_ISSET(cmdp->iflags, E_C_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) {
+ msgq_str(sp, M_INFO,
+ cmdp->argv[0]->bp, "135|\"%s\" isn't currently mapped");
+ return (1);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_mark.c b/contrib/nvi/ex/ex_mark.c
new file mode 100644
index 000000000000..08ad8c2aa30b
--- /dev/null
+++ b/contrib/nvi/ex/ex_mark.c
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_mark.c 10.8 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_mark -- :mark char
+ * :k char
+ * Mark lines.
+ *
+ *
+ * PUBLIC: int ex_mark __P((SCR *, EXCMD *));
+ */
+int
+ex_mark(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ NEEDFILE(sp, cmdp);
+
+ if (cmdp->argv[0]->len != 1) {
+ msgq(sp, M_ERR, "136|Mark names must be a single character");
+ return (1);
+ }
+ return (mark_set(sp, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
+}
diff --git a/contrib/nvi/ex/ex_mkexrc.c b/contrib/nvi/ex/ex_mkexrc.c
new file mode 100644
index 000000000000..0eb15d408cf7
--- /dev/null
+++ b/contrib/nvi/ex/ex_mkexrc.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_mkexrc.c 10.11 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "pathnames.h"
+
+/*
+ * ex_mkexrc -- :mkexrc[!] [file]
+ *
+ * Create (or overwrite) a .exrc file with the current info.
+ *
+ * PUBLIC: int ex_mkexrc __P((SCR *, EXCMD *));
+ */
+int
+ex_mkexrc(sp, cmdp)
+ SCR *sp;
+ EXCMD *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 (!FL_ISSET(cmdp->iflags, E_C_FORCE) && !stat(fname, &sb)) {
+ msgq_str(sp, M_ERR, fname,
+ "137|%s exists, not written; use ! to override");
+ 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_str(sp, M_SYSERR, fname, "%s");
+ return (1);
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ sverrno = errno;
+ (void)close(fd);
+ goto e2;
+ }
+
+ if (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV) || ferror(fp))
+ goto e1;
+ if (seq_save(sp, fp, "map ", SEQ_COMMAND) || ferror(fp))
+ goto e1;
+ if (seq_save(sp, fp, "map! ", SEQ_INPUT) || ferror(fp))
+ goto e1;
+ if (opts_save(sp, fp) || ferror(fp))
+ goto e1;
+ if (fclose(fp)) {
+ sverrno = errno;
+ goto e2;
+ }
+
+ msgq_str(sp, M_INFO, fname, "138|New exrc file: %s");
+ return (0);
+
+e1: sverrno = errno;
+ (void)fclose(fp);
+e2: errno = sverrno;
+ msgq_str(sp, M_SYSERR, fname, "%s");
+ return (1);
+}
diff --git a/contrib/nvi/ex/ex_move.c b/contrib/nvi/ex/ex_move.c
new file mode 100644
index 000000000000..d6e45c373964
--- /dev/null
+++ b/contrib/nvi/ex/ex_move.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_move.c 10.10 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_copy -- :[line [,line]] co[py] line [flags]
+ * Copy selected lines.
+ *
+ * PUBLIC: int ex_copy __P((SCR *, EXCMD *));
+ */
+int
+ex_copy(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ CB cb;
+ MARK fm1, fm2, m, tm;
+ recno_t cnt;
+ int rval;
+
+ rval = 0;
+
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * It's possible to copy things into the area that's being
+ * copied, e.g. "2,5copy3" is legitimate. Save the text to
+ * a cut buffer.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ memset(&cb, 0, sizeof(cb));
+ CIRCLEQ_INIT(&cb.textq);
+ for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
+ if (cut_line(sp, cnt, 0, 0, &cb)) {
+ rval = 1;
+ goto err;
+ }
+ cb.flags |= CB_LMODE;
+
+ /* Put the text into place. */
+ tm.lno = cmdp->lineno;
+ tm.cno = 0;
+ if (put(sp, &cb, NULL, &tm, &m, 1))
+ rval = 1;
+ else {
+ /*
+ * Copy puts the cursor on the last line copied. The cursor
+ * returned by the put routine is the first line put, not the
+ * last, because that's the historic semantic of vi.
+ */
+ cnt = (fm2.lno - fm1.lno) + 1;
+ sp->lno = m.lno + (cnt - 1);
+ sp->cno = 0;
+ }
+err: text_lfree(&cb.textq);
+ return (rval);
+}
+
+/*
+ * ex_move -- :[line [,line]] mo[ve] line
+ * Move selected lines.
+ *
+ * PUBLIC: int ex_move __P((SCR *, EXCMD *));
+ */
+int
+ex_move(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ LMARK *lmp;
+ MARK fm1, fm2;
+ recno_t cnt, diff, fl, tl, mfl, mtl;
+ size_t blen, len;
+ int mark_reset;
+ char *bp, *p;
+
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * It's not possible to move things into the area that's being
+ * moved.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
+ msgq(sp, M_ERR, "139|Destination line is inside move range");
+ return (1);
+ }
+
+ /*
+ * Log the positions of any marks in the to-be-deleted lines. This
+ * has to work with the logging code. What happens is that we log
+ * the old mark positions, make the changes, then log the new mark
+ * positions. Then the marks end up in the right positions no matter
+ * which way the log is traversed.
+ *
+ * XXX
+ * Reset the MARK_USERSET flag so that the log can undo the mark.
+ * This isn't very clean, and should probably be fixed.
+ */
+ fl = fm1.lno;
+ tl = cmdp->lineno;
+
+ /* Log the old positions of the marks. */
+ mark_reset = 0;
+ for (lmp = sp->ep->marks.lh_first; lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= fl && lmp->lno <= tl) {
+ mark_reset = 1;
+ F_CLR(lmp, MARK_USERSET);
+ (void)log_mark(sp, lmp);
+ }
+
+ /* Get memory for the copy. */
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /* Move the lines. */
+ diff = (fm2.lno - fm1.lno) + 1;
+ if (tl > fl) { /* Destination > source. */
+ mfl = tl - diff;
+ mtl = tl;
+ for (cnt = diff; cnt--;) {
+ if (db_get(sp, fl, DBG_FATAL, &p, &len))
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memcpy(bp, p, len);
+ if (db_append(sp, 1, tl, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl + 1;
+ if (db_delete(sp, fl))
+ return (1);
+ }
+ } else { /* Destination < source. */
+ mfl = tl;
+ mtl = tl + diff;
+ for (cnt = diff; cnt--;) {
+ if (db_get(sp, fl, DBG_FATAL, &p, &len))
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memcpy(bp, p, len);
+ if (db_append(sp, 1, tl++, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl;
+ ++fl;
+ if (db_delete(sp, fl))
+ return (1);
+ }
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ sp->lno = tl; /* Last line moved. */
+ sp->cno = 0;
+
+ /* Log the new positions of the marks. */
+ if (mark_reset)
+ for (lmp = sp->ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= mfl && lmp->lno <= mtl)
+ (void)log_mark(sp, lmp);
+
+
+ sp->rptlines[L_MOVED] += diff;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_open.c b/contrib/nvi/ex/ex_open.c
new file mode 100644
index 000000000000..afffaeb2202d
--- /dev/null
+++ b/contrib/nvi/ex/ex_open.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_open.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_open -- :[line] o[pen] [/pattern/] [flags]
+ *
+ * Switch to single line "open" mode.
+ *
+ * PUBLIC: int ex_open __P((SCR *, EXCMD *));
+ */
+int
+ex_open(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ /* If open option off, disallow open command. */
+ if (!O_ISSET(sp, O_OPEN)) {
+ msgq(sp, M_ERR,
+ "140|The open command requires that the open option be set");
+ return (1);
+ }
+
+ msgq(sp, M_ERR, "141|The open command is not yet implemented");
+ return (1);
+}
diff --git a/contrib/nvi/ex/ex_perl.c b/contrib/nvi/ex/ex_perl.c
new file mode 100644
index 000000000000..e620352ab518
--- /dev/null
+++ b/contrib/nvi/ex/ex_perl.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1995
+ * George V. Neville-Neil. All rights reserved.
+ * Copyright (c) 1996
+ * Sven Verdoolaege. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_perl.c 8.10 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_perl -- :[line [,line]] perl [command]
+ * Run a command through the perl interpreter.
+ *
+ * ex_perldo -- :[line [,line]] perldo [command]
+ * Run a set of lines through the perl interpreter.
+ *
+ * PUBLIC: int ex_perl __P((SCR*, EXCMD *));
+ */
+int
+ex_perl(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+#ifdef HAVE_PERL_INTERP
+ CHAR_T *p;
+ size_t len;
+
+ /* Skip leading white space. */
+ if (cmdp->argc != 0)
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len; len > 0; --len, ++p)
+ if (!isblank(*p))
+ break;
+ if (cmdp->argc == 0 || len == 0) {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+ return (cmdp->cmd == &cmds[C_PERLCMD] ?
+ perl_ex_perl(sp, p, len, cmdp->addr1.lno, cmdp->addr2.lno) :
+ perl_ex_perldo(sp, p, len, cmdp->addr1.lno, cmdp->addr2.lno));
+#else
+ msgq(sp, M_ERR, "306|Vi was not loaded with a Perl interpreter");
+ return (1);
+#endif
+}
diff --git a/contrib/nvi/ex/ex_preserve.c b/contrib/nvi/ex/ex_preserve.c
new file mode 100644
index 000000000000..5614c88d753a
--- /dev/null
+++ b/contrib/nvi/ex/ex_preserve.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_preserve.c 10.12 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_preserve -- :pre[serve]
+ * Push the file to recovery.
+ *
+ * PUBLIC: int ex_preserve __P((SCR *, EXCMD *));
+ */
+int
+ex_preserve(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ recno_t lno;
+
+ NEEDFILE(sp, cmdp);
+
+ if (!F_ISSET(sp->ep, F_RCV_ON)) {
+ msgq(sp, M_ERR, "142|Preservation of this file not possible");
+ return (1);
+ }
+
+ /* If recovery not initialized, do so. */
+ if (F_ISSET(sp->ep, F_FIRSTMODIFY) && rcv_init(sp))
+ return (1);
+
+ /* Force the file to be read in, in case it hasn't yet. */
+ if (db_last(sp, &lno))
+ return (1);
+
+ /* Sync to disk. */
+ if (rcv_sync(sp, RCV_SNAPSHOT))
+ return (1);
+
+ msgq(sp, M_INFO, "143|File preserved");
+ return (0);
+}
+
+/*
+ * ex_recover -- :rec[over][!] file
+ * Recover the file.
+ *
+ * PUBLIC: int ex_recover __P((SCR *, EXCMD *));
+ */
+int
+ex_recover(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ ARGS *ap;
+ FREF *frp;
+
+ ap = cmdp->argv[0];
+
+ /* Set the alternate file name. */
+ set_alt_name(sp, ap->bp);
+
+ /*
+ * Check for modifications. Autowrite did not historically
+ * affect :recover.
+ */
+ if (file_m2(sp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
+ return (1);
+
+ /* Get a file structure for the file. */
+ if ((frp = file_add(sp, ap->bp)) == NULL)
+ return (1);
+
+ /* Set the recover bit. */
+ F_SET(frp, FR_RECOVER);
+
+ /* Switch files. */
+ if (file_init(sp, frp, NULL, FS_SETALT |
+ (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
+ return (1);
+
+ F_SET(sp, SC_FSWITCH);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_print.c b/contrib/nvi/ex/ex_print.c
new file mode 100644
index 000000000000..4218e08e0978
--- /dev/null
+++ b/contrib/nvi/ex/ex_print.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_print.c 10.18 (Berkeley) 5/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "../common/common.h"
+
+static int ex_prchars __P((SCR *, const char *, size_t *, size_t, u_int, int));
+
+/*
+ * ex_list -- :[line [,line]] l[ist] [count] [flags]
+ *
+ * Display the addressed lines such that the output is unambiguous.
+ *
+ * PUBLIC: int ex_list __P((SCR *, EXCMD *));
+ */
+int
+ex_list(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (ex_print(sp, cmdp,
+ &cmdp->addr1, &cmdp->addr2, cmdp->iflags | E_C_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.
+ *
+ * PUBLIC: int ex_number __P((SCR *, EXCMD *));
+ */
+int
+ex_number(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (ex_print(sp, cmdp,
+ &cmdp->addr1, &cmdp->addr2, cmdp->iflags | E_C_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.
+ *
+ * PUBLIC: int ex_pr __P((SCR *, EXCMD *));
+ */
+int
+ex_pr(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (ex_print(sp, cmdp, &cmdp->addr1, &cmdp->addr2, cmdp->iflags))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_print --
+ * Print the selected lines.
+ *
+ * PUBLIC: int ex_print __P((SCR *, EXCMD *, MARK *, MARK *, u_int32_t));
+ */
+int
+ex_print(sp, cmdp, fp, tp, flags)
+ SCR *sp;
+ EXCMD *cmdp;
+ MARK *fp, *tp;
+ u_int32_t flags;
+{
+ GS *gp;
+ recno_t from, to;
+ size_t col, len;
+ char *p, buf[10];
+
+ NEEDFILE(sp, cmdp);
+
+ gp = sp->gp;
+ for (from = fp->lno, to = tp->lno; from <= to; ++from) {
+ col = 0;
+
+ /*
+ * Display the line number. The %6 format is specified
+ * by POSIX 1003.2, and is almost certainly large enough.
+ * Check, though, just in case.
+ */
+ if (LF_ISSET(E_C_HASH)) {
+ if (from <= 999999) {
+ snprintf(buf, sizeof(buf), "%6ld ", from);
+ p = buf;
+ } else
+ p = "TOOBIG ";
+ if (ex_prchars(sp, p, &col, 8, 0, 0))
+ return (1);
+ }
+
+ /*
+ * Display the line. The format for E_C_PRINT isn't very good,
+ * especially in handling end-of-line tabs, but they're almost
+ * backward compatible.
+ */
+ if (db_get(sp, from, DBG_FATAL, &p, &len))
+ return (1);
+
+ if (len == 0 && !LF_ISSET(E_C_LIST))
+ (void)ex_puts(sp, "\n");
+ else if (ex_ldisplay(sp, p, len, col, flags))
+ return (1);
+
+ if (INTERRUPTED(sp))
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ex_ldisplay --
+ * Display a line without any preceding number.
+ *
+ * PUBLIC: int ex_ldisplay __P((SCR *, const char *, size_t, size_t, u_int));
+ */
+int
+ex_ldisplay(sp, p, len, col, flags)
+ SCR *sp;
+ const char *p;
+ size_t len, col;
+ u_int flags;
+{
+ if (len > 0 && ex_prchars(sp, p, &col, len, LF_ISSET(E_C_LIST), 0))
+ return (1);
+ if (!INTERRUPTED(sp) && LF_ISSET(E_C_LIST)) {
+ p = "$";
+ if (ex_prchars(sp, p, &col, 1, LF_ISSET(E_C_LIST), 0))
+ return (1);
+ }
+ if (!INTERRUPTED(sp))
+ (void)ex_puts(sp, "\n");
+ return (0);
+}
+
+/*
+ * ex_scprint --
+ * Display a line for the substitute with confirmation routine.
+ *
+ * PUBLIC: int ex_scprint __P((SCR *, MARK *, MARK *));
+ */
+int
+ex_scprint(sp, fp, tp)
+ SCR *sp;
+ MARK *fp, *tp;
+{
+ const char *p;
+ size_t col, len;
+
+ col = 0;
+ if (O_ISSET(sp, O_NUMBER)) {
+ p = " ";
+ if (ex_prchars(sp, p, &col, 8, 0, 0))
+ return (1);
+ }
+
+ if (db_get(sp, fp->lno, DBG_FATAL, (char **)&p, &len))
+ return (1);
+
+ if (ex_prchars(sp, p, &col, fp->cno, 0, ' '))
+ return (1);
+ p += fp->cno;
+ if (ex_prchars(sp,
+ p, &col, tp->cno == fp->cno ? 1 : tp->cno - fp->cno, 0, '^'))
+ return (1);
+ if (INTERRUPTED(sp))
+ return (1);
+ p = "[ynq]"; /* XXX: should be msg_cat. */
+ if (ex_prchars(sp, p, &col, 5, 0, 0))
+ return (1);
+ (void)ex_fflush(sp);
+ return (0);
+}
+
+/*
+ * ex_prchars --
+ * Local routine to dump characters to the screen.
+ */
+static int
+ex_prchars(sp, p, colp, len, flags, repeatc)
+ SCR *sp;
+ const char *p;
+ size_t *colp, len;
+ u_int flags;
+ int repeatc;
+{
+ CHAR_T ch, *kp;
+ GS *gp;
+ size_t col, tlen, ts;
+
+ if (O_ISSET(sp, O_LIST))
+ LF_SET(E_C_LIST);
+ gp = sp->gp;
+ ts = O_VAL(sp, O_TABSTOP);
+ for (col = *colp; len--;)
+ if ((ch = *p++) == '\t' && !LF_ISSET(E_C_LIST))
+ for (tlen = ts - col % ts;
+ col < sp->cols && tlen--; ++col) {
+ (void)ex_printf(sp,
+ "%c", repeatc ? repeatc : ' ');
+ if (INTERRUPTED(sp))
+ goto intr;
+ }
+ else {
+ kp = KEY_NAME(sp, ch);
+ tlen = KEY_LEN(sp, ch);
+ if (!repeatc && col + tlen < sp->cols) {
+ (void)ex_puts(sp, kp);
+ col += tlen;
+ } else
+ for (; tlen--; ++kp, ++col) {
+ if (col == sp->cols) {
+ col = 0;
+ (void)ex_puts(sp, "\n");
+ }
+ (void)ex_printf(sp,
+ "%c", repeatc ? repeatc : *kp);
+ if (INTERRUPTED(sp))
+ goto intr;
+ }
+ }
+intr: *colp = col;
+ return (0);
+}
+
+/*
+ * ex_printf --
+ * Ex's version of printf.
+ *
+ * PUBLIC: int ex_printf __P((SCR *, const char *, ...));
+ */
+int
+#ifdef __STDC__
+ex_printf(SCR *sp, const char *fmt, ...)
+#else
+ex_printf(sp, fmt, va_alist)
+ SCR *sp;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ EX_PRIVATE *exp;
+ va_list ap;
+ size_t n;
+
+ exp = EXP(sp);
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ exp->obp_len += n = vsnprintf(exp->obp + exp->obp_len,
+ sizeof(exp->obp) - exp->obp_len, fmt, ap);
+ va_end(ap);
+
+ /* Flush when reach a <newline> or half the buffer. */
+ if (exp->obp[exp->obp_len - 1] == '\n' ||
+ exp->obp_len > sizeof(exp->obp) / 2)
+ (void)ex_fflush(sp);
+ return (n);
+}
+
+/*
+ * ex_puts --
+ * Ex's version of puts.
+ *
+ * PUBLIC: int ex_puts __P((SCR *, const char *));
+ */
+int
+ex_puts(sp, str)
+ SCR *sp;
+ const char *str;
+{
+ EX_PRIVATE *exp;
+ int doflush, n;
+
+ exp = EXP(sp);
+
+ /* Flush when reach a <newline> or the end of the buffer. */
+ for (doflush = n = 0; *str != '\0'; ++n) {
+ if (exp->obp_len > sizeof(exp->obp))
+ (void)ex_fflush(sp);
+ if ((exp->obp[exp->obp_len++] = *str++) == '\n')
+ doflush = 1;
+ }
+ if (doflush)
+ (void)ex_fflush(sp);
+ return (n);
+}
+
+/*
+ * ex_fflush --
+ * Ex's version of fflush.
+ *
+ * PUBLIC: int ex_fflush __P((SCR *sp));
+ */
+int
+ex_fflush(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+
+ if (exp->obp_len != 0) {
+ sp->gp->scr_msg(sp, M_NONE, exp->obp, exp->obp_len);
+ exp->obp_len = 0;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_put.c b/contrib/nvi/ex/ex_put.c
new file mode 100644
index 000000000000..2facb03f0fb5
--- /dev/null
+++ b/contrib/nvi/ex/ex_put.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_put.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_put -- [line] pu[t] [buffer]
+ * Append a cut buffer into the file.
+ *
+ * PUBLIC: int ex_put __P((SCR *, EXCMD *));
+ */
+int
+ex_put(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ MARK m;
+
+ NEEDFILE(sp, cmdp);
+
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (put(sp, NULL,
+ FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &m, 1))
+ return (1);
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_quit.c b/contrib/nvi/ex/ex_quit.c
new file mode 100644
index 000000000000..705fa1a64ade
--- /dev/null
+++ b/contrib/nvi/ex/ex_quit.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_quit.c 10.7 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_quit -- :quit[!]
+ * Quit.
+ *
+ * PUBLIC: int ex_quit __P((SCR *, EXCMD *));
+ */
+int
+ex_quit(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ int force;
+
+ force = FL_ISSET(cmdp->iflags, E_C_FORCE);
+
+ /* Check for file modifications, or more files to edit. */
+ if (file_m2(sp, force) || ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_read.c b/contrib/nvi/ex/ex_read.c
new file mode 100644
index 000000000000..78296ff28ca6
--- /dev/null
+++ b/contrib/nvi/ex/ex_read.c
@@ -0,0 +1,360 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_read.c 10.38 (Berkeley) 8/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+/*
+ * ex_read -- :read [file]
+ * :read [!cmd]
+ * Read from a file or utility.
+ *
+ * !!!
+ * Historical vi wouldn't undo a filter read, for no apparent reason.
+ *
+ * PUBLIC: int ex_read __P((SCR *, EXCMD *));
+ */
+int
+ex_read(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ enum { R_ARG, R_EXPANDARG, R_FILTER } which;
+ struct stat sb;
+ CHAR_T *arg, *name;
+ EX_PRIVATE *exp;
+ FILE *fp;
+ FREF *frp;
+ GS *gp;
+ MARK rm;
+ recno_t nlines;
+ size_t arglen;
+ int argc, rval;
+ char *p;
+
+ gp = sp->gp;
+
+ /*
+ * 0 args: read the current pathname.
+ * 1 args: check for "read !arg".
+ */
+ switch (cmdp->argc) {
+ case 0:
+ which = R_ARG;
+ break;
+ case 1:
+ arg = cmdp->argv[0]->bp;
+ arglen = cmdp->argv[0]->len;
+ if (*arg == '!') {
+ ++arg;
+ --arglen;
+ which = R_FILTER;
+
+ /* Secure means no shell access. */
+ if (O_ISSET(sp, O_SECURE)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F);
+ return (1);
+ }
+ } else
+ which = R_EXPANDARG;
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+ /* Load a temporary file if no file being edited. */
+ if (sp->ep == NULL) {
+ if ((frp = file_add(sp, NULL)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, 0))
+ return (1);
+ }
+
+ switch (which) {
+ case R_FILTER:
+ /*
+ * File name and bang expand the user's argument. If
+ * we don't get an additional argument, it's illegal.
+ */
+ argc = cmdp->argc;
+ if (argv_exp1(sp, cmdp, arg, arglen, 1))
+ return (1);
+ if (argc == cmdp->argc) {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+ argc = cmdp->argc - 1;
+
+ /* Set the last bang command. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ if ((exp->lastbcomm =
+ strdup(cmdp->argv[argc]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /*
+ * Vi redisplayed the user's argument if it changed, ex
+ * always displayed a !, plus the user's argument if it
+ * changed.
+ */
+ if (F_ISSET(sp, SC_VI)) {
+ if (F_ISSET(cmdp, E_MODIFY))
+ (void)vs_update(sp, "!", cmdp->argv[argc]->bp);
+ } else {
+ if (F_ISSET(cmdp, E_MODIFY))
+ (void)ex_printf(sp,
+ "!%s\n", cmdp->argv[argc]->bp);
+ else
+ (void)ex_puts(sp, "!\n");
+ (void)ex_fflush(sp);
+ }
+
+ /*
+ * Historically, filter reads as the first ex command didn't
+ * wait for the user. If SC_SCR_EXWROTE not already set, set
+ * the don't-wait flag.
+ */
+ if (!F_ISSET(sp, SC_SCR_EXWROTE))
+ F_SET(sp, SC_EX_WAIT_NO);
+
+ /*
+ * Switch into ex canonical mode. The reason to restore the
+ * original terminal modes for read filters is so that users
+ * can do things like ":r! cat /dev/tty".
+ *
+ * !!!
+ * We do not output an extra <newline>, so that we don't touch
+ * the screen on a normal read.
+ */
+ if (F_ISSET(sp, SC_VI)) {
+ if (gp->scr_screen(sp, SC_EX)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON_F);
+ return (1);
+ }
+ /*
+ * !!!
+ * Historically, the read command doesn't switch to
+ * the alternate X11 xterm screen, if doing a filter
+ * read -- don't set SA_ALTERNATE.
+ */
+ F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
+ }
+
+ if (ex_filter(sp, cmdp, &cmdp->addr1,
+ NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ))
+ return (1);
+
+ /* The filter version of read set the autoprint flag. */
+ F_SET(cmdp, E_AUTOPRINT);
+
+ /*
+ * If in vi mode, move to the first nonblank. Might have
+ * switched into ex mode, so saved the original SC_VI value.
+ */
+ sp->lno = rm.lno;
+ if (F_ISSET(sp, SC_VI)) {
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ }
+ return (0);
+ case R_ARG:
+ name = sp->frp->name;
+ break;
+ case R_EXPANDARG:
+ if (argv_exp2(sp, cmdp, arg, arglen))
+ return (1);
+ /*
+ * 0 args: impossible.
+ * 1 args: impossible (I hope).
+ * 2 args: read it.
+ * >2 args: object, too many args.
+ *
+ * The 1 args case depends on the argv_sexp() function refusing
+ * to return success without at least one non-blank character.
+ */
+ switch (cmdp->argc) {
+ case 0:
+ case 1:
+ abort();
+ /* NOTREACHED */
+ case 2:
+ name = cmdp->argv[1]->bp;
+ /*
+ * !!!
+ * Historically, the read and write commands renamed
+ * "unnamed" files, or, if the file had a name, set
+ * the alternate file name.
+ */
+ if (F_ISSET(sp->frp, FR_TMPFILE) &&
+ !F_ISSET(sp->frp, FR_EXNAMED)) {
+ if ((p = v_strdup(sp, cmdp->argv[1]->bp,
+ cmdp->argv[1]->len)) != NULL) {
+ free(sp->frp->name);
+ sp->frp->name = p;
+ }
+ /*
+ * The file has a real name, it's no longer a
+ * temporary, clear the temporary file flags.
+ */
+ F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
+ F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
+
+ /* Notify the screen. */
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+ } else
+ set_alt_name(sp, name);
+ break;
+ default:
+ ex_emsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT);
+ return (1);
+
+ }
+ break;
+ }
+
+ /*
+ * !!!
+ * 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. We permit reading from named pipes too, since
+ * they didn't exist when the original implementation of vi was done
+ * and they seem a reasonable addition.
+ */
+ if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
+ msgq_str(sp, M_SYSERR, name, "%s");
+ return (1);
+ }
+ if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) {
+ (void)fclose(fp);
+ msgq(sp, M_ERR,
+ "145|Only regular files and named pipes may be read");
+ return (1);
+ }
+
+ /* Try and get a lock. */
+ if (file_lock(sp, NULL, NULL, fileno(fp), 0) == LOCK_UNAVAIL)
+ msgq(sp, M_ERR, "146|%s: read lock was unavailable", name);
+
+ rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0);
+
+ /*
+ * In vi, 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.)
+ *
+ * In ex, set the cursor to the last line read in, if anything read in,
+ * otherwise, the address.
+ */
+ if (F_ISSET(sp, SC_VI)) {
+ sp->lno = cmdp->addr1.lno;
+ if (nlines)
+ ++sp->lno;
+ } else
+ sp->lno = cmdp->addr1.lno + nlines;
+ return (rval);
+}
+
+/*
+ * ex_readfp --
+ * Read lines into the file.
+ *
+ * PUBLIC: int ex_readfp __P((SCR *, char *, FILE *, MARK *, recno_t *, int));
+ */
+int
+ex_readfp(sp, name, fp, fm, nlinesp, silent)
+ SCR *sp;
+ char *name;
+ FILE *fp;
+ MARK *fm;
+ recno_t *nlinesp;
+ int silent;
+{
+ EX_PRIVATE *exp;
+ GS *gp;
+ recno_t lcnt, lno;
+ size_t len;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ int nf, rval;
+ char *p;
+
+ gp = sp->gp;
+ exp = EXP(sp);
+
+ /*
+ * Add in the lines from the output. Insertion starts at the line
+ * following the address.
+ */
+ ccnt = 0;
+ lcnt = 0;
+ p = "147|Reading...";
+ for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) {
+ if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (!silent) {
+ gp->scr_busy(sp, p,
+ p == NULL ? BUSY_UPDATE : BUSY_ON);
+ p = NULL;
+ }
+ }
+ if (db_append(sp, 1, lno, exp->ibp, len))
+ goto err;
+ ccnt += len;
+ }
+
+ if (ferror(fp) || fclose(fp))
+ goto err;
+
+ /* Return the number of lines read in. */
+ if (nlinesp != NULL)
+ *nlinesp = lcnt;
+
+ if (!silent) {
+ p = msg_print(sp, name, &nf);
+ msgq(sp, M_INFO,
+ "148|%s: %lu lines, %lu characters", p, lcnt, ccnt);
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ }
+
+ rval = 0;
+ if (0) {
+err: msgq_str(sp, M_SYSERR, name, "%s");
+ (void)fclose(fp);
+ rval = 1;
+ }
+
+ if (!silent)
+ gp->scr_busy(sp, NULL, BUSY_OFF);
+ return (rval);
+}
diff --git a/contrib/nvi/ex/ex_screen.c b/contrib/nvi/ex/ex_screen.c
new file mode 100644
index 000000000000..9bc5bf04b06a
--- /dev/null
+++ b/contrib/nvi/ex/ex_screen.c
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_screen.c 10.11 (Berkeley) 6/29/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+/*
+ * ex_bg -- :bg
+ * Hide the screen.
+ *
+ * PUBLIC: int ex_bg __P((SCR *, EXCMD *));
+ */
+int
+ex_bg(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (vs_bg(sp));
+}
+
+/*
+ * ex_fg -- :fg [file]
+ * Show the screen.
+ *
+ * PUBLIC: int ex_fg __P((SCR *, EXCMD *));
+ */
+int
+ex_fg(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ SCR *nsp;
+ int newscreen;
+
+ newscreen = F_ISSET(cmdp, E_NEWSCREEN);
+ if (vs_fg(sp, &nsp, cmdp->argc ? cmdp->argv[0]->bp : NULL, newscreen))
+ return (1);
+
+ /* Set up the switch. */
+ if (newscreen) {
+ sp->nextdisp = nsp;
+ F_SET(sp, SC_SSWITCH);
+ }
+ return (0);
+}
+
+/*
+ * ex_resize -- :resize [+-]rows
+ * Change the screen size.
+ *
+ * PUBLIC: int ex_resize __P((SCR *, EXCMD *));
+ */
+int
+ex_resize(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ adj_t adj;
+
+ switch (FL_ISSET(cmdp->iflags,
+ E_C_COUNT | E_C_COUNT_NEG | E_C_COUNT_POS)) {
+ case E_C_COUNT:
+ adj = A_SET;
+ break;
+ case E_C_COUNT | E_C_COUNT_NEG:
+ adj = A_DECREASE;
+ break;
+ case E_C_COUNT | E_C_COUNT_POS:
+ adj = A_INCREASE;
+ break;
+ default:
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+ return (vs_resize(sp, cmdp->count, adj));
+}
+
+/*
+ * ex_sdisplay --
+ * Display the list of screens.
+ *
+ * PUBLIC: int ex_sdisplay __P((SCR *));
+ */
+int
+ex_sdisplay(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SCR *tsp;
+ int cnt, col, len, sep;
+
+ gp = sp->gp;
+ if ((tsp = gp->hq.cqh_first) == (void *)&gp->hq) {
+ msgq(sp, M_INFO, "149|No background screens to display");
+ return (0);
+ }
+
+ col = len = sep = 0;
+ for (cnt = 1; tsp != (void *)&gp->hq && !INTERRUPTED(sp);
+ tsp = tsp->q.cqe_next) {
+ col += len = strlen(tsp->frp->name) + sep;
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_puts(sp, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_puts(sp, " ");
+ }
+ (void)ex_puts(sp, tsp->frp->name);
+ ++cnt;
+ }
+ if (!INTERRUPTED(sp))
+ (void)ex_puts(sp, "\n");
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_script.c b/contrib/nvi/ex/ex_script.c
new file mode 100644
index 000000000000..9ca6d60060d9
--- /dev/null
+++ b/contrib/nvi/ex/ex_script.c
@@ -0,0 +1,798 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Brian Hirt.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_script.c 10.30 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_SYS5_PTY
+#include <sys/stropts.h>
+#endif
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h> /* XXX: OSF/1 bug: include before <grp.h> */
+#include <grp.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "script.h"
+#include "pathnames.h"
+
+static void sscr_check __P((SCR *));
+static int sscr_getprompt __P((SCR *));
+static int sscr_init __P((SCR *));
+static int sscr_insert __P((SCR *));
+static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
+static int sscr_pty __P((int *, int *, char *, struct termios *, void *));
+static int sscr_setprompt __P((SCR *, char *, size_t));
+
+/*
+ * ex_script -- : sc[ript][!] [file]
+ * Switch to script mode.
+ *
+ * PUBLIC: int ex_script __P((SCR *, EXCMD *));
+ */
+int
+ex_script(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ /* Vi only command. */
+ if (!F_ISSET(sp, SC_VI)) {
+ msgq(sp, M_ERR,
+ "150|The script command is only available in vi mode");
+ return (1);
+ }
+
+ /* Switch to the new file. */
+ if (cmdp->argc != 0 && ex_edit(sp, cmdp))
+ return (1);
+
+ /* Create the shell, figure out the prompt. */
+ if (sscr_init(sp))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * sscr_init --
+ * Create a pty setup for a shell.
+ */
+static int
+sscr_init(sp)
+ SCR *sp;
+{
+ SCRIPT *sc;
+ char *sh, *sh_path;
+
+ /* We're going to need a shell. */
+ if (opts_empty(sp, O_SHELL, 0))
+ return (1);
+
+ 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);
+
+#ifdef TIOCGWINSZ
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+
+ if (sscr_pty(&sc->sh_master,
+ &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "pty");
+ goto err;
+ }
+#else
+ if (sscr_pty(&sc->sh_master,
+ &sc->sh_slave, sc->sh_name, &sc->sh_term, NULL) == -1) {
+ msgq(sp, M_SYSERR, "pty");
+ goto err;
+ }
+#endif
+
+ /*
+ * __TK__ huh?
+ * 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. */
+ /*
+ * XXX
+ * So that shells that do command line editing turn it off.
+ */
+ (void)setenv("TERM", "emacs", 1);
+ (void)setenv("TERMCAP", "emacs:", 1);
+ (void)setenv("EMACS", "t", 1);
+
+ (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_str(sp, M_SYSERR, sh_path, "execl: %s");
+ _exit(127);
+ default: /* Parent. */
+ break;
+ }
+
+ if (sscr_getprompt(sp))
+ return (1);
+
+ F_SET(sp, SC_SCRIPT);
+ F_SET(sp->gp, G_SCRWIN);
+ 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)
+ SCR *sp;
+{
+ struct timeval tv;
+ CHAR_T *endp, *p, *t, buf[1024];
+ SCRIPT *sc;
+ fd_set fdset;
+ recno_t lline;
+ size_t llen, len;
+ u_int value;
+ int nr;
+
+ 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 = KEY_VAL(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ if (db_last(sp, &lline) ||
+ db_append(sp, 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 (db_last(sp, &lline) || db_append(sp, 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.
+ *
+ * PUBLIC: int sscr_exec __P((SCR *, recno_t));
+ */
+int
+sscr_exec(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ SCRIPT *sc;
+ recno_t last_lno;
+ size_t blen, len, last_len, tlen;
+ int isempty, matchprompt, nw, rval;
+ char *bp, *p;
+
+ /* If there's a prompt on the last line, append the command. */
+ if (db_last(sp, &last_lno))
+ return (1);
+ if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
+ 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 (db_eget(sp, lno, &p, &len, &isempty)) {
+ if (isempty)
+ goto empty;
+ 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, "151|No command 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 (db_set(sp, last_lno, bp, last_len + len))
+err1: rval = 1;
+ }
+ if (matchprompt)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * sscr_input --
+ * Read any waiting shell input.
+ *
+ * PUBLIC: int sscr_input __P((SCR *));
+ */
+int
+sscr_input(sp)
+ SCR *sp;
+{
+ GS *gp;
+ struct timeval poll;
+ fd_set rdfd;
+ int maxfd;
+
+ gp = sp->gp;
+
+loop: maxfd = 0;
+ FD_ZERO(&rdfd);
+ poll.tv_sec = 0;
+ poll.tv_usec = 0;
+
+ /* Set up the input mask. */
+ for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, SC_SCRIPT)) {
+ FD_SET(sp->script->sh_master, &rdfd);
+ if (sp->script->sh_master > maxfd)
+ maxfd = sp->script->sh_master;
+ }
+
+ /* Check for input. */
+ switch (select(maxfd + 1, &rdfd, NULL, NULL, &poll)) {
+ case -1:
+ msgq(sp, M_SYSERR, "select");
+ return (1);
+ case 0:
+ return (0);
+ default:
+ break;
+ }
+
+ /* Read the input. */
+ for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, SC_SCRIPT) &&
+ FD_ISSET(sp->script->sh_master, &rdfd) && sscr_insert(sp))
+ return (1);
+ goto loop;
+}
+
+/*
+ * sscr_insert --
+ * Take a line from the shell and insert it into the file.
+ */
+static int
+sscr_insert(sp)
+ SCR *sp;
+{
+ struct timeval tv;
+ CHAR_T *endp, *p, *t;
+ SCRIPT *sc;
+ fd_set rdfd;
+ recno_t lno;
+ size_t blen, len, tlen;
+ u_int value;
+ int nr, rval;
+ char *bp;
+
+ /* Find out where the end of the file is. */
+ if (db_last(sp, &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);
+ 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 = KEY_VAL(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ len = p - t;
+ if (db_append(sp, 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_ZERO(&rdfd);
+ FD_SET(sc->sh_master, &rdfd);
+ if (select(sc->sh_master + 1,
+ &rdfd, NULL, NULL, &tv) == 1) {
+ memmove(bp, t, len);
+ endp = bp + len;
+ goto more;
+ }
+ }
+ if (sscr_setprompt(sp, t, len))
+ return (1);
+ if (db_append(sp, 1, lno++, t, len))
+ goto ret;
+ }
+
+ /* The cursor moves to EOF. */
+ sp->lno = lno;
+ sp->cno = len ? len - 1 : 0;
+ rval = vs_refresh(sp, 1);
+
+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);
+ 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.
+ *
+ * PUBLIC: int sscr_end __P((SCR *));
+ */
+int
+sscr_end(sp)
+ SCR *sp;
+{
+ SCRIPT *sc;
+
+ if ((sc = sp->script) == NULL)
+ return (0);
+
+ /* Turn off the script flags. */
+ F_CLR(sp, SC_SCRIPT);
+ sscr_check(sp);
+
+ /* 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. */
+ (void)proc_wait(sp, (long)sc->sh_pid, "script-shell", 0, 0);
+
+ /* Free memory. */
+ free(sc->sh_prompt);
+ free(sc);
+ sp->script = NULL;
+
+ return (0);
+}
+
+/*
+ * sscr_check --
+ * Set/clear the global scripting bit.
+ */
+static void
+sscr_check(sp)
+ SCR *sp;
+{
+ GS *gp;
+
+ gp = sp->gp;
+ for (sp = gp->dq.cqh_first; sp != (void *)&gp->dq; sp = sp->q.cqe_next)
+ if (F_ISSET(sp, SC_SCRIPT)) {
+ F_SET(gp, G_SCRWIN);
+ return;
+ }
+ F_CLR(gp, G_SCRWIN);
+}
+
+#ifdef HAVE_SYS5_PTY
+static int ptys_open __P((int, char *));
+static int ptym_open __P((char *));
+
+static int
+sscr_pty(amaster, aslave, name, termp, winp)
+ int *amaster, *aslave;
+ char *name;
+ struct termios *termp;
+ void *winp;
+{
+ int master, slave, ttygid;
+
+ /* open master terminal */
+ if ((master = ptym_open(name)) < 0) {
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+ }
+
+ /* open slave terminal */
+ if ((slave = ptys_open(master, name)) >= 0) {
+ *amaster = master;
+ *aslave = slave;
+ } else {
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+ }
+
+ if (termp)
+ (void) tcsetattr(slave, TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+ if (winp != NULL)
+ (void) ioctl(slave, TIOCSWINSZ, (struct winsize *)winp);
+#endif
+ return (0);
+}
+
+/*
+ * ptym_open --
+ * This function opens a master pty and returns the file descriptor
+ * to it. pts_name is also returned which is the name of the slave.
+ */
+static int
+ptym_open(pts_name)
+ char *pts_name;
+{
+ int fdm;
+ char *ptr, *ptsname();
+
+ strcpy(pts_name, _PATH_SYSV_PTY);
+ if ((fdm = open(pts_name, O_RDWR)) < 0 )
+ return (-1);
+
+ if (grantpt(fdm) < 0) {
+ close(fdm);
+ return (-2);
+ }
+
+ if (unlockpt(fdm) < 0) {
+ close(fdm);
+ return (-3);
+ }
+
+ if (unlockpt(fdm) < 0) {
+ close(fdm);
+ return (-3);
+ }
+
+ /* get slave's name */
+ if ((ptr = ptsname(fdm)) == NULL) {
+ close(fdm);
+ return (-3);
+ }
+ strcpy(pts_name, ptr);
+ return (fdm);
+}
+
+/*
+ * ptys_open --
+ * This function opens the slave pty.
+ */
+static int
+ptys_open(fdm, pts_name)
+ int fdm;
+ char *pts_name;
+{
+ int fds;
+
+ if ((fds = open(pts_name, O_RDWR)) < 0) {
+ close(fdm);
+ return (-5);
+ }
+
+ if (ioctl(fds, I_PUSH, "ptem") < 0) {
+ close(fds);
+ close(fdm);
+ return (-6);
+ }
+
+ if (ioctl(fds, I_PUSH, "ldterm") < 0) {
+ close(fds);
+ close(fdm);
+ return (-7);
+ }
+
+ if (ioctl(fds, I_PUSH, "ttcompat") < 0) {
+ close(fds);
+ close(fdm);
+ return (-8);
+ }
+
+ return (fds);
+}
+
+#else /* !HAVE_SYS5_PTY */
+
+static int
+sscr_pty(amaster, aslave, name, termp, winp)
+ int *amaster, *aslave;
+ char *name;
+ struct termios *termp;
+ void *winp;
+{
+ static char line[] = "/dev/ptyXX";
+ register char *cp1, *cp2;
+ register int master, slave, ttygid;
+ struct group *gr;
+
+ if ((gr = getgrnam("tty")) != NULL)
+ ttygid = gr->gr_gid;
+ else
+ ttygid = -1;
+
+ for (cp1 = "pqrs"; *cp1; cp1++) {
+ line[8] = *cp1;
+ for (cp2 = "0123456789abcdef"; *cp2; cp2++) {
+ line[5] = 'p';
+ line[9] = *cp2;
+ if ((master = open(line, O_RDWR, 0)) == -1) {
+ if (errno == ENOENT)
+ return (-1); /* out of ptys */
+ } else {
+ line[5] = 't';
+ (void) chown(line, getuid(), ttygid);
+ (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+#ifdef HAVE_REVOKE
+ (void) revoke(line);
+#endif
+ if ((slave = open(line, O_RDWR, 0)) != -1) {
+ *amaster = master;
+ *aslave = slave;
+ if (name)
+ strcpy(name, line);
+ if (termp)
+ (void) tcsetattr(slave,
+ TCSAFLUSH, termp);
+#ifdef TIOCSWINSZ
+ if (winp)
+ (void) ioctl(slave, TIOCSWINSZ,
+ (char *)winp);
+#endif
+ return (0);
+ }
+ (void) close(master);
+ }
+ }
+ }
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+}
+#endif /* HAVE_SYS5_PTY */
diff --git a/contrib/nvi/ex/ex_set.c b/contrib/nvi/ex/ex_set.c
new file mode 100644
index 000000000000..11e929764242
--- /dev/null
+++ b/contrib/nvi/ex/ex_set.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_set.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_set -- :set
+ * Ex set option.
+ *
+ * PUBLIC: int ex_set __P((SCR *, EXCMD *));
+ */
+int
+ex_set(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ switch(cmdp->argc) {
+ case 0:
+ opts_dump(sp, CHANGED_DISPLAY);
+ break;
+ default:
+ if (opts_set(sp, cmdp->argv, cmdp->cmd->usage))
+ return (1);
+ break;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_shell.c b/contrib/nvi/ex/ex_shell.c
new file mode 100644
index 000000000000..95168033a13f
--- /dev/null
+++ b/contrib/nvi/ex/ex_shell.c
@@ -0,0 +1,378 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_shell.c 10.38 (Berkeley) 8/19/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+static const char *sigmsg __P((int));
+
+/*
+ * ex_shell -- :sh[ell]
+ * Invoke the program named in the SHELL environment variable
+ * with the argument -i.
+ *
+ * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
+ */
+int
+ex_shell(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ int rval;
+ char buf[MAXPATHLEN];
+
+ /* We'll need a shell. */
+ if (opts_empty(sp, O_SHELL, 0))
+ return (1);
+
+ /*
+ * XXX
+ * Assumes all shells use -i.
+ */
+ (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
+
+ /* Restore the window name. */
+ (void)sp->gp->scr_rename(sp, NULL, 0);
+
+ /* If we're still in a vi screen, move out explicitly. */
+ rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
+
+ /* Set the window name. */
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+
+ /*
+ * !!!
+ * Historically, vi didn't require a continue message after the
+ * return of the shell. Match it.
+ */
+ F_SET(sp, SC_EX_WAIT_NO);
+
+ return (rval);
+}
+
+/*
+ * ex_exec_proc --
+ * Run a separate process.
+ *
+ * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int));
+ */
+int
+ex_exec_proc(sp, cmdp, cmd, msg, need_newline)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *cmd;
+ const char *msg;
+ int need_newline;
+{
+ GS *gp;
+ const char *name;
+ pid_t pid;
+
+ gp = sp->gp;
+
+ /* We'll need a shell. */
+ if (opts_empty(sp, O_SHELL, 0))
+ return (1);
+
+ /* Enter ex mode. */
+ if (F_ISSET(sp, SC_VI)) {
+ if (gp->scr_screen(sp, SC_EX)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
+ return (1);
+ }
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
+ F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
+ }
+
+ /* Put out additional newline, message. */
+ if (need_newline)
+ (void)ex_puts(sp, "\n");
+ if (msg != NULL) {
+ (void)ex_puts(sp, msg);
+ (void)ex_puts(sp, "\n");
+ }
+ (void)ex_fflush(sp);
+
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ msgq(sp, M_SYSERR, "vfork");
+ return (1);
+ case 0: /* Utility. */
+ 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_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
+ _exit(127);
+ /* NOTREACHED */
+ default: /* Parent. */
+ return (proc_wait(sp, (long)pid, cmd, 0, 0));
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * 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.
+ *
+ * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
+ */
+int
+proc_wait(sp, pid, cmd, silent, okpipe)
+ SCR *sp;
+ long pid;
+ const char *cmd;
+ int silent, okpipe;
+{
+ size_t len;
+ int nf, pstat;
+ char *p;
+
+ /* Wait for the utility, ignoring interruptions. */
+ for (;;) {
+ errno = 0;
+ if (waitpid((pid_t)pid, &pstat, 0) != -1)
+ break;
+ if (errno != EINTR) {
+ msgq(sp, M_SYSERR, "waitpid");
+ return (1);
+ }
+ }
+
+ /*
+ * 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);
+ p = msg_print(sp, cmd, &nf);
+ len = strlen(p);
+ msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
+ MIN(len, 20), p, len > 20 ? " ..." : "",
+ sigmsg(WTERMSIG(pstat)),
+ WCOREDUMP(pstat) ? "; core dumped" : "");
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ return (1);
+ }
+
+ if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
+ /*
+ * Remain silent for "normal" errors when doing shell file
+ * name expansions, they almost certainly indicate nothing
+ * more than a failure to match.
+ *
+ * Remain silent for vi read filter errors. It's historic
+ * practice.
+ */
+ if (!silent) {
+ for (; isblank(*cmd); ++cmd);
+ p = msg_print(sp, cmd, &nf);
+ len = strlen(p);
+ msgq(sp, M_ERR, "%.*s%s: exited with status %d",
+ MIN(len, 20), p, len > 20 ? " ..." : "",
+ WEXITSTATUS(pstat));
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ }
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * XXX
+ * The sys_siglist[] table in the C library has this information, but there's
+ * no portable way to get to it. (Believe me, I tried.)
+ */
+typedef struct _sigs {
+ int number; /* signal number */
+ char *message; /* related message */
+} SIGS;
+
+SIGS const sigs[] = {
+#ifdef SIGABRT
+ SIGABRT, "Abort trap",
+#endif
+#ifdef SIGALRM
+ SIGALRM, "Alarm clock",
+#endif
+#ifdef SIGBUS
+ SIGBUS, "Bus error",
+#endif
+#ifdef SIGCLD
+ SIGCLD, "Child exited or stopped",
+#endif
+#ifdef SIGCHLD
+ SIGCHLD, "Child exited",
+#endif
+#ifdef SIGCONT
+ SIGCONT, "Continued",
+#endif
+#ifdef SIGDANGER
+ SIGDANGER, "System crash imminent",
+#endif
+#ifdef SIGEMT
+ SIGEMT, "EMT trap",
+#endif
+#ifdef SIGFPE
+ SIGFPE, "Floating point exception",
+#endif
+#ifdef SIGGRANT
+ SIGGRANT, "HFT monitor mode granted",
+#endif
+#ifdef SIGHUP
+ SIGHUP, "Hangup",
+#endif
+#ifdef SIGILL
+ SIGILL, "Illegal instruction",
+#endif
+#ifdef SIGINFO
+ SIGINFO, "Information request",
+#endif
+#ifdef SIGINT
+ SIGINT, "Interrupt",
+#endif
+#ifdef SIGIO
+ SIGIO, "I/O possible",
+#endif
+#ifdef SIGIOT
+ SIGIOT, "IOT trap",
+#endif
+#ifdef SIGKILL
+ SIGKILL, "Killed",
+#endif
+#ifdef SIGLOST
+ SIGLOST, "Record lock",
+#endif
+#ifdef SIGMIGRATE
+ SIGMIGRATE, "Migrate process to another CPU",
+#endif
+#ifdef SIGMSG
+ SIGMSG, "HFT input data pending",
+#endif
+#ifdef SIGPIPE
+ SIGPIPE, "Broken pipe",
+#endif
+#ifdef SIGPOLL
+ SIGPOLL, "I/O possible",
+#endif
+#ifdef SIGPRE
+ SIGPRE, "Programming error",
+#endif
+#ifdef SIGPROF
+ SIGPROF, "Profiling timer expired",
+#endif
+#ifdef SIGPWR
+ SIGPWR, "Power failure imminent",
+#endif
+#ifdef SIGRETRACT
+ SIGRETRACT, "HFT monitor mode retracted",
+#endif
+#ifdef SIGQUIT
+ SIGQUIT, "Quit",
+#endif
+#ifdef SIGSAK
+ SIGSAK, "Secure Attention Key",
+#endif
+#ifdef SIGSEGV
+ SIGSEGV, "Segmentation fault",
+#endif
+#ifdef SIGSOUND
+ SIGSOUND, "HFT sound sequence completed",
+#endif
+#ifdef SIGSTOP
+ SIGSTOP, "Suspended (signal)",
+#endif
+#ifdef SIGSYS
+ SIGSYS, "Bad system call",
+#endif
+#ifdef SIGTERM
+ SIGTERM, "Terminated",
+#endif
+#ifdef SIGTRAP
+ SIGTRAP, "Trace/BPT trap",
+#endif
+#ifdef SIGTSTP
+ SIGTSTP, "Suspended",
+#endif
+#ifdef SIGTTIN
+ SIGTTIN, "Stopped (tty input)",
+#endif
+#ifdef SIGTTOU
+ SIGTTOU, "Stopped (tty output)",
+#endif
+#ifdef SIGURG
+ SIGURG, "Urgent I/O condition",
+#endif
+#ifdef SIGUSR1
+ SIGUSR1, "User defined signal 1",
+#endif
+#ifdef SIGUSR2
+ SIGUSR2, "User defined signal 2",
+#endif
+#ifdef SIGVTALRM
+ SIGVTALRM, "Virtual timer expired",
+#endif
+#ifdef SIGWINCH
+ SIGWINCH, "Window size changes",
+#endif
+#ifdef SIGXCPU
+ SIGXCPU, "Cputime limit exceeded",
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ, "Filesize limit exceeded",
+#endif
+};
+
+/*
+ * sigmsg --
+ * Return a pointer to a message describing a signal.
+ */
+static const char *
+sigmsg(signo)
+ int signo;
+{
+ static char buf[40];
+ const SIGS *sigp;
+ int n;
+
+ for (n = 0,
+ sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp)
+ if (sigp->number == signo)
+ return (sigp->message);
+ (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
+ return (buf);
+}
diff --git a/contrib/nvi/ex/ex_shift.c b/contrib/nvi/ex/ex_shift.c
new file mode 100644
index 000000000000..83bd36d12a7d
--- /dev/null
+++ b/contrib/nvi/ex/ex_shift.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_shift.c 10.11 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+enum which {LEFT, RIGHT};
+static int shift __P((SCR *, EXCMD *, enum which));
+
+/*
+ * ex_shiftl -- :<[<...]
+ *
+ *
+ * PUBLIC: int ex_shiftl __P((SCR *, EXCMD *));
+ */
+int
+ex_shiftl(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (shift(sp, cmdp, LEFT));
+}
+
+/*
+ * ex_shiftr -- :>[>...]
+ *
+ * PUBLIC: int ex_shiftr __P((SCR *, EXCMD *));
+ */
+int
+ex_shiftr(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (shift(sp, cmdp, RIGHT));
+}
+
+/*
+ * shift --
+ * Ex shift support.
+ */
+static int
+shift(sp, cmdp, rl)
+ SCR *sp;
+ EXCMD *cmdp;
+ enum which rl;
+{
+ recno_t from, to;
+ size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
+ int curset;
+ char *p, *bp, *tbp;
+
+ NEEDFILE(sp, cmdp);
+
+ if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
+ msgq(sp, M_INFO, "152|shiftwidth option set to 0");
+ return (0);
+ }
+
+ /* Copy the lines being shifted into the unnamed buffer. */
+ if (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
+ return (1);
+
+ /*
+ * 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 (db_get(sp, from, DBG_FATAL, &p, &len))
+ 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. */
+ memcpy(tbp, p + oldidx, len - oldidx);
+
+ /* Set the replacement line. */
+ if (db_set(sp, 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, to, &sp->cno);
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ sp->rptlines[L_SHIFT] += cmdp->addr2.lno - cmdp->addr1.lno + 1;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_source.c b/contrib/nvi/ex/ex_source.c
new file mode 100644
index 000000000000..b52c527716fd
--- /dev/null
+++ b/contrib/nvi/ex/ex_source.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_source.c 10.12 (Berkeley) 8/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_source -- :source file
+ * Execute ex commands from a file.
+ *
+ * PUBLIC: int ex_source __P((SCR *, EXCMD *));
+ */
+int
+ex_source(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ struct stat sb;
+ int fd, len;
+ char *bp, *name;
+
+ name = cmdp->argv[0]->bp;
+ if ((fd = open(name, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ goto err;
+
+ /*
+ * XXX
+ * I'd like to test to see if the file is too large 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. So, put an fairly unreasonable limit on it,
+ * I don't want to be dropping core here.
+ */
+#define MEGABYTE 1048576
+ if (sb.st_size > MEGABYTE) {
+ errno = ENOMEM;
+ goto err;
+ }
+
+ MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);
+ if (bp == NULL) {
+ (void)close(fd);
+ return (1);
+ }
+ bp[sb.st_size] = '\0';
+
+ /* Read the file into memory. */
+ len = read(fd, bp, (int)sb.st_size);
+ (void)close(fd);
+ if (len == -1 || len != sb.st_size) {
+ if (len != sb.st_size)
+ errno = EIO;
+ free(bp);
+err: msgq_str(sp, M_SYSERR, name, "%s");
+ return (1);
+ }
+
+ /* Put it on the ex queue. */
+ return (ex_run_str(sp, name, bp, (size_t)sb.st_size, 1, 1));
+}
diff --git a/contrib/nvi/ex/ex_stop.c b/contrib/nvi/ex/ex_stop.c
new file mode 100644
index 000000000000..bc55fd24ccb7
--- /dev/null
+++ b/contrib/nvi/ex/ex_stop.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_stop.c 10.10 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_stop -- :stop[!]
+ * :suspend[!]
+ * Suspend execution.
+ *
+ * PUBLIC: int ex_stop __P((SCR *, EXCMD *));
+ */
+int
+ex_stop(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ int allowed;
+
+ /* For some strange reason, the force flag turns off autowrite. */
+ if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && file_aw(sp, FS_ALL))
+ return (1);
+
+ if (sp->gp->scr_suspend(sp, &allowed))
+ return (1);
+ if (!allowed)
+ ex_emsg(sp, NULL, EXM_NOSUSPEND);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_subst.c b/contrib/nvi/ex/ex_subst.c
new file mode 100644
index 000000000000..0ebb81dd58e7
--- /dev/null
+++ b/contrib/nvi/ex/ex_subst.c
@@ -0,0 +1,1459 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_subst.c 10.37 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */
+#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */
+
+static int re_conv __P((SCR *, char **, size_t *, int *));
+static int re_cscope_conv __P((SCR *, char **, size_t *, int *));
+static int re_sub __P((SCR *,
+ char *, char **, size_t *, size_t *, regmatch_t [10]));
+static int re_tag_conv __P((SCR *, char **, size_t *, int *));
+static int s __P((SCR *, EXCMD *, char *, regex_t *, u_int));
+
+/*
+ * ex_s --
+ * [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
+ *
+ * Substitute on lines matching a pattern.
+ *
+ * PUBLIC: int ex_s __P((SCR *, EXCMD *));
+ */
+int
+ex_s(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ regex_t *re;
+ size_t blen, len;
+ u_int flags;
+ int delim;
+ 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.
+ */
+ if (cmdp->argc == 0)
+ goto subagain;
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len; len > 0; --len, ++p) {
+ if (!isblank(*p))
+ break;
+ }
+ if (len == 0)
+subagain: return (ex_subagain(sp, cmdp));
+
+ delim = *p++;
+ if (isalnum(delim) || delim == '\\')
+ return (s(sp, cmdp, p, &sp->subre_c, SUB_MUSTSETR));
+
+ /*
+ * !!!
+ * The full-blown substitute command reset the remembered
+ * state of the 'c' and 'g' suffices.
+ */
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * Get the pattern string, toss escaping 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 escaping 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 escaping 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] == '\\')
+ if (p[1] == delim)
+ ++p;
+ else if (p[1] == '\\')
+ *t++ = *p++;
+ *t++ = *p++;
+ }
+
+ /*
+ * If the pattern string is empty, use the last RE (not just the
+ * last substitution RE).
+ */
+ if (*ptrn == '\0') {
+ if (sp->re == NULL) {
+ ex_emsg(sp, NULL, EXM_NOPREVRE);
+ return (1);
+ }
+
+ /* Re-compile the RE if necessary. */
+ if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
+ sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
+ return (1);
+ flags = 0;
+ } else {
+ /*
+ * !!!
+ * Compile the RE. Historic practice is that substitutes set
+ * the search direction as well as both substitute and search
+ * RE's. We compile the RE twice, as we don't want to bother
+ * ref counting the pattern string and (opaque) structure.
+ */
+ if (re_compile(sp, ptrn, t - ptrn,
+ &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH))
+ return (1);
+ if (re_compile(sp, ptrn, t - ptrn,
+ &sp->subre, &sp->subre_len, &sp->subre_c, RE_C_SUBST))
+ return (1);
+
+ flags = SUB_FIRST;
+ sp->searchdir = FORWARD;
+ }
+ re = &sp->re_c;
+
+ /*
+ * Get the replacement string.
+ *
+ * The special character & (\& if O_MAGIC not set) matches the
+ * entire RE. No handling of & is required here, it's done by
+ * re_sub().
+ *
+ * The special character ~ (\~ if O_MAGIC not set) inserts the
+ * previous replacement string into this replacement string.
+ * 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.
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escaping character if it escapes a delimiter or
+ * if O_MAGIC is set and it escapes a tilde.
+ *
+ * !!!
+ * If the entire replacement pattern is "%", then use the last
+ * replacement pattern. This semantic was added to vi in System
+ * V and then percolated elsewhere, presumably around the time
+ * that it was added to their version of ed(1).
+ */
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ if (sp->repl != NULL)
+ free(sp->repl);
+ sp->repl = NULL;
+ sp->repl_len = 0;
+ } else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim))
+ p += p[1] == delim ? 2 : 1;
+ else {
+ 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] == delim)
+ ++p;
+ else if (p[1] == '\\') {
+ *t++ = *p++;
+ ++len;
+ } else if (p[1] == '~') {
+ ++p;
+ if (!O_ISSET(sp, O_MAGIC))
+ goto tilde;
+ }
+ } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
+tilde: ++p;
+ memcpy(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ len += sp->repl_len;
+ continue;
+ }
+ *t++ = *p++;
+ ++len;
+ }
+ if ((sp->repl_len = len) != 0) {
+ if (sp->repl != NULL)
+ free(sp->repl);
+ if ((sp->repl = malloc(len)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ memcpy(sp->repl, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+ }
+ return (s(sp, cmdp, p, re, flags));
+}
+
+/*
+ * ex_subagain --
+ * [line [,line]] & [cgr] [count] [#lp]]
+ *
+ * Substitute using the last substitute RE and replacement pattern.
+ *
+ * PUBLIC: int ex_subagain __P((SCR *, EXCMD *));
+ */
+int
+ex_subagain(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (sp->subre == NULL) {
+ ex_emsg(sp, NULL, EXM_NOPREVRE);
+ return (1);
+ }
+ if (!F_ISSET(sp, SC_RE_SUBST) && re_compile(sp,
+ sp->subre, sp->subre_len, NULL, NULL, &sp->subre_c, RE_C_SUBST))
+ return (1);
+ return (s(sp,
+ cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->subre_c, 0));
+}
+
+/*
+ * ex_subtilde --
+ * [line [,line]] ~ [cgr] [count] [#lp]]
+ *
+ * Substitute using the last RE and last substitute replacement pattern.
+ *
+ * PUBLIC: int ex_subtilde __P((SCR *, EXCMD *));
+ */
+int
+ex_subtilde(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (sp->re == NULL) {
+ ex_emsg(sp, NULL, EXM_NOPREVRE);
+ return (1);
+ }
+ if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
+ sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))
+ return (1);
+ return (s(sp,
+ cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->re_c, 0));
+}
+
+/*
+ * s --
+ * 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.
+ *
+ * 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 *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); \
+ } \
+ } \
+ memcpy(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; \
+ } \
+}
+
+static int
+s(sp, cmdp, s, re, flags)
+ SCR *sp;
+ EXCMD *cmdp;
+ char *s;
+ regex_t *re;
+ u_int flags;
+{
+ EVENT ev;
+ MARK from, to;
+ TEXTH tiq;
+ recno_t elno, lno, slno;
+ regmatch_t match[10];
+ size_t blen, cnt, last, lbclen, lblen, len, llen;
+ size_t offset, saved_offset, scno;
+ int cflag, lflag, nflag, pflag, rflag;
+ int didsub, do_eol_match, eflags, empty_ok, eval;
+ int linechanged, matched, quit, rval;
+ char *bp, *lb;
+
+ NEEDFILE(sp, cmdp);
+
+ slno = sp->lno;
+ scno = sp->cno;
+
+ /*
+ * !!!
+ * Historically, the 'g' and 'c' suffices were always toggled as flags,
+ * so ":s/A/B/" was the same as ":s/A/B/ccgg". If O_EDCOMPATIBLE was
+ * not set, they were initialized to 0 for all substitute commands. If
+ * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
+ * specified substitute/replacement patterns (see ex_s()).
+ */
+ if (!O_ISSET(sp, O_EDCOMPATIBLE))
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * 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 = lflag = nflag = pflag = rflag = 0;
+ if (s == NULL)
+ goto noargs;
+ for (lno = OOBLNO; *s != '\0'; ++s)
+ switch (*s) {
+ case ' ':
+ case '\t':
+ continue;
+ case '+':
+ ++cmdp->flagoff;
+ break;
+ case '-':
+ --cmdp->flagoff;
+ 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, "153|Count overflow");
+ else if (lno == LONG_MIN)
+ msgq(sp, M_ERR, "154|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;
+ if (!db_exist(sp, cmdp->addr2.lno) &&
+ db_last(sp, &cmdp->addr2.lno))
+ return (1);
+ break;
+ case '#':
+ nflag = 1;
+ break;
+ case 'c':
+ sp->c_suffix = !sp->c_suffix;
+
+ /* Ex text structure initialization. */
+ if (F_ISSET(sp, SC_EX)) {
+ memset(&tiq, 0, sizeof(TEXTH));
+ CIRCLEQ_INIT(&tiq);
+ }
+ break;
+ case 'g':
+ sp->g_suffix = !sp->g_suffix;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ if (LF_ISSET(SUB_FIRST)) {
+ msgq(sp, M_ERR,
+ "155|Regular expression specified; r flag meaningless");
+ return (1);
+ }
+ if (!F_ISSET(sp, SC_RE_SEARCH)) {
+ ex_emsg(sp, NULL, EXM_NOPREVRE);
+ return (1);
+ }
+ rflag = 1;
+ re = &sp->re_c;
+ break;
+ default:
+ goto usage;
+ }
+
+ if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) {
+usage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+
+noargs: if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
+ msgq(sp, M_ERR,
+"156|The #, l and p flags may not be combined with the c flag in vi mode");
+ return (1);
+ }
+
+ /*
+ * 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 (INTERRUPTED(sp))
+ break;
+
+ /* Get the line. */
+ if (db_get(sp, lno, DBG_FATAL, &s, &llen))
+ goto err;
+
+ /*
+ * Make a local copy if doing confirmation -- when calling
+ * the confirm routine we're likely to lose the cached copy.
+ */
+ if (sp->c_suffix) {
+ if (bp == NULL) {
+ GET_SPACE_RET(sp, bp, blen, llen);
+ } else
+ ADD_SPACE_RET(sp, bp, blen, llen);
+ memcpy(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, 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: match[0].rm_so = 0;
+ match[0].rm_eo = len;
+
+ /* Get the next match. */
+ eval = regexec(re, (char *)s + offset, 10, 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 err;
+ }
+ 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 && match[0].rm_so == 0 && 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 (sp->c_suffix) {
+ /*
+ * Set the cursor position for confirmation. Note,
+ * if we matched on a '$', the cursor may be past
+ * the end of line.
+ */
+ from.lno = to.lno = lno;
+ from.cno = match[0].rm_so + offset;
+ to.cno = match[0].rm_eo + offset;
+ /*
+ * Both ex and vi have to correct for a change before
+ * the first character in the line.
+ */
+ if (llen == 0)
+ from.cno = to.cno = 0;
+ if (F_ISSET(sp, SC_VI)) {
+ /*
+ * Only vi has to correct for a change after
+ * the last character in the line.
+ *
+ * XXX
+ * It would be nice to change the vi code so
+ * that we could display a cursor past EOL.
+ */
+ if (to.cno >= llen)
+ to.cno = llen - 1;
+ if (from.cno >= llen)
+ from.cno = llen - 1;
+
+ sp->lno = from.lno;
+ sp->cno = from.cno;
+ if (vs_refresh(sp, 1))
+ goto err;
+
+ vs_update(sp, msg_cat(sp,
+ "169|Confirm change? [n]", NULL), NULL);
+
+ if (v_event_get(sp, &ev, 0, 0))
+ goto err;
+ switch (ev.e_event) {
+ case E_CHARACTER:
+ break;
+ case E_EOF:
+ case E_ERR:
+ case E_INTERRUPT:
+ goto lquit;
+ default:
+ v_event_err(sp, &ev);
+ goto lquit;
+ }
+ } else {
+ if (ex_print(sp, cmdp, &from, &to, 0) ||
+ ex_scprint(sp, &from, &to))
+ goto lquit;
+ if (ex_txt(sp, &tiq, 0, TXT_CR))
+ goto err;
+ ev.e_c = tiq.cqh_first->lb[0];
+ }
+
+ switch (ev.e_c) {
+ case CH_YES:
+ break;
+ default:
+ case CH_NO:
+ didsub = 0;
+ BUILD(sp, s +offset, match[0].rm_eo);
+ goto skip;
+ case CH_QUIT:
+ /* Set the quit/interrupted flags. */
+lquit: quit = 1;
+ F_SET(sp->gp, G_INTERRUPTED);
+
+ /*
+ * Resolve any changes, then return to (and
+ * exit from) the main loop.
+ */
+ goto endmatch;
+ }
+ }
+
+ /*
+ * Set the cursor to the last position changed, converting
+ * from 1-based to 0-based.
+ */
+ sp->lno = lno;
+ sp->cno = match[0].rm_so;
+
+ /* Copy the bytes before the match into the build buffer. */
+ BUILD(sp, s + offset, match[0].rm_so);
+
+ /* Substitute the matching bytes. */
+ didsub = 1;
+ if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match))
+ goto err;
+
+ /* Set the change flag so we know this line was modified. */
+ linechanged = 1;
+
+ /* Move past the matched bytes. */
+skip: offset += match[0].rm_eo;
+ len -= 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 && sp->c_suffix && sp->g_suffix) {
+ /*
+ * 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 (db_insert(sp, lno,
+ lb + last, sp->newl[cnt] - last))
+ goto err;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ offset -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store and retrieve the line. */
+ if (db_set(sp, lno, lb + last, lbclen))
+ goto err;
+ if (db_get(sp, lno, DBG_FATAL, &s, &llen))
+ goto err;
+ ADD_SPACE_RET(sp, bp, blen, llen)
+ memcpy(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 (sp->g_suffix && 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 (db_insert(sp,
+ lno, lb + last, sp->newl[cnt] - last))
+ goto err;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store the changed line. */
+ if (db_set(sp, lno, lb + last, lbclen))
+ goto err;
+
+ /* Update changed line counter. */
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+
+ /*
+ * !!!
+ * Display as necessary. Historic practice is to only
+ * display the last line of a line split into multiple
+ * lines.
+ */
+ if (lflag || nflag || pflag) {
+ from.lno = to.lno = lno;
+ from.cno = to.cno = 0;
+ if (lflag)
+ (void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
+ if (nflag)
+ (void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
+ if (pflag)
+ (void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
+ }
+ }
+
+ /*
+ * !!!
+ * Historically, vi attempted to leave the cursor at the same place if
+ * the substitution was done at the current cursor position. Otherwise
+ * it moved it to the first non-blank of the last line changed. There
+ * were some problems: for example, :s/$/foo/ with the cursor on the
+ * last character of the line left the cursor on the last character, or
+ * the & command with multiple occurrences of the matching string in the
+ * line usually left the cursor in a fairly random position.
+ *
+ * We try to do the same thing, with the exception that if the user is
+ * doing substitution with confirmation, we move to the last line about
+ * which the user was consulted, as opposed to the last line that they
+ * actually changed. This prevents a screen flash if the user doesn't
+ * change many of the possible lines.
+ */
+ if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ }
+
+ /*
+ * If not in a global command, and nothing matched, say so.
+ * Else, if none of the lines displayed, put something up.
+ */
+ rval = 0;
+ if (!matched) {
+ if (!F_ISSET(sp, SC_EX_GLOBAL)) {
+ msgq(sp, M_ERR, "157|No match found");
+ goto err;
+ }
+ } else if (!lflag && !nflag && !pflag)
+ F_SET(cmdp, E_AUTOPRINT);
+
+ if (0) {
+err: rval = 1;
+ }
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ if (lb != NULL)
+ free(lb);
+ return (rval);
+}
+
+/*
+ * re_compile --
+ * Compile the RE.
+ *
+ * PUBLIC: int re_compile __P((SCR *,
+ * PUBLIC: char *, size_t, char **, size_t *, regex_t *, u_int));
+ */
+int
+re_compile(sp, ptrn, plen, ptrnp, lenp, rep, flags)
+ SCR *sp;
+ char *ptrn, **ptrnp;
+ size_t plen, *lenp;
+ regex_t *rep;
+ u_int flags;
+{
+ size_t len;
+ int reflags, replaced, rval;
+ char *p;
+
+ /* Set RE flags. */
+ reflags = 0;
+ if (!LF_ISSET(RE_C_CSCOPE | RE_C_TAG)) {
+ if (O_ISSET(sp, O_EXTENDED))
+ reflags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ reflags |= REG_ICASE;
+ if (O_ISSET(sp, O_ICLOWER)) {
+ for (p = ptrn, len = plen; len > 0; ++p, --len)
+ if (isupper(*p))
+ break;
+ if (len == 0)
+ reflags |= REG_ICASE;
+ }
+ }
+
+ /* If we're replacing a saved value, clear the old one. */
+ if (LF_ISSET(RE_C_SEARCH) && F_ISSET(sp, SC_RE_SEARCH)) {
+ regfree(&sp->re_c);
+ F_CLR(sp, SC_RE_SEARCH);
+ }
+ if (LF_ISSET(RE_C_SUBST) && F_ISSET(sp, SC_RE_SUBST)) {
+ regfree(&sp->subre_c);
+ F_CLR(sp, SC_RE_SUBST);
+ }
+
+ /*
+ * If we're saving the string, it's a pattern we haven't seen before,
+ * so convert the vi-style RE's to POSIX 1003.2 RE's. Save a copy for
+ * later recompilation. Free any previously saved value.
+ */
+ if (ptrnp != NULL) {
+ if (LF_ISSET(RE_C_CSCOPE)) {
+ if (re_cscope_conv(sp, &ptrn, &plen, &replaced))
+ return (1);
+ /*
+ * XXX
+ * Currently, the match-any-<blank> expression used in
+ * re_cscope_conv() requires extended RE's. This may
+ * not be right or safe.
+ */
+ reflags |= REG_EXTENDED;
+ } else if (LF_ISSET(RE_C_TAG)) {
+ if (re_tag_conv(sp, &ptrn, &plen, &replaced))
+ return (1);
+ } else
+ if (re_conv(sp, &ptrn, &plen, &replaced))
+ return (1);
+
+ /* Discard previous pattern. */
+ if (*ptrnp != NULL) {
+ free(*ptrnp);
+ *ptrnp = NULL;
+ }
+ if (lenp != NULL)
+ *lenp = plen;
+
+ /*
+ * Copy the string into allocated memory.
+ *
+ * XXX
+ * Regcomp isn't 8-bit clean, so the pattern is nul-terminated
+ * for now. There's just no other solution.
+ */
+ MALLOC(sp, *ptrnp, char *, plen + 1);
+ if (*ptrnp != NULL) {
+ memcpy(*ptrnp, ptrn, plen);
+ (*ptrnp)[plen] = '\0';
+ }
+
+ /* Free up conversion-routine-allocated memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+
+ if (*ptrnp == NULL)
+ return (1);
+
+ ptrn = *ptrnp;
+ }
+
+ /*
+ * XXX
+ * Regcomp isn't 8-bit clean, so we just lost if the pattern
+ * contained a nul. Bummer!
+ */
+ if ((rval = regcomp(rep, ptrn, /* plen, */ reflags)) != 0) {
+ if (!LF_ISSET(RE_C_SILENT))
+ re_error(sp, rval, rep);
+ return (1);
+ }
+
+ if (LF_ISSET(RE_C_SEARCH))
+ F_SET(sp, SC_RE_SEARCH);
+ if (LF_ISSET(RE_C_SUBST))
+ F_SET(sp, SC_RE_SUBST);
+
+ return (0);
+}
+
+/*
+ * 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.
+ *
+ * !!!/XXX
+ * This doesn't exactly match the historic behavior of vi because we do
+ * the ~ substitution before calling the RE engine, so magic characters
+ * in the replacement string will be expanded by the RE engine, and they
+ * weren't historically. It's a bug.
+ */
+static int
+re_conv(sp, ptrnp, plenp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ size_t *plenp;
+ int *replacedp;
+{
+ size_t blen, len, 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 most of the memory allocation and copies.
+ */
+ magic = 0;
+ for (p = *ptrnp, len = *plenp, needlen = 0; len > 0; ++p, --len)
+ switch (*p) {
+ case '\\':
+ if (len > 1) {
+ --len;
+ 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 '*':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += 1;
+ }
+ break;
+ default:
+ needlen += 2;
+ }
+ } else
+ needlen += 1;
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += sp->repl_len;
+ }
+ break;
+ 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. */
+ *replacedp = 1;
+ GET_SPACE_RET(sp, bp, blen, needlen);
+
+ for (p = *ptrnp, len = *plenp, t = bp; len > 0; ++p, --len)
+ switch (*p) {
+ case '\\':
+ if (len > 1) {
+ --len;
+ switch (*++p) {
+ case '<':
+ memcpy(t,
+ RE_WSTART, sizeof(RE_WSTART) - 1);
+ t += sizeof(RE_WSTART) - 1;
+ break;
+ case '>':
+ memcpy(t,
+ RE_WSTOP, sizeof(RE_WSTOP) - 1);
+ t += sizeof(RE_WSTOP) - 1;
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '~';
+ else {
+ memcpy(t,
+ sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case '*':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = '\\';
+ *t++ = *p;
+ }
+ } else
+ *t++ = '\\';
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ memcpy(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ } else
+ *t++ = '~';
+ break;
+ case '.':
+ case '[':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = *p;
+ break;
+ }
+
+ *ptrnp = bp;
+ *plenp = t - bp;
+ return (0);
+}
+
+/*
+ * re_tag_conv --
+ * Convert a tags search path into something that the POSIX
+ * 1003.2 RE functions can handle.
+ */
+static int
+re_tag_conv(sp, ptrnp, plenp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ size_t *plenp;
+ int *replacedp;
+{
+ size_t blen, len;
+ int lastdollar;
+ char *bp, *p, *t;
+
+ len = *plenp;
+
+ /* Max memory usage is 2 times the length of the string. */
+ *replacedp = 1;
+ GET_SPACE_RET(sp, bp, blen, len * 2);
+
+ p = *ptrnp;
+ t = bp;
+
+ /* If the last character is a '/' or '?', we just strip it. */
+ if (len > 0 && (p[len - 1] == '/' || p[len - 1] == '?'))
+ --len;
+
+ /* If the next-to-last or last character is a '$', it's magic. */
+ if (len > 0 && p[len - 1] == '$') {
+ --len;
+ lastdollar = 1;
+ } else
+ lastdollar = 0;
+
+ /* If the first character is a '/' or '?', we just strip it. */
+ if (len > 0 && (p[0] == '/' || p[0] == '?')) {
+ ++p;
+ --len;
+ }
+
+ /* If the first or second character is a '^', it's magic. */
+ if (p[0] == '^') {
+ *t++ = *p++;
+ --len;
+ }
+
+ /*
+ * Escape every other magic character we can find, meanwhile stripping
+ * the backslashes ctags inserts when escaping the search delimiter
+ * characters.
+ */
+ for (; len > 0; --len) {
+ if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) {
+ ++p;
+ --len;
+ } else if (strchr("^.[]$*", p[0]))
+ *t++ = '\\';
+ *t++ = *p++;
+ }
+ if (lastdollar)
+ *t++ = '$';
+
+ *ptrnp = bp;
+ *plenp = t - bp;
+ return (0);
+}
+
+/*
+ * re_cscope_conv --
+ * Convert a cscope search path into something that the POSIX
+ * 1003.2 RE functions can handle.
+ */
+static int
+re_cscope_conv(sp, ptrnp, plenp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ size_t *plenp;
+ int *replacedp;
+{
+ size_t blen, len, nspaces;
+ char *bp, *p, *t;
+
+ /*
+ * Each space in the source line printed by cscope represents an
+ * arbitrary sequence of spaces, tabs, and comments.
+ */
+#define CSCOPE_RE_SPACE "([ \t]|/\\*([^*]|\\*/)*\\*/)*"
+ for (nspaces = 0, p = *ptrnp, len = *plenp; len > 0; ++p, --len)
+ if (*p == ' ')
+ ++nspaces;
+
+ /*
+ * Allocate plenty of space:
+ * the string, plus potential escaping characters;
+ * nspaces + 2 copies of CSCOPE_RE_SPACE;
+ * ^, $, nul terminator characters.
+ */
+ *replacedp = 1;
+ len = (p - *ptrnp) * 2 + (nspaces + 2) * sizeof(CSCOPE_RE_SPACE) + 3;
+ GET_SPACE_RET(sp, bp, blen, len);
+
+ p = *ptrnp;
+ t = bp;
+
+ *t++ = '^';
+ memcpy(t, CSCOPE_RE_SPACE, sizeof(CSCOPE_RE_SPACE) - 1);
+ t += sizeof(CSCOPE_RE_SPACE) - 1;
+
+ for (len = *plenp; len > 0; ++p, --len)
+ if (*p == ' ') {
+ memcpy(t, CSCOPE_RE_SPACE, sizeof(CSCOPE_RE_SPACE) - 1);
+ t += sizeof(CSCOPE_RE_SPACE) - 1;
+ } else {
+ if (strchr("\\^.[]$*+?()|{}", *p))
+ *t++ = '\\';
+ *t++ = *p;
+ }
+
+ memcpy(t, CSCOPE_RE_SPACE, sizeof(CSCOPE_RE_SPACE) - 1);
+ t += sizeof(CSCOPE_RE_SPACE) - 1;
+ *t++ = '$';
+
+ *ptrnp = bp;
+ *plenp = t - bp;
+ return (0);
+}
+
+/*
+ * re_error --
+ * Report a regular expression error.
+ *
+ * PUBLIC: void re_error __P((SCR *, int, regex_t *));
+ */
+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);
+ }
+}
+
+/*
+ * re_sub --
+ * Do the substitution for a regular expression.
+ */
+static int
+re_sub(sp, ip, lbp, lbclenp, lblenp, match)
+ SCR *sp;
+ char *ip; /* Input line. */
+ char **lbp;
+ size_t *lbclenp, *lblenp;
+ regmatch_t match[10];
+{
+ 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 escaping characters. This (hopefully) matches historic practice.
+ */
+#define OUTCH(ch, nltrans) { \
+ CHAR_T __ch = (ch); \
+ u_int __value = KEY_VAL(sp, __ch); \
+ if (nltrans && (__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 '&':
+ ++rp;
+ if (!O_ISSET(sp, O_MAGIC)) {
+ 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 (match[no].rm_so == -1 ||
+ match[no].rm_eo == -1)
+ break;
+ mlen = match[no].rm_eo - match[no].rm_so;
+ for (t = ip + match[no].rm_so; mlen--; ++t)
+ OUTCH(*t, 0);
+ 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;
+ }
+ }
+ OUTCH(ch, 1);
+ }
+
+ *lbp = lb; /* Update caller's information. */
+ *lbclenp = lbclen;
+ *lblenp = lblen;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_tag.c b/contrib/nvi/ex/ex_tag.c
new file mode 100644
index 000000000000..461b1526ef00
--- /dev/null
+++ b/contrib/nvi/ex/ex_tag.c
@@ -0,0 +1,1324 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_tag.c 10.36 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h> /* XXX: param.h may not have included types.h */
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "tag.h"
+
+static char *binary_search __P((char *, char *, char *));
+static int compare __P((char *, char *, char *));
+static void ctag_file __P((SCR *, TAGF *, char *, char **, size_t *));
+static int ctag_search __P((SCR *, char *, size_t, char *));
+static int ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
+static TAGQ *ctag_slist __P((SCR *, char *));
+static char *linear_search __P((char *, char *, char *));
+static int tag_copy __P((SCR *, TAG *, TAG **));
+static int tag_pop __P((SCR *, TAGQ *, int));
+static int tagf_copy __P((SCR *, TAGF *, TAGF **));
+static int tagf_free __P((SCR *, TAGF *));
+static int tagq_copy __P((SCR *, TAGQ *, TAGQ **));
+
+/*
+ * ex_tag_first --
+ * The tag code can be entered from main, e.g., "vi -t tag".
+ *
+ * PUBLIC: int ex_tag_first __P((SCR *, char *));
+ */
+int
+ex_tag_first(sp, tagarg)
+ SCR *sp;
+ char *tagarg;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ /* Build an argument for the ex :tag command. */
+ ex_cinit(&cmd, C_TAG, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, tagarg, strlen(tagarg));
+
+ /*
+ * XXX
+ * Historic vi went ahead and created a temporary file when it failed
+ * to find the tag. We match historic practice, but don't distinguish
+ * between real error and failure to find the tag.
+ */
+ if (ex_tag_push(sp, &cmd))
+ return (0);
+
+ /* Display tags in the center of the screen. */
+ F_CLR(sp, SC_SCR_TOP);
+ F_SET(sp, SC_SCR_CENTER);
+
+ return (0);
+}
+
+/*
+ * ex_tag_push -- ^]
+ * :tag[!] [string]
+ *
+ * Enter a new TAGQ context based on a ctag string.
+ *
+ * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *));
+ */
+int
+ex_tag_push(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EX_PRIVATE *exp;
+ FREF *frp;
+ TAG *rtp;
+ TAGQ *rtqp, *tqp;
+ recno_t lno;
+ size_t cno;
+ long tl;
+ int force, istmp;
+
+ exp = EXP(sp);
+ switch (cmdp->argc) {
+ case 1:
+ if (exp->tag_last != NULL)
+ free(exp->tag_last);
+
+ if ((exp->tag_last = strdup(cmdp->argv[0]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* Taglength may limit the number of characters. */
+ if ((tl =
+ O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tag_last) > tl)
+ exp->tag_last[tl] = '\0';
+ break;
+ case 0:
+ if (exp->tag_last == NULL) {
+ msgq(sp, M_ERR, "158|No previous tag entered");
+ return (1);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Get the tag information. */
+ if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
+ return (1);
+
+ /*
+ * Allocate all necessary memory before swapping screens. Initialize
+ * flags so we know what to free.
+ */
+ rtp = NULL;
+ rtqp = NULL;
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ /* Initialize the `local context' tag queue structure. */
+ CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
+ CIRCLEQ_INIT(&rtqp->tagq);
+
+ /* Initialize and link in its tag structure. */
+ CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
+ CIRCLEQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
+ rtqp->current = rtp;
+ }
+
+ /*
+ * Stick the current context information in a convenient place, we're
+ * about to lose it. Note, if we're called on editor startup, there
+ * will be no FREF structure.
+ */
+ frp = sp->frp;
+ lno = sp->lno;
+ cno = sp->cno;
+ istmp = frp == NULL ||
+ F_ISSET(frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN);
+
+ /* Try to switch to the tag. */
+ force = FL_ISSET(cmdp->iflags, E_C_FORCE);
+ if (F_ISSET(cmdp, E_NEWSCREEN)) {
+ if (ex_tag_Nswitch(sp, tqp->tagq.cqh_first, force))
+ goto err;
+
+ /* Everything else gets done in the new screen. */
+ sp = sp->nextdisp;
+ exp = EXP(sp);
+ } else
+ if (ex_tag_nswitch(sp, tqp->tagq.cqh_first, force))
+ goto err;
+
+ /*
+ * If this is the first tag, put a `current location' queue entry
+ * in place, so we can pop all the way back to the current mark.
+ * Note, it doesn't point to much of anything, it's a placeholder.
+ */
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ CIRCLEQ_INSERT_HEAD(&exp->tq, rtqp, q);
+ } else
+ rtqp = exp->tq.cqh_first;
+
+ /* Link the new TAGQ structure into place. */
+ CIRCLEQ_INSERT_HEAD(&exp->tq, tqp, q);
+
+ (void)ctag_search(sp,
+ tqp->current->search, tqp->current->slen, tqp->tag);
+
+ /*
+ * Move the current context from the temporary save area into the
+ * right structure.
+ *
+ * If we were in a temporary file, we don't have a context to which
+ * we can return, so just make it be the same as what we're moving
+ * to. It will be a little odd that ^T doesn't change anything, but
+ * I don't think it's a big deal.
+ */
+ if (istmp) {
+ rtqp->current->frp = sp->frp;
+ rtqp->current->lno = sp->lno;
+ rtqp->current->cno = sp->cno;
+ } else {
+ rtqp->current->frp = frp;
+ rtqp->current->lno = lno;
+ rtqp->current->cno = cno;
+ }
+ return (0);
+
+err:
+alloc_err:
+ if (rtqp != NULL)
+ free(rtqp);
+ if (rtp != NULL)
+ free(rtp);
+ tagq_free(sp, tqp);
+ return (1);
+}
+
+/*
+ * ex_tag_next --
+ * Switch context to the next TAG.
+ *
+ * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
+ */
+int
+ex_tag_next(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGQ *tqp;
+
+ exp = EXP(sp);
+ if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
+ tag_msg(sp, TAG_EMPTY, NULL);
+ return (1);
+ }
+ if ((tp = tqp->current->q.cqe_next) == (void *)&tqp->tagq) {
+ msgq(sp, M_ERR, "282|Already at the last tag of this group");
+ return (1);
+ }
+ if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
+ return (1);
+ tqp->current = tp;
+
+ if (F_ISSET(tqp, TAG_CSCOPE))
+ (void)cscope_search(sp, tqp, tp);
+ else
+ (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
+ return (0);
+}
+
+/*
+ * ex_tag_prev --
+ * Switch context to the next TAG.
+ *
+ * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
+ */
+int
+ex_tag_prev(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGQ *tqp;
+
+ exp = EXP(sp);
+ if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
+ tag_msg(sp, TAG_EMPTY, NULL);
+ return (0);
+ }
+ if ((tp = tqp->current->q.cqe_prev) == (void *)&tqp->tagq) {
+ msgq(sp, M_ERR, "255|Already at the first tag of this group");
+ return (1);
+ }
+ if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
+ return (1);
+ tqp->current = tp;
+
+ if (F_ISSET(tqp, TAG_CSCOPE))
+ (void)cscope_search(sp, tqp, tp);
+ else
+ (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
+ return (0);
+}
+
+/*
+ * ex_tag_nswitch --
+ * Switch context to the specified TAG.
+ *
+ * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int));
+ */
+int
+ex_tag_nswitch(sp, tp, force)
+ SCR *sp;
+ TAG *tp;
+ int force;
+{
+ /* Get a file structure. */
+ if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
+ return (1);
+
+ /* If not changing files, return, we're done. */
+ if (tp->frp == sp->frp)
+ return (0);
+
+ /* Check for permission to leave. */
+ if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /* Initialize the new file. */
+ if (file_init(sp, tp->frp, NULL, FS_SETALT))
+ return (1);
+
+ /* Display tags in the center of the screen. */
+ F_CLR(sp, SC_SCR_TOP);
+ F_SET(sp, SC_SCR_CENTER);
+
+ /* Switch. */
+ F_SET(sp, SC_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_tag_Nswitch --
+ * Switch context to the specified TAG in a new screen.
+ *
+ * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int));
+ */
+int
+ex_tag_Nswitch(sp, tp, force)
+ SCR *sp;
+ TAG *tp;
+ int force;
+{
+ SCR *new;
+
+ /* Get a file structure. */
+ if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
+ return (1);
+
+ /* Get a new screen. */
+ if (screen_init(sp->gp, sp, &new))
+ return (1);
+ if (vs_split(sp, new, 0)) {
+ (void)file_end(new, new->ep, 1);
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Get a backing file. */
+ if (tp->frp == sp->frp) {
+ /* Copy file state. */
+ new->ep = sp->ep;
+ ++new->ep->refcnt;
+
+ new->frp = tp->frp;
+ new->frp->flags = sp->frp->flags;
+ } else if (file_init(new, tp->frp, NULL, force)) {
+ (void)vs_discard(new, NULL);
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Create the argument list. */
+ new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name);
+
+ /* Display tags in the center of the screen. */
+ F_CLR(new, SC_SCR_TOP);
+ F_SET(new, SC_SCR_CENTER);
+
+ /* Switch. */
+ sp->nextdisp = new;
+ F_SET(sp, SC_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * ex_tag_pop -- ^T
+ * :tagp[op][!] [number | file]
+ *
+ * Pop to a previous TAGQ context.
+ *
+ * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *));
+ */
+int
+ex_tag_pop(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAGQ *tqp, *dtqp;
+ size_t arglen;
+ long off;
+ char *arg, *p, *t;
+
+ /* Check for an empty stack. */
+ exp = EXP(sp);
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ tag_msg(sp, TAG_EMPTY, NULL);
+ return (1);
+ }
+
+ /* Find the last TAG structure that we're going to DISCARD! */
+ switch (cmdp->argc) {
+ case 0: /* Pop one tag. */
+ dtqp = exp->tq.cqh_first;
+ break;
+ case 1: /* Name or number. */
+ arg = cmdp->argv[0]->bp;
+ off = strtol(arg, &p, 10);
+ if (*p != '\0')
+ goto filearg;
+
+ /* Number: pop that many queue entries. */
+ if (off < 1)
+ return (0);
+ for (tqp = exp->tq.cqh_first;
+ tqp != (void *)&exp->tq && --off > 1;
+ tqp = tqp->q.cqe_next);
+ if (tqp == (void *)&exp->tq) {
+ msgq(sp, M_ERR,
+ "159|Less than %s entries on the tags stack; use :display t[ags]",
+ arg);
+ return (1);
+ }
+ dtqp = tqp;
+ break;
+
+ /* File argument: pop to that queue entry. */
+filearg: arglen = strlen(arg);
+ for (tqp = exp->tq.cqh_first;
+ tqp != (void *)&exp->tq;
+ dtqp = tqp, tqp = tqp->q.cqe_next) {
+ /* Don't pop to the current file. */
+ if (tqp == exp->tq.cqh_first)
+ continue;
+ p = tqp->current->frp->name;
+ if ((t = strrchr(p, '/')) == NULL)
+ t = p;
+ else
+ ++t;
+ if (!strncmp(arg, t, arglen))
+ break;
+ }
+ if (tqp == (void *)&exp->tq) {
+ msgq_str(sp, M_ERR, arg,
+ "160|No file %s on the tags stack to return to; use :display t[ags]");
+ return (1);
+ }
+ if (tqp == exp->tq.cqh_first)
+ return (0);
+ break;
+ default:
+ abort();
+ }
+
+ return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)));
+}
+
+/*
+ * ex_tag_top -- :tagt[op][!]
+ * Clear the tag stack.
+ *
+ * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *));
+ */
+int
+ex_tag_top(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+
+ /* Check for an empty stack. */
+ if (exp->tq.cqh_first == (void *)&exp->tq) {
+ tag_msg(sp, TAG_EMPTY, NULL);
+ return (1);
+ }
+
+ /* Return to the oldest information. */
+ return (tag_pop(sp,
+ exp->tq.cqh_last->q.cqe_prev, FL_ISSET(cmdp->iflags, E_C_FORCE)));
+}
+
+/*
+ * tag_pop --
+ * Pop up to and including the specified TAGQ context.
+ */
+static int
+tag_pop(sp, dtqp, force)
+ SCR *sp;
+ TAGQ *dtqp;
+ int force;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGQ *tqp;
+
+ exp = EXP(sp);
+
+ /*
+ * Update the cursor from the saved TAG information of the TAG
+ * structure we're moving to.
+ */
+ tp = dtqp->q.cqe_next->current;
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ tp->frp->lno = tp->lno;
+ tp->frp->cno = tp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+ if (file_init(sp, tp->frp, NULL, FS_SETALT))
+ return (1);
+
+ F_SET(sp, SC_FSWITCH);
+ }
+
+ /* Pop entries off the queue up to and including dtqp. */
+ do {
+ tqp = exp->tq.cqh_first;
+ if (tagq_free(sp, tqp))
+ return (0);
+ } while (tqp != dtqp);
+
+ /*
+ * If only a single tag left, we've returned to the first tag point,
+ * and the stack is now empty.
+ */
+ if (exp->tq.cqh_first->q.cqe_next == (void *)&exp->tq)
+ tagq_free(sp, exp->tq.cqh_first);
+
+ return (0);
+}
+
+/*
+ * ex_tag_display --
+ * Display the list of tags.
+ *
+ * PUBLIC: int ex_tag_display __P((SCR *));
+ */
+int
+ex_tag_display(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGQ *tqp;
+ int cnt;
+ size_t len;
+ char *p, *sep;
+
+ exp = EXP(sp);
+ if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
+ tag_msg(sp, TAG_EMPTY, NULL);
+ return (0);
+ }
+
+ /*
+ * We give the file name 20 columns and the search string the rest.
+ * If there's not enough room, we don't do anything special, it's
+ * not worth the effort, it just makes the display more confusing.
+ *
+ * We also assume that characters in file names map 1-1 to printing
+ * characters. This might not be true, but I don't think it's worth
+ * fixing. (The obvious fix is to pass the filenames through the
+ * msg_print function.)
+ */
+#define L_NAME 30 /* Name. */
+#define L_SLOP 4 /* Leading number plus trailing *. */
+#define L_SPACE 5 /* Spaces after name, before tag. */
+#define L_TAG 20 /* Tag. */
+ if (sp->cols <= L_NAME + L_SLOP) {
+ msgq(sp, M_ERR, "292|Display too small.");
+ return (0);
+ }
+
+ /*
+ * Display the list of tags for each queue entry. The first entry
+ * is numbered, and the current tag entry has an asterisk appended.
+ */
+ for (cnt = 1, tqp = exp->tq.cqh_first; !INTERRUPTED(sp) &&
+ tqp != (void *)&exp->tq; ++cnt, tqp = tqp->q.cqe_next)
+ for (tp = tqp->tagq.cqh_first;
+ tp != (void *)&tqp->tagq; tp = tp->q.cqe_next) {
+ if (tp == tqp->tagq.cqh_first)
+ (void)ex_printf(sp, "%2d ", cnt);
+ else
+ (void)ex_printf(sp, " ");
+ p = tp->frp == NULL ? tp->fname : tp->frp->name;
+ if ((len = strlen(p)) > L_NAME) {
+ len = len - (L_NAME - 4);
+ (void)ex_printf(sp, " ... %*.*s",
+ L_NAME - 4, L_NAME - 4, p + len);
+ } else
+ (void)ex_printf(sp,
+ " %*.*s", L_NAME, L_NAME, p);
+ if (tqp->current == tp)
+ (void)ex_printf(sp, "*");
+
+ if (tp == tqp->tagq.cqh_first && tqp->tag != NULL &&
+ (sp->cols - L_NAME) >= L_TAG + L_SPACE) {
+ len = strlen(tqp->tag);
+ if (len > sp->cols - (L_NAME + L_SPACE))
+ len = sp->cols - (L_NAME + L_SPACE);
+ (void)ex_printf(sp, "%s%.*s",
+ tqp->current == tp ? " " : " ",
+ (int)len, tqp->tag);
+ }
+ (void)ex_printf(sp, "\n");
+ }
+ return (0);
+}
+
+/*
+ * ex_tag_copy --
+ * Copy a screen's tag structures.
+ *
+ * PUBLIC: int ex_tag_copy __P((SCR *, SCR *));
+ */
+int
+ex_tag_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ EX_PRIVATE *oexp, *nexp;
+ TAGQ *aqp, *tqp;
+ TAG *ap, *tp;
+ TAGF *atfp, *tfp;
+
+ oexp = EXP(orig);
+ nexp = EXP(sp);
+
+ /* Copy tag queue and tags stack. */
+ for (aqp = oexp->tq.cqh_first;
+ aqp != (void *)&oexp->tq; aqp = aqp->q.cqe_next) {
+ if (tagq_copy(sp, aqp, &tqp))
+ return (1);
+ for (ap = aqp->tagq.cqh_first;
+ ap != (void *)&aqp->tagq; ap = ap->q.cqe_next) {
+ if (tag_copy(sp, ap, &tp))
+ return (1);
+ /* Set the current pointer. */
+ if (aqp->current == ap)
+ tqp->current = tp;
+ CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
+ }
+ CIRCLEQ_INSERT_TAIL(&nexp->tq, tqp, q);
+ }
+
+ /* Copy list of tag files. */
+ for (atfp = oexp->tagfq.tqh_first;
+ atfp != NULL; atfp = atfp->q.tqe_next) {
+ if (tagf_copy(sp, atfp, &tfp))
+ return (1);
+ TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
+ }
+
+ /* Copy the last tag. */
+ if (oexp->tag_last != NULL &&
+ (nexp->tag_last = strdup(oexp->tag_last)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * tagf_copy --
+ * Copy a TAGF structure and return it in new memory.
+ */
+static int
+tagf_copy(sp, otfp, tfpp)
+ SCR *sp;
+ TAGF *otfp, **tfpp;
+{
+ TAGF *tfp;
+
+ MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
+ *tfp = *otfp;
+
+ /* XXX: Allocate as part of the TAGF structure!!! */
+ if ((tfp->name = strdup(otfp->name)) == NULL)
+ return (1);
+
+ *tfpp = tfp;
+ return (0);
+}
+
+/*
+ * tagq_copy --
+ * Copy a TAGQ structure and return it in new memory.
+ */
+static int
+tagq_copy(sp, otqp, tqpp)
+ SCR *sp;
+ TAGQ *otqp, **tqpp;
+{
+ TAGQ *tqp;
+ size_t len;
+
+ len = sizeof(TAGQ);
+ if (otqp->tag != NULL)
+ len += otqp->tlen + 1;
+ MALLOC_RET(sp, tqp, TAGQ *, len);
+ memcpy(tqp, otqp, len);
+
+ CIRCLEQ_INIT(&tqp->tagq);
+ tqp->current = NULL;
+ if (otqp->tag != NULL)
+ tqp->tag = tqp->buf;
+
+ *tqpp = tqp;
+ return (0);
+}
+
+/*
+ * tag_copy --
+ * Copy a TAG structure and return it in new memory.
+ */
+static int
+tag_copy(sp, otp, tpp)
+ SCR *sp;
+ TAG *otp, **tpp;
+{
+ TAG *tp;
+ size_t len;
+
+ len = sizeof(TAG);
+ if (otp->fname != NULL)
+ len += otp->fnlen + 1;
+ if (otp->search != NULL)
+ len += otp->slen + 1;
+ MALLOC_RET(sp, tp, TAG *, len);
+ memcpy(tp, otp, len);
+
+ if (otp->fname != NULL)
+ tp->fname = tp->buf;
+ if (otp->search != NULL)
+ tp->search = tp->fname + otp->fnlen + 1;
+
+ *tpp = tp;
+ return (0);
+}
+
+/*
+ * tagf_free --
+ * Free a TAGF structure.
+ */
+static int
+tagf_free(sp, tfp)
+ SCR *sp;
+ TAGF *tfp;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ TAILQ_REMOVE(&exp->tagfq, tfp, q);
+ free(tfp->name);
+ free(tfp);
+ return (0);
+}
+
+/*
+ * tagq_free --
+ * Free a TAGQ structure (and associated TAG structures).
+ *
+ * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
+ */
+int
+tagq_free(sp, tqp)
+ SCR *sp;
+ TAGQ *tqp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+
+ exp = EXP(sp);
+ while ((tp = tqp->tagq.cqh_first) != (void *)&tqp->tagq) {
+ CIRCLEQ_REMOVE(&tqp->tagq, tp, q);
+ free(tp);
+ }
+ /*
+ * !!!
+ * If allocated and then the user failed to switch files, the TAGQ
+ * structure was never attached to any list.
+ */
+ if (tqp->q.cqe_next != NULL)
+ CIRCLEQ_REMOVE(&exp->tq, tqp, q);
+ free(tqp);
+ return (0);
+}
+
+/*
+ * tag_msg
+ * A few common messages.
+ *
+ * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
+ */
+void
+tag_msg(sp, msg, tag)
+ SCR *sp;
+ tagmsg_t msg;
+ char *tag;
+{
+ switch (msg) {
+ case TAG_BADLNO:
+ msgq_str(sp, M_ERR, tag,
+ "164|%s: the tag's line number is past the end of the file");
+ break;
+ case TAG_EMPTY:
+ msgq(sp, M_INFO, "165|The tags stack is empty");
+ break;
+ case TAG_SEARCH:
+ msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
+ break;
+ default:
+ abort();
+ }
+}
+
+/*
+ * ex_tagf_alloc --
+ * Create a new list of ctag files.
+ *
+ * PUBLIC: int ex_tagf_alloc __P((SCR *, char *));
+ */
+int
+ex_tagf_alloc(sp, str)
+ SCR *sp;
+ char *str;
+{
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ size_t len;
+ char *p, *t;
+
+ /* Free current queue. */
+ exp = EXP(sp);
+ while ((tfp = exp->tagfq.tqh_first) != NULL)
+ tagf_free(sp, tfp);
+
+ /* Create new queue. */
+ for (p = t = str;; ++p) {
+ if (*p == '\0' || isblank(*p)) {
+ if ((len = p - t) > 1) {
+ MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
+ MALLOC(sp, tfp->name, char *, len + 1);
+ if (tfp->name == NULL) {
+ free(tfp);
+ return (1);
+ }
+ memcpy(tfp->name, t, len);
+ tfp->name[len] = '\0';
+ tfp->flags = 0;
+ TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q);
+ }
+ t = p + 1;
+ }
+ if (*p == '\0')
+ break;
+ }
+ return (0);
+}
+ /* Free previous queue. */
+/*
+ * ex_tag_free --
+ * Free the ex tag information.
+ *
+ * PUBLIC: int ex_tag_free __P((SCR *));
+ */
+int
+ex_tag_free(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ TAGQ *tqp;
+
+ /* Free up tag information. */
+ exp = EXP(sp);
+ while ((tqp = exp->tq.cqh_first) != (void *)&exp->tq)
+ tagq_free(sp, tqp);
+ while ((tfp = exp->tagfq.tqh_first) != NULL)
+ tagf_free(sp, tfp);
+ if (exp->tag_last != NULL)
+ free(exp->tag_last);
+ return (0);
+}
+
+/*
+ * ctag_search --
+ * Search a file for a tag.
+ */
+static int
+ctag_search(sp, search, slen, tag)
+ SCR *sp;
+ char *search, *tag;
+ size_t slen;
+{
+ MARK m;
+ char *p;
+
+ /*
+ * !!!
+ * The historic tags file format (from a long, long time ago...)
+ * used a line number, not a search string. I got complaints, so
+ * people are still using the format. POSIX 1003.2 permits it.
+ */
+ if (isdigit(search[0])) {
+ m.lno = atoi(search);
+ if (!db_exist(sp, m.lno)) {
+ tag_msg(sp, TAG_BADLNO, tag);
+ return (1);
+ }
+ } 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;
+ if (f_search(sp, &m, &m,
+ search, slen, NULL, SEARCH_FILE | SEARCH_TAG))
+ if ((p = strrchr(search, '(')) != NULL) {
+ slen = p - search;
+ if (f_search(sp, &m, &m, search, slen,
+ NULL, SEARCH_FILE | SEARCH_TAG))
+ goto notfound;
+ } else {
+notfound: tag_msg(sp, TAG_SEARCH, tag);
+ return (1);
+ }
+ /*
+ * !!!
+ * Historically, tags set the search direction if it wasn't
+ * already set.
+ */
+ if (sp->searchdir == NOTSET)
+ sp->searchdir = FORWARD;
+ }
+
+ /*
+ * !!!
+ * Tags move to the first non-blank, NOT the search pattern start.
+ */
+ sp->lno = m.lno;
+ sp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ return (0);
+}
+
+/*
+ * ctag_slist --
+ * Search the list of tags files for a tag, and return tag queue.
+ */
+static TAGQ *
+ctag_slist(sp, tag)
+ SCR *sp;
+ char *tag;
+{
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ TAGQ *tqp;
+ size_t len;
+ int echk;
+
+ exp = EXP(sp);
+
+ /* Allocate and initialize the tag queue structure. */
+ len = strlen(tag);
+ CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
+ CIRCLEQ_INIT(&tqp->tagq);
+ tqp->tag = tqp->buf;
+ memcpy(tqp->tag, tag, (tqp->tlen = len) + 1);
+
+ /*
+ * Find the tag, only display missing file messages once, and
+ * then only if we didn't find the tag.
+ */
+ for (echk = 0,
+ tfp = exp->tagfq.tqh_first; tfp != NULL; tfp = tfp->q.tqe_next)
+ if (ctag_sfile(sp, tfp, tqp, tag)) {
+ echk = 1;
+ F_SET(tfp, TAGF_ERR);
+ } else
+ F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN);
+
+ /* Check to see if we found anything. */
+ if (tqp->tagq.cqh_first == (void *)&tqp->tagq) {
+ msgq_str(sp, M_ERR, tag, "162|%s: tag not found");
+ if (echk)
+ for (tfp = exp->tagfq.tqh_first;
+ tfp != NULL; tfp = tfp->q.tqe_next)
+ if (F_ISSET(tfp, TAGF_ERR) &&
+ !F_ISSET(tfp, TAGF_ERR_WARN)) {
+ errno = tfp->errnum;
+ msgq_str(sp, M_SYSERR, tfp->name, "%s");
+ F_SET(tfp, TAGF_ERR_WARN);
+ }
+ free(tqp);
+ return (NULL);
+ }
+
+ tqp->current = tqp->tagq.cqh_first;
+ return (tqp);
+
+alloc_err:
+ return (NULL);
+}
+
+/*
+ * ctag_sfile --
+ * Search a tags file for a tag, adding any found to the tag queue.
+ */
+static int
+ctag_sfile(sp, tfp, tqp, tname)
+ SCR *sp;
+ TAGF *tfp;
+ TAGQ *tqp;
+ char *tname;
+{
+ struct stat sb;
+ TAG *tp;
+ size_t dlen, nlen, slen;
+ int fd, i, nf1, nf2;
+ char *back, *cname, *dname, *front, *map, *name, *p, *search, *t;
+
+ if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
+ tfp->errnum = errno;
+ return (1);
+ }
+
+ /*
+ * XXX
+ * Some old BSD systems require MAP_FILE as an argument when mapping
+ * regular files.
+ */
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+ /*
+ * 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 mmap fails if the file is too large.
+ */
+ if (fstat(fd, &sb) != 0 ||
+ (map = mmap(NULL, (size_t)sb.st_size, PROT_READ | PROT_WRITE,
+ MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
+ tfp->errnum = errno;
+ (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)
+ goto done;
+
+ /*
+ * Initialize and link in the tag structure(s). The historic ctags
+ * file format only permitted a single tag location per tag. The
+ * obvious extension to permit multiple tags locations per tag is to
+ * output multiple records in the standard format. Unfortunately,
+ * this won't work correctly with historic ex/vi implementations,
+ * because their binary search assumes that there's only one record
+ * per tag, and so will use a random tag entry if there si more than
+ * one. This code handles either format.
+ *
+ * The tags file is in the following format:
+ *
+ * <tag> <filename> <line number> | <pattern>
+ *
+ * Figure out how long everything is so we can allocate in one swell
+ * foop, but discard anything that looks wrong.
+ */
+ for (;;) {
+ /* Nul-terminate the end of the line. */
+ for (p = front; p < back && *p != '\n'; ++p);
+ if (p == back || *p != '\n')
+ break;
+ *p = '\0';
+
+ /* Update the pointers for the next time. */
+ t = p + 1;
+ p = front;
+ front = t;
+
+ /* Break the line into tokens. */
+ for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
+ switch (i) {
+ case 0: /* Tag. */
+ cname = t;
+ break;
+ case 1: /* Filename. */
+ name = t;
+ nlen = strlen(name);
+ break;
+ }
+
+ /* Check for corruption. */
+ if (i != 2 || p == NULL || t == NULL)
+ goto corrupt;
+
+ /* The rest of the string is the search pattern. */
+ search = p;
+ if ((slen = strlen(p)) == 0) {
+corrupt: p = msg_print(sp, tname, &nf1);
+ t = msg_print(sp, tfp->name, &nf2);
+ msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t);
+ if (nf1)
+ FREE_SPACE(sp, p, 0);
+ if (nf2)
+ FREE_SPACE(sp, t, 0);
+ continue;
+ }
+
+ /* Check for passing the last entry. */
+ if (strcmp(tname, cname))
+ break;
+
+ /* Resolve the file name. */
+ ctag_file(sp, tfp, name, &dname, &dlen);
+
+ CALLOC_GOTO(sp, tp,
+ TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1);
+ tp->fname = tp->buf;
+ if (dlen != 0) {
+ memcpy(tp->fname, dname, dlen);
+ tp->fname[dlen] = '/';
+ ++dlen;
+ }
+ memcpy(tp->fname + dlen, name, nlen + 1);
+ tp->fnlen = dlen + nlen;
+ tp->search = tp->fname + tp->fnlen + 1;
+ memcpy(tp->search, search, (tp->slen = slen) + 1);
+ CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
+ }
+
+alloc_err:
+done: if (munmap(map, (size_t)sb.st_size))
+ msgq(sp, M_SYSERR, "munmap");
+ if (close(fd))
+ msgq(sp, M_SYSERR, "close");
+ return (0);
+}
+
+/*
+ * ctag_file --
+ * Search for the right path to this file.
+ */
+static void
+ctag_file(sp, tfp, name, dirp, dlenp)
+ SCR *sp;
+ TAGF *tfp;
+ char *name, **dirp;
+ size_t *dlenp;
+{
+ struct stat sb;
+ size_t len;
+ char *p, buf[MAXPATHLEN];
+
+ /*
+ * !!!
+ * If the tag file path is a relative path, see if it exists. If it
+ * doesn't, look relative to the tags file path. It's okay for a tag
+ * file to not exist, and historically, vi simply displayed a "new"
+ * file. However, if the path exists relative to the tag file, it's
+ * pretty clear what's happening, so we may as well get it right.
+ */
+ *dlenp = 0;
+ if (name[0] != '/' &&
+ stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
+ *p = '\0';
+ len = snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name);
+ *p = '/';
+ if (stat(buf, &sb) == 0) {
+ *dirp = tfp->name;
+ *dlenp = strlen(*dirp);
+ }
+ }
+}
+
+/*
+ * 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 EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+
+#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);
+ case LESS: /* No such string. */
+ return (NULL);
+ 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/contrib/nvi/ex/ex_tcl.c b/contrib/nvi/ex/ex_tcl.c
new file mode 100644
index 000000000000..06736a781006
--- /dev/null
+++ b/contrib/nvi/ex/ex_tcl.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1995
+ * George V. Neville-Neil. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_tcl.c 8.10 (Berkeley) 9/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+#ifdef HAVE_TCL_INTERP
+#include <tcl.h>
+#endif
+
+/*
+ * ex_tcl -- :[line [,line]] tcl [command]
+ * Run a command through the tcl interpreter.
+ *
+ * PUBLIC: int ex_tcl __P((SCR*, EXCMD *));
+ */
+int
+ex_tcl(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+#ifdef HAVE_TCL_INTERP
+ CHAR_T *p;
+ GS *gp;
+ size_t len;
+ char buf[128];
+
+ /* Initialize the interpreter. */
+ gp = sp->gp;
+ if (gp->tcl_interp == NULL && tcl_init(gp))
+ return (1);
+
+ /* Skip leading white space. */
+ if (cmdp->argc != 0)
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len; len > 0; --len, ++p)
+ if (!isblank(*p))
+ break;
+ if (cmdp->argc == 0 || len == 0) {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+
+ (void)snprintf(buf, sizeof(buf),
+ "set viScreenId %d\nset viStartLine %lu\nset viStopLine %lu",
+ sp->id, cmdp->addr1.lno, cmdp->addr2.lno);
+ if (Tcl_Eval(gp->tcl_interp, buf) == TCL_OK &&
+ Tcl_Eval(gp->tcl_interp, cmdp->argv[0]->bp) == TCL_OK)
+ return (0);
+
+ msgq(sp, M_ERR, "Tcl: %s", ((Tcl_Interp *)gp->tcl_interp)->result);
+ return (1);
+#else
+ msgq(sp, M_ERR, "302|Vi was not loaded with a Tcl interpreter");
+ return (1);
+#endif /* HAVE_TCL_INTERP */
+}
diff --git a/contrib/nvi/ex/ex_txt.c b/contrib/nvi/ex/ex_txt.c
new file mode 100644
index 000000000000..2f62ff51a47c
--- /dev/null
+++ b/contrib/nvi/ex/ex_txt.c
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_txt.c 10.17 (Berkeley) 10/10/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * !!!
+ * The backslash characters was special when it preceded a newline as part of
+ * a substitution replacement pattern. For example, the input ":a\<cr>" would
+ * failed immediately with an error, as the <cr> wasn't part of a substitution
+ * replacement pattern. This implies a frightening integration of the editor
+ * and the parser and/or the RE engine. There's no way I'm going to reproduce
+ * those semantics.
+ *
+ * So, if backslashes are special, this code inserts the backslash and the next
+ * character into the string, without regard for the character or the command
+ * being entered. Since "\<cr>" was illegal historically (except for the one
+ * special case), and the command will fail eventually, no historical scripts
+ * should break (presuming they didn't depend on the failure mode itself or the
+ * characters remaining when failure occurred.
+ */
+
+static int txt_dent __P((SCR *, TEXT *));
+static void txt_prompt __P((SCR *, TEXT *, ARG_CHAR_T, u_int32_t));
+
+/*
+ * ex_txt --
+ * Get lines from the terminal for ex.
+ *
+ * PUBLIC: int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t));
+ */
+int
+ex_txt(sp, tiqh, prompt, flags)
+ SCR *sp;
+ TEXTH *tiqh;
+ ARG_CHAR_T prompt;
+ u_int32_t flags;
+{
+ EVENT ev;
+ GS *gp;
+ TEXT ait, *ntp, *tp;
+ carat_t carat_st;
+ size_t cnt;
+ int rval;
+
+ rval = 0;
+
+ /*
+ * Get a 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)
+ goto err;
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ /* Set the starting line number. */
+ tp->lno = sp->lno + 1;
+
+ /*
+ * If it's a terminal, set up autoindent, put out the prompt, and
+ * set it up so we know we were suspended. Otherwise, turn off
+ * the autoindent flag, as that requires less special casing below.
+ *
+ * XXX
+ * Historic practice is that ^Z suspended command mode (but, because
+ * it ran in cooked mode, it was unaffected by the autowrite option.)
+ * On restart, any "current" input was discarded, whether in insert
+ * mode or not, and ex was in command mode. This code matches historic
+ * practice, but not 'cause it's easier.
+ */
+ gp = sp->gp;
+ if (F_ISSET(gp, G_SCRIPTED))
+ LF_CLR(TXT_AUTOINDENT);
+ else {
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ LF_SET(TXT_EOFCHAR);
+ if (v_txt_auto(sp, sp->lno, NULL, 0, tp))
+ goto err;
+ }
+ txt_prompt(sp, tp, prompt, flags);
+ }
+
+ for (carat_st = C_NOTSET;;) {
+ if (v_event_get(sp, &ev, 0, 0))
+ goto err;
+
+ /* Deal with all non-character events. */
+ switch (ev.e_event) {
+ case E_CHARACTER:
+ break;
+ case E_ERR:
+ goto err;
+ case E_REPAINT:
+ case E_WRESIZE:
+ continue;
+ case E_EOF:
+ rval = 1;
+ /* FALLTHROUGH */
+ case E_INTERRUPT:
+ /*
+ * Handle EOF/SIGINT events by discarding partially
+ * entered text and returning. EOF returns failure,
+ * E_INTERRUPT returns success.
+ */
+ goto notlast;
+ default:
+ v_event_err(sp, &ev);
+ goto notlast;
+ }
+
+ /*
+ * Deal with character events.
+ *
+ * Check to see if the character fits into the input buffer.
+ * (Use tp->len, ignore overwrite and non-printable chars.)
+ */
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+
+ switch (ev.e_value) {
+ case K_CR:
+ /*
+ * !!!
+ * Historically, <carriage-return>'s in the command
+ * weren't special, so the ex parser would return an
+ * unknown command error message. However, if they
+ * terminated the command if they were in a map. I'm
+ * pretty sure this still isn't right, but it handles
+ * what I've seen so far.
+ */
+ if (!F_ISSET(&ev.e_ch, CH_MAPPED))
+ goto ins_ch;
+ /* FALLTHROUGH */
+ case K_NL:
+ /*
+ * '\' can escape <carriage-return>/<newline>. We
+ * don't discard the backslash because we need it
+ * to get the <newline> through the ex parser.
+ */
+ if (LF_ISSET(TXT_BACKSLASH) &&
+ tp->len != 0 && tp->lb[tp->len - 1] == '\\')
+ goto ins_ch;
+
+ /*
+ * CR returns from the ex command line.
+ *
+ * XXX
+ * Terminate with a nul, needed by filter.
+ */
+ if (LF_ISSET(TXT_CR)) {
+ tp->lb[tp->len] = '\0';
+ goto done;
+ }
+
+ /*
+ * '.' may terminate text input mode; free the current
+ * TEXT.
+ */
+ if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 &&
+ tp->lb[tp->len - 1] == '.') {
+notlast: CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+ goto done;
+ }
+
+ /* Set up bookkeeping for the new line. */
+ if ((ntp = text_init(sp, NULL, 0, 32)) == NULL)
+ goto err;
+ ntp->lno = tp->lno + 1;
+
+ /*
+ * Reset the autoindent line value. 0^D keeps the ai
+ * line from changing, ^D changes the level, even if
+ * there were no characters in the old line. Note, if
+ * using the current tp structure, use the cursor as
+ * the length, the autoindent characters may have been
+ * erased.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ if (carat_st == C_NOCHANGE) {
+ if (v_txt_auto(sp,
+ OOBLNO, &ait, ait.ai, ntp))
+ goto err;
+ free(ait.lb);
+ } else
+ if (v_txt_auto(sp,
+ OOBLNO, tp, tp->len, ntp))
+ goto err;
+ carat_st = C_NOTSET;
+ }
+ txt_prompt(sp, ntp, prompt, flags);
+
+ /*
+ * Swap old and new TEXT's, and insert the new TEXT
+ * into the queue.
+ */
+ tp = ntp;
+ CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
+ break;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
+ carat_st = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
+ carat_st = C_ZEROSET;
+ goto ins_ch;
+ case K_CNTRLD: /* Delete autoindent char. */
+ /*
+ * !!!
+ * Historically, the ^D command took (but then ignored)
+ * a count. For simplicity, we don't return it unless
+ * it's the first character entered. The check for len
+ * equal to 0 is okay, TXT_AUTOINDENT won't be set.
+ */
+ if (LF_ISSET(TXT_CNTRLD)) {
+ for (cnt = 0; cnt < tp->len; ++cnt)
+ if (!isblank(tp->lb[cnt]))
+ break;
+ if (cnt == tp->len) {
+ tp->len = 1;
+ tp->lb[0] = ev.e_c;
+ tp->lb[1] = '\0';
+
+ /*
+ * Put out a line separator, in case
+ * the command fails.
+ */
+ (void)putchar('\n');
+ goto done;
+ }
+ }
+
+ /*
+ * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that
+ * the EOF characters are discarded if there are other
+ * characters to process in the line, i.e. if the EOF
+ * is not the first character in the line. For this
+ * reason, historic ex discarded the EOF characters,
+ * even if occurring in the middle of the input line.
+ * We match that historic practice.
+ *
+ * !!!
+ * The test for discarding in the middle of the line is
+ * done in the switch, because the CARAT forms are N+1,
+ * not N.
+ *
+ * !!!
+ * There's considerable magic to make the terminal code
+ * return the EOF character at all. See that code for
+ * details.
+ */
+ if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0)
+ continue;
+ switch (carat_st) {
+ case C_CARATSET: /* ^^D */
+ if (tp->len > tp->ai + 1)
+ continue;
+
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);
+ memcpy(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat_st = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (tp->len > tp->ai + 1)
+ continue;
+
+ carat_st = C_NOTSET;
+leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
+ tp->ai = tp->len = 0;
+ break;
+ case C_NOTSET: /* ^D */
+ if (tp->len > tp->ai)
+ continue;
+
+ if (txt_dent(sp, tp))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+
+ /* Clear and redisplay the line. */
+ (void)gp->scr_ex_adjust(sp, EX_TERM_CE);
+ txt_prompt(sp, tp, prompt, flags);
+ break;
+ default:
+ /*
+ * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c.
+ *
+ * Silently eliminate any iscntrl() character that was
+ * not already handled specially, except for <tab> and
+ * <ff>.
+ */
+ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ev.e_c) &&
+ ev.e_value != K_FORMFEED && ev.e_value != K_TAB)
+ break;
+
+ tp->lb[tp->len++] = ev.e_c;
+ break;
+ }
+ }
+ /* NOTREACHED */
+
+done: return (rval);
+
+err:
+alloc_err:
+ return (1);
+}
+
+/*
+ * txt_prompt --
+ * Display the ex prompt, line number, ai characters. Characters had
+ * better be printable by the terminal driver, but that's its problem,
+ * not ours.
+ */
+static void
+txt_prompt(sp, tp, prompt, flags)
+ SCR *sp;
+ TEXT *tp;
+ ARG_CHAR_T prompt;
+ u_int32_t flags;
+{
+ /* Display the prompt. */
+ if (LF_ISSET(TXT_PROMPT))
+ (void)printf("%c", prompt);
+
+ /* Display the line number. */
+ if (LF_ISSET(TXT_NUMBER) && O_ISSET(sp, O_NUMBER))
+ (void)printf("%6lu ", (u_long)tp->lno);
+
+ /* Print out autoindent string. */
+ if (LF_ISSET(TXT_AUTOINDENT))
+ (void)printf("%.*s", (int)tp->ai, tp->lb);
+ (void)fflush(stdout);
+}
+
+/*
+ * txt_dent --
+ * Handle ^D outdents.
+ *
+ * Ex version of vi/v_ntext.c:txt_dent(). See that code for the (usual)
+ * ranting and raving. This is a fair bit simpler as ^T isn't special.
+ */
+static int
+txt_dent(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 < tp->len; ++off)
+ if (tp->lb[off] == '\t')
+ scno += COL_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Get the previous shiftwidth column. */
+ cno = scno;
+ scno -= --scno % sw;
+
+ /*
+ * Since we don't know what comes before the character(s) being
+ * deleted, we have to resolve the autoindent characters . The
+ * example is a <tab>, which doesn't take up a full shiftwidth
+ * number of columns because it's preceded by <space>s. This is
+ * easy to get if the user sets shiftwidth to a value less than
+ * tabstop, and then uses ^T to indent, and ^D to outdent.
+ *
+ * Count up spaces/tabs needed to get to the target.
+ */
+ for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs)
+ cno += COL_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tabs + spaces + 1);
+
+ /* Adjust the final ai character count. */
+ tp->ai = tabs + spaces;
+
+ /* Enter the replacement characters. */
+ for (tp->len = 0; tabs > 0; --tabs)
+ tp->lb[tp->len++] = '\t';
+ for (; spaces > 0; --spaces)
+ tp->lb[tp->len++] = ' ';
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_undo.c b/contrib/nvi/ex/ex_undo.c
new file mode 100644
index 000000000000..0b0b5b26be18
--- /dev/null
+++ b/contrib/nvi/ex/ex_undo.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_undo.c 10.6 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_undo -- u
+ * Undo the last change.
+ *
+ * PUBLIC: int ex_undo __P((SCR *, EXCMD *));
+ */
+int
+ex_undo(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ EXF *ep;
+ MARK m;
+
+ /*
+ * !!!
+ * Historic undo always set the previous context mark.
+ */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (mark_set(sp, ABSMARK1, &m, 1))
+ return (1);
+
+ /*
+ * !!!
+ * 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.
+ */
+ ep = sp->ep;
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = FORWARD;
+ }
+ switch (ep->lundo) {
+ case BACKWARD:
+ if (log_forward(sp, &m))
+ return (1);
+ ep->lundo = FORWARD;
+ break;
+ case FORWARD:
+ if (log_backward(sp, &m))
+ return (1);
+ ep->lundo = BACKWARD;
+ break;
+ case NOTSET:
+ abort();
+ }
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_usage.c b/contrib/nvi/ex/ex_usage.c
new file mode 100644
index 000000000000..cddf7a6643c7
--- /dev/null
+++ b/contrib/nvi/ex/ex_usage.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_usage.c 10.13 (Berkeley) 5/3/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+/*
+ * ex_help -- :help
+ * Display help message.
+ *
+ * PUBLIC: int ex_help __P((SCR *, EXCMD *));
+ */
+int
+ex_help(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ (void)ex_puts(sp,
+ "To see the list of vi commands, enter \":viusage<CR>\"\n");
+ (void)ex_puts(sp,
+ "To see the list of ex commands, enter \":exusage<CR>\"\n");
+ (void)ex_puts(sp,
+ "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n");
+ (void)ex_puts(sp,
+ "For a vi key usage statement enter \":viusage [key]<CR>\"\n");
+ (void)ex_puts(sp, "To exit, enter \":q!\"\n");
+ return (0);
+}
+
+/*
+ * ex_usage -- :exusage [cmd]
+ * Display ex usage strings.
+ *
+ * PUBLIC: int ex_usage __P((SCR *, EXCMD *));
+ */
+int
+ex_usage(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ ARGS *ap;
+ EXCMDLIST const *cp;
+ int newscreen;
+ char *name, *p, nb[MAXCMDNAMELEN + 5];
+
+ switch (cmdp->argc) {
+ case 1:
+ ap = cmdp->argv[0];
+ if (isupper(ap->bp[0])) {
+ newscreen = 1;
+ ap->bp[0] = tolower(ap->bp[0]);
+ } else
+ newscreen = 0;
+ for (cp = cmds; cp->name != NULL &&
+ memcmp(ap->bp, cp->name, ap->len); ++cp);
+ if (cp->name == NULL ||
+ newscreen && !F_ISSET(cp, E_NEWSCREEN)) {
+ if (newscreen)
+ ap->bp[0] = toupper(ap->bp[0]);
+ (void)ex_printf(sp, "The %.*s command is unknown\n",
+ (int)ap->len, ap->bp);
+ } else {
+ (void)ex_printf(sp,
+ "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(sp,
+ "Command: %s\n Usage: %s\n", cp->help, cp->usage);
+ }
+ break;
+ case 0:
+ for (cp = cmds; cp->name != NULL && !INTERRUPTED(sp); ++cp) {
+ /*
+ * The ^D command has an unprintable name.
+ *
+ * XXX
+ * We display both capital and lower-case versions of
+ * the appropriate commands -- no need to add in extra
+ * room, they're all short names.
+ */
+ if (cp == &cmds[C_SCROLL])
+ name = "^D";
+ else if (F_ISSET(cp, E_NEWSCREEN)) {
+ nb[0] = '[';
+ nb[1] = toupper(cp->name[0]);
+ nb[2] = cp->name[0];
+ nb[3] = ']';
+ for (name = cp->name + 1,
+ p = nb + 4; (*p++ = *name++) != '\0';);
+ name = nb;
+ } else
+ name = cp->name;
+ (void)ex_printf(sp,
+ "%*s: %s\n", MAXCMDNAMELEN, name, cp->help);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * ex_viusage -- :viusage [key]
+ * Display vi usage strings.
+ *
+ * PUBLIC: int ex_viusage __P((SCR *, EXCMD *));
+ */
+int
+ex_viusage(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ GS *gp;
+ VIKEYS const *kp;
+ int key;
+
+ gp = sp->gp;
+ switch (cmdp->argc) {
+ case 1:
+ if (cmdp->argv[0]->len != 1) {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (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;
+
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ kp = &vikeys[key];
+
+ if (kp->usage == NULL)
+nokey: (void)ex_printf(sp,
+ "The %s key has no current meaning\n",
+ KEY_NAME(sp, key));
+ else
+ (void)ex_printf(sp,
+ " Key:%s%s\nUsage: %s\n",
+ isblank(*kp->help) ? "" : " ", kp->help, kp->usage);
+ break;
+ case 0:
+ for (key = 0; key <= MAXVIKEY && !INTERRUPTED(sp); ++key) {
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ kp = &vikeys[key];
+ if (kp->help != NULL)
+ (void)ex_printf(sp, "%s\n", kp->help);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_util.c b/contrib/nvi/ex/ex_util.c
new file mode 100644
index 000000000000..6c4772e61540
--- /dev/null
+++ b/contrib/nvi/ex/ex_util.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_util.c 10.23 (Berkeley) 6/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_cinit --
+ * Create an EX command structure.
+ *
+ * PUBLIC: void ex_cinit __P((EXCMD *,
+ * PUBLIC: int, int, recno_t, recno_t, int, ARGS **));
+ */
+void
+ex_cinit(cmdp, cmd_id, naddr, lno1, lno2, force, ap)
+ EXCMD *cmdp;
+ int cmd_id, force, naddr;
+ recno_t lno1, lno2;
+ ARGS **ap;
+{
+ memset(cmdp, 0, sizeof(EXCMD));
+ cmdp->cmd = &cmds[cmd_id];
+ cmdp->addrcnt = naddr;
+ cmdp->addr1.lno = lno1;
+ cmdp->addr2.lno = lno2;
+ cmdp->addr1.cno = cmdp->addr2.cno = 1;
+ if (force)
+ cmdp->iflags |= E_C_FORCE;
+ cmdp->argc = 0;
+ if ((cmdp->argv = ap) != NULL)
+ cmdp->argv[0] = NULL;
+}
+
+/*
+ * ex_cadd --
+ * Add an argument to an EX command structure.
+ *
+ * PUBLIC: void ex_cadd __P((EXCMD *, ARGS *, char *, size_t));
+ */
+void
+ex_cadd(cmdp, ap, arg, len)
+ EXCMD *cmdp;
+ ARGS *ap;
+ char *arg;
+ size_t len;
+{
+ cmdp->argv[cmdp->argc] = ap;
+ ap->bp = arg;
+ ap->len = len;
+ cmdp->argv[++cmdp->argc] = NULL;
+}
+
+/*
+ * ex_getline --
+ * Return a line from the file.
+ *
+ * PUBLIC: int ex_getline __P((SCR *, FILE *, size_t *));
+ */
+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 (errno = 0, off = 0, p = exp->ibp;;) {
+ if (off >= exp->ibp_len) {
+ BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1);
+ p = exp->ibp + off;
+ }
+ if ((ch = getc(fp)) == EOF && !feof(fp)) {
+ if (errno == EINTR) {
+ errno = 0;
+ clearerr(fp);
+ continue;
+ }
+ return (1);
+ }
+ if (ch == EOF || ch == '\n') {
+ if (ch == EOF && !off)
+ return (1);
+ *lenp = off;
+ return (0);
+ }
+ *p++ = ch;
+ ++off;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * ex_ncheck --
+ * Check for more files to edit.
+ *
+ * PUBLIC: int ex_ncheck __P((SCR *, int));
+ */
+int
+ex_ncheck(sp, force)
+ SCR *sp;
+ int force;
+{
+ char **ap;
+
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ */
+ if (!force && sp->ccnt != sp->q_ccnt + 1 &&
+ sp->cargv != NULL && sp->cargv[1] != NULL) {
+ sp->q_ccnt = sp->ccnt;
+
+ for (ap = sp->cargv + 1; *ap != NULL; ++ap);
+ msgq(sp, M_ERR,
+ "167|%d more files to edit", (ap - sp->cargv) - 1);
+
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * ex_init --
+ * Init the screen for ex.
+ *
+ * PUBLIC: int ex_init __P((SCR *));
+ */
+int
+ex_init(sp)
+ SCR *sp;
+{
+ GS *gp;
+
+ gp = sp->gp;
+
+ if (gp->scr_screen(sp, SC_EX))
+ return (1);
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
+
+ sp->rows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+
+ F_CLR(sp, SC_VI);
+ F_SET(sp, SC_EX | SC_SCR_EX);
+ return (0);
+}
+
+/*
+ * ex_emsg --
+ * Display a few common ex and vi error messages.
+ *
+ * PUBLIC: void ex_emsg __P((SCR *, char *, exm_t));
+ */
+void
+ex_emsg(sp, p, which)
+ SCR *sp;
+ char *p;
+ exm_t which;
+{
+ switch (which) {
+ case EXM_EMPTYBUF:
+ msgq(sp, M_ERR, "168|Buffer %s is empty", p);
+ break;
+ case EXM_FILECOUNT:
+ msgq_str(sp, M_ERR, p,
+ "144|%s: expanded into too many file names");
+ break;
+ case EXM_NOCANON:
+ msgq(sp, M_ERR,
+ "283|The %s command requires the ex terminal interface", p);
+ break;
+ case EXM_NOCANON_F:
+ msgq(sp, M_ERR,
+ "272|That form of %s requires the ex terminal interface",
+ p);
+ break;
+ case EXM_NOFILEYET:
+ if (p == NULL)
+ msgq(sp, M_ERR,
+ "274|Command failed, no file read in yet.");
+ else
+ msgq(sp, M_ERR,
+ "173|The %s command requires that a file have already been read in", p);
+ break;
+ case EXM_NOPREVBUF:
+ msgq(sp, M_ERR, "171|No previous buffer to execute");
+ break;
+ case EXM_NOPREVRE:
+ msgq(sp, M_ERR, "172|No previous regular expression");
+ break;
+ case EXM_NOSUSPEND:
+ msgq(sp, M_ERR, "230|This screen may not be suspended");
+ break;
+ case EXM_SECURE:
+ msgq(sp, M_ERR,
+"290|The %s command is not supported when the secure edit option is set", p);
+ break;
+ case EXM_SECURE_F:
+ msgq(sp, M_ERR,
+"284|That form of %s is not supported when the secure edit option is set", p);
+ break;
+ case EXM_USAGE:
+ msgq(sp, M_ERR, "174|Usage: %s", p);
+ break;
+ }
+}
diff --git a/contrib/nvi/ex/ex_version.c b/contrib/nvi/ex/ex_version.c
new file mode 100644
index 000000000000..d7363c8bd1ac
--- /dev/null
+++ b/contrib/nvi/ex/ex_version.c
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_version.c 10.31 (Berkeley) 8/22/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "version.h"
+
+/*
+ * ex_version -- :version
+ * Display the program version.
+ *
+ * PUBLIC: int ex_version __P((SCR *, EXCMD *));
+ */
+int
+ex_version(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ msgq(sp, M_INFO, VI_VERSION);
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_visual.c b/contrib/nvi/ex/ex_visual.c
new file mode 100644
index 000000000000..82e503d44658
--- /dev/null
+++ b/contrib/nvi/ex/ex_visual.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_visual.c 10.13 (Berkeley) 6/28/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+
+/*
+ * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags]
+ * Switch to visual mode.
+ *
+ * PUBLIC: int ex_visual __P((SCR *, EXCMD *));
+ */
+int
+ex_visual(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ SCR *tsp;
+ 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,
+ "175|The visual command requires that the open option be set");
+ return (1);
+ }
+
+ /* Move to the address. */
+ sp->lno = cmdp->addr1.lno == 0 ? 1 : 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 (FL_ISSET(cmdp->iflags,
+ E_C_CARAT | E_C_DASH | E_C_DOT | E_C_PLUS)) {
+ case E_C_CARAT:
+ pos = '^';
+ break;
+ case E_C_DASH:
+ pos = '-';
+ break;
+ case E_C_DOT:
+ pos = '.';
+ break;
+ case E_C_PLUS:
+ pos = '+';
+ break;
+ default:
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = 0;
+ (void)nonblank(sp, sp->lno, &sp->cno);
+ F_SET(sp->frp, FR_CURSORSET);
+ goto nopush;
+ }
+
+ if (FL_ISSET(cmdp->iflags, E_C_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)v_event_push(sp, NULL, buf, len, 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 (FL_ISSET(cmdp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT)) {
+ case E_C_HASH:
+ O_SET(sp, O_NUMBER);
+ break;
+ case E_C_LIST:
+ O_SET(sp, O_LIST);
+ break;
+ case E_C_PRINT:
+ break;
+ }
+
+nopush: /*
+ * !!!
+ * You can call the visual part of the editor from within an ex
+ * global command.
+ *
+ * XXX
+ * Historically, undoing a visual session was a single undo command,
+ * i.e. you could undo all of the changes you made in visual mode.
+ * We don't get this right; I'm waiting for the new logging code to
+ * be available.
+ *
+ * It's explicit, don't have to wait for the user, unless there's
+ * already a reason to wait.
+ */
+ if (!F_ISSET(sp, SC_SCR_EXWROTE))
+ F_SET(sp, SC_EX_WAIT_NO);
+
+ if (F_ISSET(sp, SC_EX_GLOBAL)) {
+ /*
+ * When the vi screen(s) exit, we don't want to lose our hold
+ * on this screen or this file, otherwise we're going to fail
+ * fairly spectacularly.
+ */
+ ++sp->refcnt;
+ ++sp->ep->refcnt;
+
+ /*
+ * Fake up a screen pointer -- vi doesn't get to change our
+ * underlying file, regardless.
+ */
+ tsp = sp;
+ if (vi(&tsp))
+ return (1);
+
+ /*
+ * !!!
+ * Historically, if the user exited the vi screen(s) using an
+ * ex quit command (e.g. :wq, :q) ex/vi exited, it was only if
+ * they exited vi using the Q command that ex continued. Some
+ * early versions of nvi continued in ex regardless, but users
+ * didn't like the semantic.
+ *
+ * Reset the screen.
+ */
+ if (ex_init(sp))
+ return (1);
+
+ /* Move out of the vi screen. */
+ (void)ex_puts(sp, "\n");
+ } else {
+ F_CLR(sp, SC_EX | SC_SCR_EX);
+ F_SET(sp, SC_VI);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ex/ex_write.c b/contrib/nvi/ex/ex_write.c
new file mode 100644
index 000000000000..b3122e353563
--- /dev/null
+++ b/contrib/nvi/ex/ex_write.c
@@ -0,0 +1,375 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_write.c 10.30 (Berkeley) 7/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+enum which {WN, WQ, WRITE, XIT};
+static int exwr __P((SCR *, EXCMD *, enum which));
+
+/*
+ * ex_wn -- :wn[!] [>>] [file]
+ * Write to a file and switch to the next one.
+ *
+ * PUBLIC: int ex_wn __P((SCR *, EXCMD *));
+ */
+int
+ex_wn(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ if (exwr(sp, cmdp, WN))
+ return (1);
+ if (file_m3(sp, 0))
+ return (1);
+
+ /* The file name isn't a new file to edit. */
+ cmdp->argc = 0;
+
+ return (ex_next(sp, cmdp));
+}
+
+/*
+ * ex_wq -- :wq[!] [>>] [file]
+ * Write to a file and quit.
+ *
+ * PUBLIC: int ex_wq __P((SCR *, EXCMD *));
+ */
+int
+ex_wq(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ int force;
+
+ if (exwr(sp, cmdp, WQ))
+ return (1);
+ if (file_m3(sp, 0))
+ return (1);
+
+ force = FL_ISSET(cmdp->iflags, E_C_FORCE);
+
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
+ return (0);
+}
+
+/*
+ * ex_write -- :write[!] [>>] [file]
+ * :write [!] [cmd]
+ * Write to a file.
+ *
+ * PUBLIC: int ex_write __P((SCR *, EXCMD *));
+ */
+int
+ex_write(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ return (exwr(sp, cmdp, WRITE));
+}
+
+
+/*
+ * ex_xit -- :x[it]! [file]
+ * Write out any modifications and quit.
+ *
+ * PUBLIC: int ex_xit __P((SCR *, EXCMD *));
+ */
+int
+ex_xit(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ int force;
+
+ NEEDFILE(sp, cmdp);
+
+ if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
+ return (1);
+ if (file_m3(sp, 0))
+ return (1);
+
+ force = FL_ISSET(cmdp->iflags, E_C_FORCE);
+
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
+ return (0);
+}
+
+/*
+ * exwr --
+ * The guts of the ex write commands.
+ */
+static int
+exwr(sp, cmdp, cmd)
+ SCR *sp;
+ EXCMD *cmdp;
+ enum which cmd;
+{
+ MARK rm;
+ int flags;
+ char *name, *p;
+
+ NEEDFILE(sp, cmdp);
+
+ /* All write commands can have an associated '!'. */
+ LF_INIT(FS_POSSIBLE);
+ if (FL_ISSET(cmdp->iflags, E_C_FORCE))
+ LF_SET(FS_FORCE);
+
+ /* Skip any leading whitespace. */
+ if (cmdp->argc != 0)
+ for (p = cmdp->argv[0]->bp; *p != '\0' && isblank(*p); ++p);
+
+ /* If "write !" it's a pipe to a utility. */
+ if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
+ /* Secure means no shell access. */
+ if (O_ISSET(sp, O_SECURE)) {
+ ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F);
+ return (1);
+ }
+
+ /* Expand the argument. */
+ for (++p; *p && isblank(*p); ++p);
+ if (*p == '\0') {
+ ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
+ return (1);
+ }
+ if (argv_exp1(sp, cmdp, p, strlen(p), 1))
+ return (1);
+
+ /*
+ * Historically, vi waited after a write filter even if there
+ * wasn't any output from the command. People complained when
+ * nvi waited only if there was output, wanting the visual cue
+ * that the program hadn't written anything.
+ */
+ F_SET(sp, SC_EX_WAIT_YES);
+
+ /*
+ * !!!
+ * Ignore the return cursor position, the cursor doesn't
+ * move.
+ */
+ if (ex_filter(sp, cmdp, &cmdp->addr1,
+ &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
+ return (1);
+
+ /* Ex terminates with a bang, even if the command fails. */
+ if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
+ (void)ex_puts(sp, "!\n");
+
+ return (0);
+ }
+
+ /* Set the FS_ALL flag if we're writing the entire file. */
+ if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
+ LF_SET(FS_ALL);
+
+ /* If "write >>" it's an append to a file. */
+ if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
+ LF_SET(FS_APPEND);
+
+ /* Skip ">>" and whitespace. */
+ for (p += 2; *p && isblank(*p); ++p);
+ }
+
+ /* If no other arguments, just write the file back. */
+ if (cmdp->argc == 0 || *p == '\0')
+ return (file_write(sp,
+ &cmdp->addr1, &cmdp->addr2, NULL, flags));
+
+ /* Build an argv so we get an argument count and file expansion. */
+ if (argv_exp2(sp, cmdp, p, strlen(p)))
+ return (1);
+
+ /*
+ * 0 args: impossible.
+ * 1 args: impossible (I hope).
+ * 2 args: read it.
+ * >2 args: object, too many args.
+ *
+ * The 1 args case depends on the argv_sexp() function refusing
+ * to return success without at least one non-blank character.
+ */
+ switch (cmdp->argc) {
+ case 0:
+ case 1:
+ abort();
+ /* NOTREACHED */
+ case 2:
+ name = cmdp->argv[1]->bp;
+
+ /*
+ * !!!
+ * Historically, the read and write commands renamed
+ * "unnamed" files, or, if the file had a name, set
+ * the alternate file name.
+ */
+ if (F_ISSET(sp->frp, FR_TMPFILE) &&
+ !F_ISSET(sp->frp, FR_EXNAMED)) {
+ if ((p = v_strdup(sp,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len)) != NULL) {
+ free(sp->frp->name);
+ sp->frp->name = p;
+ }
+ /*
+ * The file has a real name, it's no longer a
+ * temporary, clear the temporary file flags.
+ *
+ * !!!
+ * If we're writing the whole file, FR_NAMECHANGE
+ * will be cleared by the write routine -- this is
+ * historic practice.
+ */
+ F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
+ F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);
+
+ /* Notify the screen. */
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+ } else
+ set_alt_name(sp, name);
+ break;
+ default:
+ ex_emsg(sp, p, EXM_FILECOUNT);
+ return (1);
+ }
+
+ return (file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags));
+}
+
+/*
+ * ex_writefp --
+ * Write a range of lines to a FILE *.
+ *
+ * PUBLIC: int ex_writefp __P((SCR *,
+ * PUBLIC: char *, FILE *, MARK *, MARK *, u_long *, u_long *, int));
+ */
+int
+ex_writefp(sp, name, fp, fm, tm, nlno, nch, silent)
+ SCR *sp;
+ char *name;
+ FILE *fp;
+ MARK *fm, *tm;
+ u_long *nlno, *nch;
+ int silent;
+{
+ struct stat sb;
+ GS *gp;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ recno_t fline, tline, lcnt;
+ size_t len;
+ int rval;
+ char *msg, *p;
+
+ gp = sp->gp;
+ fline = fm->lno;
+ tline = tm->lno;
+
+ if (nlno != NULL) {
+ *nch = 0;
+ *nlno = 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 db_get() and msgq(). Db_get() is safe, see
+ * the comment in ex_filter.c:ex_filter() 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."
+ */
+ ccnt = 0;
+ lcnt = 0;
+ msg = "253|Writing...";
+ if (tline != 0)
+ for (; fline <= tline; ++fline, ++lcnt) {
+ /* Caller has to provide any interrupt message. */
+ if ((lcnt + 1) % INTERRUPT_CHECK == 0) {
+ if (INTERRUPTED(sp))
+ break;
+ if (!silent) {
+ gp->scr_busy(sp, msg, msg == NULL ?
+ BUSY_UPDATE : BUSY_ON);
+ msg = NULL;
+ }
+ }
+ if (db_get(sp, fline, DBG_FATAL, &p, &len))
+ goto err;
+ if (fwrite(p, 1, len, fp) != len)
+ goto err;
+ ccnt += len;
+ if (putc('\n', fp) != '\n')
+ break;
+ ++ccnt;
+ }
+
+ if (fflush(fp))
+ goto err;
+ /*
+ * XXX
+ * I don't trust NFS -- check to make sure that we're talking to
+ * a regular file and sync so that NFS is forced to flush.
+ */
+ if (!fstat(fileno(fp), &sb) &&
+ S_ISREG(sb.st_mode) && fsync(fileno(fp)))
+ goto err;
+
+ if (fclose(fp))
+ goto err;
+
+ rval = 0;
+ if (0) {
+err: if (!F_ISSET(sp->ep, F_MULTILOCK))
+ msgq_str(sp, M_SYSERR, name, "%s");
+ (void)fclose(fp);
+ rval = 1;
+ }
+
+ if (!silent)
+ gp->scr_busy(sp, NULL, BUSY_OFF);
+
+ /* Report the possibly partial transfer. */
+ if (nlno != NULL) {
+ *nch = ccnt;
+ *nlno = lcnt;
+ }
+ return (rval);
+}
diff --git a/contrib/nvi/ex/ex_yank.c b/contrib/nvi/ex/ex_yank.c
new file mode 100644
index 000000000000..778dc7dee62c
--- /dev/null
+++ b/contrib/nvi/ex/ex_yank.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_yank.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_yank -- :[line [,line]] ya[nk] [buffer] [count]
+ * Yank the lines into a buffer.
+ *
+ * PUBLIC: int ex_yank __P((SCR *, EXCMD *));
+ */
+int
+ex_yank(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * !!!
+ * Historically, yanking lines in ex didn't count toward the
+ * number-of-lines-yanked report.
+ */
+ return (cut(sp,
+ FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE));
+}
diff --git a/contrib/nvi/ex/ex_z.c b/contrib/nvi/ex/ex_z.c
new file mode 100644
index 000000000000..41b72ad1fd42
--- /dev/null
+++ b/contrib/nvi/ex/ex_z.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ex_z.c 10.10 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+
+/*
+ * ex_z -- :[line] z [^-.+=] [count] [flags]
+ * Adjust window.
+ *
+ * PUBLIC: int ex_z __P((SCR *, EXCMD *));
+ */
+int
+ex_z(sp, cmdp)
+ SCR *sp;
+ EXCMD *cmdp;
+{
+ MARK abs;
+ recno_t cnt, equals, lno;
+ int eofcheck;
+
+ NEEDFILE(sp, cmdp);
+
+ /*
+ * !!!
+ * 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 (FL_ISSET(cmdp->iflags, E_C_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 (FL_ISSET(cmdp->iflags,
+ E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) {
+ case E_C_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_C_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_C_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;
+
+ /*
+ * !!!
+ * Historically, z. set the absolute cursor mark.
+ */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ (void)mark_set(sp, ABSMARK1, &abs, 1);
+ break;
+ case E_C_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, cmdp))
+ return (1);
+ (void)ex_puts(sp, "----------------------------------------\n");
+ cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
+ if (ex_pr(sp, cmdp))
+ return (1);
+ (void)ex_puts(sp, "----------------------------------------\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_ADDR_DEF))
+ ++lno;
+ /* FALLTHROUGH */
+ case E_C_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 (db_last(sp, &lno))
+ return (1);
+ if (cmdp->addr2.lno > lno)
+ cmdp->addr2.lno = lno;
+ }
+
+ if (ex_pr(sp, cmdp))
+ return (1);
+ if (equals)
+ sp->lno = equals;
+ return (0);
+}
diff --git a/contrib/nvi/ex/script.h b/contrib/nvi/ex/script.h
new file mode 100644
index 000000000000..e29f63347961
--- /dev/null
+++ b/contrib/nvi/ex/script.h
@@ -0,0 +1,23 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)script.h 10.2 (Berkeley) 3/6/96
+ */
+
+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 */
+#ifdef TIOCGWINSZ
+ struct winsize sh_win; /* Window size. */
+#endif
+ struct termios sh_term; /* Terminal information. */
+};
diff --git a/contrib/nvi/ex/tag.h b/contrib/nvi/ex/tag.h
new file mode 100644
index 000000000000..aee3dd27471d
--- /dev/null
+++ b/contrib/nvi/ex/tag.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1994, 1996
+ * Rob Mayoff. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)tag.h 10.5 (Berkeley) 5/15/96
+ */
+
+/*
+ * Cscope connection information. One of these is maintained per cscope
+ * connection, linked from the EX_PRIVATE structure.
+ */
+struct _csc {
+ LIST_ENTRY(_csc) q; /* Linked list of cscope connections. */
+
+ char *dname; /* Base directory of this cscope connection. */
+ size_t dlen; /* Length of base directory. */
+ pid_t pid; /* PID of the connected cscope process. */
+ time_t mtime; /* Last modification time of cscope database. */
+
+ FILE *from_fp; /* from cscope: FILE. */
+ int from_fd; /* from cscope: file descriptor. */
+ FILE *to_fp; /* to cscope: FILE. */
+ int to_fd; /* to cscope: file descriptor. */
+
+ char **paths; /* Array of search paths for this cscope. */
+ char *pbuf; /* Search path buffer. */
+ size_t pblen; /* Search path buffer length. */
+
+ char buf[1]; /* Variable length buffer. */
+};
+
+/*
+ * Tag file information. One of these is maintained per tag file, linked
+ * from the EXPRIVATE structure.
+ */
+struct _tagf { /* Tag files. */
+ TAILQ_ENTRY(_tagf) q; /* Linked list of tag files. */
+ char *name; /* Tag file name. */
+ int errnum; /* Errno. */
+
+#define TAGF_ERR 0x01 /* Error occurred. */
+#define TAGF_ERR_WARN 0x02 /* Error reported. */
+ u_int8_t flags;
+};
+
+/*
+ * Tags are structured internally as follows:
+ *
+ * +----+ +----+ +----+ +----+
+ * | EP | -> | Q1 | <-- | T1 | <-- | T2 |
+ * +----+ +----+ --> +----+ --> +----+
+ * |
+ * +----+ +----+
+ * | Q2 | <-- | T1 |
+ * +----+ --> +----+
+ * |
+ * +----+ +----+
+ * | Q3 | <-- | T1 |
+ * +----+ --> +----+
+ *
+ * Each Q is a TAGQ, or tag "query", which is the result of one tag or cscope
+ * command. Each Q references one or more TAG's, or tagged file locations.
+ *
+ * tag: put a new Q at the head (^])
+ * tagnext: T1 -> T2 inside Q (^N)
+ * tagprev: T2 -> T1 inside Q (^P)
+ * tagpop: discard Q (^T)
+ * tagtop: discard all Q
+ */
+struct _tag { /* Tag list. */
+ CIRCLEQ_ENTRY(_tag) q; /* Linked list of tags. */
+
+ /* Tag pop/return information. */
+ FREF *frp; /* Saved file. */
+ recno_t lno; /* Saved line number. */
+ size_t cno; /* Saved column number. */
+
+ char *fname; /* Filename. */
+ size_t fnlen; /* Filename length. */
+ recno_t slno; /* Search line number. */
+ char *search; /* Search string. */
+ size_t slen; /* Search string length. */
+
+ char buf[1]; /* Variable length buffer. */
+};
+
+struct _tagq { /* Tag queue. */
+ CIRCLEQ_ENTRY(_tagq) q; /* Linked list of tag queues. */
+ /* This queue's tag list. */
+ CIRCLEQ_HEAD(_tagqh, _tag) tagq;
+
+ TAG *current; /* Current TAG within the queue. */
+
+ char *tag; /* Tag string. */
+ size_t tlen; /* Tag string length. */
+
+#define TAG_CSCOPE 0x01 /* Cscope tag. */
+ u_int8_t flags;
+
+ char buf[1]; /* Variable length buffer. */
+};
diff --git a/contrib/nvi/ex/version.h b/contrib/nvi/ex/version.h
new file mode 100644
index 000000000000..7d657b635fc2
--- /dev/null
+++ b/contrib/nvi/ex/version.h
@@ -0,0 +1,2 @@
+#define VI_VERSION \
+ "Version 1.79 (10/23/96) The CSRG, University of California, Berkeley."
diff --git a/contrib/nvi/include/bitstring.h b/contrib/nvi/include/bitstring.h
new file mode 100644
index 000000000000..88437e7fb9f7
--- /dev/null
+++ b/contrib/nvi/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/contrib/nvi/include/cl_extern.h b/contrib/nvi/include/cl_extern.h
new file mode 100644
index 000000000000..bafeb944e969
--- /dev/null
+++ b/contrib/nvi/include/cl_extern.h
@@ -0,0 +1,56 @@
+#ifndef HAVE_CURSES_ADDNSTR
+int addnstr __P((char *, int));
+#endif
+#ifndef HAVE_CURSES_BEEP
+void beep __P((void));
+#endif
+#ifndef HAVE_CURSES_FLASH
+void flash __P((void));
+#endif
+#ifndef HAVE_CURSES_IDLOK
+void idlok __P((WINDOW *, int));
+#endif
+#ifndef HAVE_CURSES_KEYPAD
+int keypad __P((void *, int));
+#endif
+#ifndef HAVE_CURSES_NEWTERM
+void *newterm __P((const char *, FILE *, FILE *));
+#endif
+#ifndef HAVE_CURSES_SETUPTERM
+void setupterm __P((char *, int, int *));
+#endif
+#ifdef HAVE_CURSES_TIGETSTR
+char *tigetstr();
+#else
+char *tigetstr __P((char *));
+#endif
+#ifndef HAVE_CURSES_TIGETSTR
+int tigetnum __P((char *));
+#endif
+int cl_addstr __P((SCR *, const char *, size_t));
+int cl_attr __P((SCR *, scr_attr_t, int));
+int cl_baud __P((SCR *, u_long *));
+int cl_bell __P((SCR *));
+int cl_clrtoeol __P((SCR *));
+int cl_cursor __P((SCR *, size_t *, size_t *));
+int cl_deleteln __P((SCR *));
+int cl_ex_adjust __P((SCR *, exadj_t));
+int cl_insertln __P((SCR *));
+int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+int cl_move __P((SCR *, size_t, size_t));
+int cl_refresh __P((SCR *, int));
+int cl_rename __P((SCR *, char *, int));
+int cl_suspend __P((SCR *, int *));
+void cl_usage __P((void));
+int sig_init __P((GS *, SCR *));
+int cl_event __P((SCR *, EVENT *, u_int32_t, int));
+int cl_screen __P((SCR *, u_int32_t));
+int cl_quit __P((GS *));
+int cl_getcap __P((SCR *, char *, char **));
+int cl_term_init __P((SCR *));
+int cl_term_end __P((GS *));
+int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+int cl_optchange __P((SCR *, int, char *, u_long *));
+int cl_omesg __P((SCR *, CL_PRIVATE *, int));
+int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
+int cl_putchar __P((int));
diff --git a/contrib/nvi/include/com_extern.h b/contrib/nvi/include/com_extern.h
new file mode 100644
index 000000000000..f140f9e34232
--- /dev/null
+++ b/contrib/nvi/include/com_extern.h
@@ -0,0 +1,199 @@
+#ifndef HAVE_BSEARCH
+void *bsearch __P((const void *, const void *, size_t,
+ size_t, int (*)(const void *, const void *)));
+#endif
+#ifndef HAVE_SETENV
+int setenv __P((const char *, const char *, int));
+#endif
+#ifndef HAVE_UNSETENV
+void unsetenv __P((const char *));
+#endif
+#ifndef HAVE_GETHOSTNAME
+int gethostname __P((char *, int));
+#endif
+#ifndef HAVE_GETOPT
+int getopt __P((int, char * const *, const char *));
+#endif
+#ifndef HAVE_MEMCHR
+void *memchr __P((const void *, int, size_t));
+#endif
+#ifndef HAVE_MEMCPY
+void *memcpy __P((void *, const void *, size_t));
+#endif
+#ifndef HAVE_MEMMOVE
+void *memmove __P((void *, const void *, size_t));
+#endif
+#ifndef HAVE_MEMSET
+void *memset __P((void *, int, size_t));
+#endif
+#ifndef HAVE_MKSTEMP
+int mkstemp __P((char *));
+#endif
+#ifndef HAVE_MMAP
+char *mmap __P((char *, size_t, int, int, int, off_t));
+#endif
+#ifndef HAVE_MMAP
+int munmap __P((char *, size_t));
+#endif
+#ifndef HAVE_SNPRINTF
+int snprintf __P((char *, size_t, const char *, ...));
+#endif
+#ifndef HAVE_STRDUP
+char *strdup __P((const char *));
+#endif
+#ifndef HAVE_STRERROR
+char *strerror __P((int));
+#endif
+#ifndef HAVE_STRPBRK
+char *strpbrk __P((const char *, const char *));
+#endif
+#ifndef HAVE_STRSEP
+char *strsep __P((char **, const char *));
+#endif
+#ifndef HAVE_STRTOL
+long strtol __P((const char *, char **, int));
+#endif
+#ifndef HAVE_STRTOUL
+unsigned long strtoul __P((const char *, char **, int));
+#endif
+#ifndef HAVE_VSNPRINTF
+int vsnprintf __P((char *, size_t, const char *, ...));
+#endif
+SCR *api_fscreen __P((int, char *));
+int api_aline __P((SCR *, recno_t, char *, size_t));
+int api_dline __P((SCR *, recno_t));
+int api_gline __P((SCR *, recno_t, char **, size_t *));
+int api_iline __P((SCR *, recno_t, char *, size_t));
+int api_lline __P((SCR *, recno_t *));
+int api_sline __P((SCR *, recno_t, char *, size_t));
+int api_getmark __P((SCR *, int, MARK *));
+int api_setmark __P((SCR *, int, MARK *));
+int api_nextmark __P((SCR *, int, char *));
+int api_getcursor __P((SCR *, MARK *));
+int api_setcursor __P((SCR *, MARK *));
+void api_emessage __P((SCR *, char *));
+void api_imessage __P((SCR *, char *));
+int api_edit __P((SCR *, char *, SCR **, int));
+int api_escreen __P((SCR *));
+int api_swscreen __P((SCR *, SCR *));
+int api_map __P((SCR *, char *, char *, size_t));
+int api_unmap __P((SCR *, char *));
+int api_opts_get __P((SCR *, char *, char **, int *));
+int api_opts_set __P((SCR *, char *, char *, u_long, int));
+int api_run_str __P((SCR *, char *));
+int cut __P((SCR *, CHAR_T *, MARK *, MARK *, int));
+int cut_line __P((SCR *, recno_t, size_t, size_t, CB *));
+void cut_close __P((GS *));
+TEXT *text_init __P((SCR *, const char *, size_t, size_t));
+void text_lfree __P((TEXTH *));
+void text_free __P((TEXT *));
+int del __P((SCR *, MARK *, MARK *, int));
+FREF *file_add __P((SCR *, CHAR_T *));
+int file_init __P((SCR *, FREF *, char *, int));
+int file_end __P((SCR *, EXF *, int));
+int file_write __P((SCR *, MARK *, MARK *, char *, int));
+int file_m1 __P((SCR *, int, int));
+int file_m2 __P((SCR *, int));
+int file_m3 __P((SCR *, int));
+int file_aw __P((SCR *, int));
+void set_alt_name __P((SCR *, char *));
+lockr_t file_lock __P((SCR *, char *, int *, int, int));
+int v_key_init __P((SCR *));
+void v_key_ilookup __P((SCR *));
+size_t v_key_len __P((SCR *, ARG_CHAR_T));
+CHAR_T *v_key_name __P((SCR *, ARG_CHAR_T));
+int v_key_val __P((SCR *, ARG_CHAR_T));
+int v_event_push __P((SCR *, EVENT *, CHAR_T *, size_t, u_int));
+int v_event_get __P((SCR *, EVENT *, int, u_int32_t));
+void v_event_err __P((SCR *, EVENT *));
+int v_event_flush __P((SCR *, u_int));
+int db_eget __P((SCR *, recno_t, char **, size_t *, int *));
+int db_get __P((SCR *, recno_t, u_int32_t, char **, size_t *));
+int db_delete __P((SCR *, recno_t));
+int db_append __P((SCR *, int, recno_t, char *, size_t));
+int db_insert __P((SCR *, recno_t, char *, size_t));
+int db_set __P((SCR *, recno_t, char *, size_t));
+int db_exist __P((SCR *, recno_t));
+int db_last __P((SCR *, recno_t *));
+void db_err __P((SCR *, recno_t));
+int log_init __P((SCR *, EXF *));
+int log_end __P((SCR *, EXF *));
+int log_cursor __P((SCR *));
+int log_line __P((SCR *, recno_t, u_int));
+int log_mark __P((SCR *, LMARK *));
+int log_backward __P((SCR *, MARK *));
+int log_setline __P((SCR *));
+int log_forward __P((SCR *, MARK *));
+int editor __P((GS *, int, char *[]));
+void v_end __P((GS *));
+int mark_init __P((SCR *, EXF *));
+int mark_end __P((SCR *, EXF *));
+int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
+int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
+int mark_insdel __P((SCR *, lnop_t, recno_t));
+void msgq __P((SCR *, mtype_t, const char *, ...));
+void msgq_str __P((SCR *, mtype_t, char *, char *));
+void mod_rpt __P((SCR *));
+void msgq_status __P((SCR *, recno_t, u_int));
+int msg_open __P((SCR *, char *));
+void msg_close __P((GS *));
+const char *msg_cmsg __P((SCR *, cmsg_t, size_t *));
+const char *msg_cat __P((SCR *, const char *, size_t *));
+char *msg_print __P((SCR *, const char *, int *));
+int opts_init __P((SCR *, int *));
+int opts_set __P((SCR *, ARGS *[], char *));
+int o_set __P((SCR *, int, u_int, char *, u_long));
+int opts_empty __P((SCR *, int, int));
+void opts_dump __P((SCR *, enum optdisp));
+int opts_save __P((SCR *, FILE *));
+OPTLIST const *opts_search __P((char *));
+void opts_nomatch __P((SCR *, char *));
+int opts_copy __P((SCR *, SCR *));
+void opts_free __P((SCR *));
+int f_altwerase __P((SCR *, OPTION *, char *, u_long *));
+int f_columns __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_msgcat __P((SCR *, OPTION *, char *, u_long *));
+int f_paragraph __P((SCR *, OPTION *, char *, u_long *));
+int f_print __P((SCR *, OPTION *, char *, u_long *));
+int f_readonly __P((SCR *, OPTION *, char *, u_long *));
+int f_recompile __P((SCR *, OPTION *, char *, u_long *));
+int f_reformat __P((SCR *, OPTION *, char *, u_long *));
+int f_section __P((SCR *, OPTION *, char *, u_long *));
+int f_ttywerase __P((SCR *, OPTION *, char *, u_long *));
+int f_w300 __P((SCR *, OPTION *, char *, u_long *));
+int f_w1200 __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 put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
+int rcv_tmp __P((SCR *, EXF *, char *));
+int rcv_init __P((SCR *));
+int rcv_sync __P((SCR *, u_int));
+int rcv_list __P((SCR *));
+int rcv_read __P((SCR *, FREF *));
+int screen_init __P((GS *, SCR *, SCR **));
+int screen_end __P((SCR *));
+SCR *screen_next __P((SCR *));
+int f_search __P((SCR *,
+ MARK *, MARK *, char *, size_t, char **, u_int));
+int b_search __P((SCR *,
+ MARK *, MARK *, char *, size_t, char **, u_int));
+void search_busy __P((SCR *, busy_t));
+int seq_set __P((SCR *, CHAR_T *,
+ size_t, CHAR_T *, size_t, CHAR_T *, size_t, seq_t, int));
+int seq_delete __P((SCR *, CHAR_T *, size_t, seq_t));
+int seq_mdel __P((SEQ *));
+SEQ *seq_find
+ __P((SCR *, SEQ **, EVENT *, CHAR_T *, size_t, seq_t, int *));
+void seq_close __P((GS *));
+int seq_dump __P((SCR *, seq_t, int));
+int seq_save __P((SCR *, FILE *, char *, seq_t));
+int e_memcmp __P((CHAR_T *, EVENT *, size_t));
+void *binc __P((SCR *, void *, size_t *, size_t));
+int nonblank __P((SCR *, recno_t, size_t *));
+char *tail __P((char *));
+CHAR_T *v_strdup __P((SCR *, const CHAR_T *, size_t));
+enum nresult nget_uslong __P((u_long *, const char *, char **, int));
+enum nresult nget_slong __P((long *, const char *, char **, int));
+void TRACE __P((SCR *, const char *, ...));
diff --git a/contrib/nvi/include/ex_def.h b/contrib/nvi/include/ex_def.h
new file mode 100644
index 000000000000..2e69b4872f59
--- /dev/null
+++ b/contrib/nvi/include/ex_def.h
@@ -0,0 +1,78 @@
+#define C_SCROLL 0
+#define C_BANG 1
+#define C_HASH 2
+#define C_SUBAGAIN 3
+#define C_STAR 4
+#define C_SHIFTL 5
+#define C_EQUAL 6
+#define C_SHIFTR 7
+#define C_AT 8
+#define C_APPEND 9
+#define C_ABBR 10
+#define C_ARGS 11
+#define C_BG 12
+#define C_CHANGE 13
+#define C_CD 14
+#define C_CHDIR 15
+#define C_COPY 16
+#define C_CSCOPE 17
+#define C_DELETE 18
+#define C_DISPLAY 19
+#define C_EDIT 20
+#define C_EX 21
+#define C_EXUSAGE 22
+#define C_FILE 23
+#define C_FG 24
+#define C_GLOBAL 25
+#define C_HELP 26
+#define C_INSERT 27
+#define C_JOIN 28
+#define C_K 29
+#define C_LIST 30
+#define C_MOVE 31
+#define C_MARK 32
+#define C_MAP 33
+#define C_MKEXRC 34
+#define C_NEXT 35
+#define C_NUMBER 36
+#define C_OPEN 37
+#define C_PRINT 38
+#define C_PERLCMD 39
+#define C_PERLDOCMD 40
+#define C_PRESERVE 41
+#define C_PREVIOUS 42
+#define C_PUT 43
+#define C_QUIT 44
+#define C_READ 45
+#define C_RECOVER 46
+#define C_RESIZE 47
+#define C_REWIND 48
+#define C_SUBSTITUTE 49
+#define C_SCRIPT 50
+#define C_SET 51
+#define C_SHELL 52
+#define C_SOURCE 53
+#define C_STOP 54
+#define C_SUSPEND 55
+#define C_T 56
+#define C_TAG 57
+#define C_TAGNEXT 58
+#define C_TAGPOP 59
+#define C_TAGPREV 60
+#define C_TAGTOP 61
+#define C_TCLCMD 62
+#define C_UNDO 63
+#define C_UNABBREVIATE 64
+#define C_UNMAP 65
+#define C_V 66
+#define C_VERSION 67
+#define C_VISUAL_EX 68
+#define C_VISUAL_VI 69
+#define C_VIUSAGE 70
+#define C_WRITE 71
+#define C_WN 72
+#define C_WQ 73
+#define C_XIT 74
+#define C_YANK 75
+#define C_Z 76
+#define C_SUBTILDE 77
diff --git a/contrib/nvi/include/ex_extern.h b/contrib/nvi/include/ex_extern.h
new file mode 100644
index 000000000000..ed01701f1185
--- /dev/null
+++ b/contrib/nvi/include/ex_extern.h
@@ -0,0 +1,127 @@
+int ex __P((SCR **));
+int ex_cmd __P((SCR *));
+int ex_range __P((SCR *, EXCMD *, int *));
+int ex_is_abbrev __P((char *, size_t));
+int ex_is_unmap __P((char *, size_t));
+void ex_badaddr
+ __P((SCR *, EXCMDLIST const *, enum badaddr, enum nresult));
+int ex_abbr __P((SCR *, EXCMD *));
+int ex_unabbr __P((SCR *, EXCMD *));
+int ex_append __P((SCR *, EXCMD *));
+int ex_change __P((SCR *, EXCMD *));
+int ex_insert __P((SCR *, EXCMD *));
+int ex_next __P((SCR *, EXCMD *));
+int ex_prev __P((SCR *, EXCMD *));
+int ex_rew __P((SCR *, EXCMD *));
+int ex_args __P((SCR *, EXCMD *));
+char **ex_buildargv __P((SCR *, EXCMD *, char *));
+int argv_init __P((SCR *, EXCMD *));
+int argv_exp0 __P((SCR *, EXCMD *, char *, size_t));
+int argv_exp1 __P((SCR *, EXCMD *, char *, size_t, int));
+int argv_exp2 __P((SCR *, EXCMD *, char *, size_t));
+int argv_exp3 __P((SCR *, EXCMD *, char *, size_t));
+int argv_free __P((SCR *));
+int ex_at __P((SCR *, EXCMD *));
+int ex_bang __P((SCR *, EXCMD *));
+int ex_cd __P((SCR *, EXCMD *));
+int ex_cscope __P((SCR *, EXCMD *));
+int cscope_display __P((SCR *));
+int cscope_search __P((SCR *, TAGQ *, TAG *));
+int ex_delete __P((SCR *, EXCMD *));
+int ex_display __P((SCR *, EXCMD *));
+int ex_edit __P((SCR *, EXCMD *));
+int ex_equal __P((SCR *, EXCMD *));
+int ex_file __P((SCR *, EXCMD *));
+int ex_filter __P((SCR *,
+ EXCMD *, MARK *, MARK *, MARK *, char *, enum filtertype));
+int ex_global __P((SCR *, EXCMD *));
+int ex_v __P((SCR *, EXCMD *));
+int ex_g_insdel __P((SCR *, lnop_t, recno_t));
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_optchange __P((SCR *, int, char *, u_long *));
+int ex_exrc __P((SCR *));
+int ex_run_str __P((SCR *, char *, char *, size_t, int, int));
+int ex_join __P((SCR *, EXCMD *));
+int ex_map __P((SCR *, EXCMD *));
+int ex_unmap __P((SCR *, EXCMD *));
+int ex_mark __P((SCR *, EXCMD *));
+int ex_mkexrc __P((SCR *, EXCMD *));
+int ex_copy __P((SCR *, EXCMD *));
+int ex_move __P((SCR *, EXCMD *));
+int ex_open __P((SCR *, EXCMD *));
+int ex_perl __P((SCR*, EXCMD *));
+int ex_preserve __P((SCR *, EXCMD *));
+int ex_recover __P((SCR *, EXCMD *));
+int ex_list __P((SCR *, EXCMD *));
+int ex_number __P((SCR *, EXCMD *));
+int ex_pr __P((SCR *, EXCMD *));
+int ex_print __P((SCR *, EXCMD *, MARK *, MARK *, u_int32_t));
+int ex_ldisplay __P((SCR *, const char *, size_t, size_t, u_int));
+int ex_scprint __P((SCR *, MARK *, MARK *));
+int ex_printf __P((SCR *, const char *, ...));
+int ex_puts __P((SCR *, const char *));
+int ex_fflush __P((SCR *sp));
+int ex_put __P((SCR *, EXCMD *));
+int ex_quit __P((SCR *, EXCMD *));
+int ex_read __P((SCR *, EXCMD *));
+int ex_readfp __P((SCR *, char *, FILE *, MARK *, recno_t *, int));
+int ex_bg __P((SCR *, EXCMD *));
+int ex_fg __P((SCR *, EXCMD *));
+int ex_resize __P((SCR *, EXCMD *));
+int ex_sdisplay __P((SCR *));
+int ex_script __P((SCR *, EXCMD *));
+int sscr_exec __P((SCR *, recno_t));
+int sscr_input __P((SCR *));
+int sscr_end __P((SCR *));
+int ex_set __P((SCR *, EXCMD *));
+int ex_shell __P((SCR *, EXCMD *));
+int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int));
+int proc_wait __P((SCR *, long, const char *, int, int));
+int ex_shiftl __P((SCR *, EXCMD *));
+int ex_shiftr __P((SCR *, EXCMD *));
+int ex_source __P((SCR *, EXCMD *));
+int ex_stop __P((SCR *, EXCMD *));
+int ex_s __P((SCR *, EXCMD *));
+int ex_subagain __P((SCR *, EXCMD *));
+int ex_subtilde __P((SCR *, EXCMD *));
+int re_compile __P((SCR *,
+ char *, size_t, char **, size_t *, regex_t *, u_int));
+void re_error __P((SCR *, int, regex_t *));
+int ex_tag_first __P((SCR *, char *));
+int ex_tag_push __P((SCR *, EXCMD *));
+int ex_tag_next __P((SCR *, EXCMD *));
+int ex_tag_prev __P((SCR *, EXCMD *));
+int ex_tag_nswitch __P((SCR *, TAG *, int));
+int ex_tag_Nswitch __P((SCR *, TAG *, int));
+int ex_tag_pop __P((SCR *, EXCMD *));
+int ex_tag_top __P((SCR *, EXCMD *));
+int ex_tag_display __P((SCR *));
+int ex_tag_copy __P((SCR *, SCR *));
+int tagq_free __P((SCR *, TAGQ *));
+void tag_msg __P((SCR *, tagmsg_t, char *));
+int ex_tagf_alloc __P((SCR *, char *));
+int ex_tag_free __P((SCR *));
+int ex_tcl __P((SCR*, EXCMD *));
+int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t));
+int ex_undo __P((SCR *, EXCMD *));
+int ex_help __P((SCR *, EXCMD *));
+int ex_usage __P((SCR *, EXCMD *));
+int ex_viusage __P((SCR *, EXCMD *));
+void ex_cinit __P((EXCMD *,
+ int, int, recno_t, recno_t, int, ARGS **));
+void ex_cadd __P((EXCMD *, ARGS *, char *, size_t));
+int ex_getline __P((SCR *, FILE *, size_t *));
+int ex_ncheck __P((SCR *, int));
+int ex_init __P((SCR *));
+void ex_emsg __P((SCR *, char *, exm_t));
+int ex_version __P((SCR *, EXCMD *));
+int ex_visual __P((SCR *, EXCMD *));
+int ex_wn __P((SCR *, EXCMD *));
+int ex_wq __P((SCR *, EXCMD *));
+int ex_write __P((SCR *, EXCMD *));
+int ex_xit __P((SCR *, EXCMD *));
+int ex_writefp __P((SCR *,
+ char *, FILE *, MARK *, MARK *, u_long *, u_long *, int));
+int ex_yank __P((SCR *, EXCMD *));
+int ex_z __P((SCR *, EXCMD *));
diff --git a/contrib/nvi/include/ip_extern.h b/contrib/nvi/include/ip_extern.h
new file mode 100644
index 000000000000..b805343a8e81
--- /dev/null
+++ b/contrib/nvi/include/ip_extern.h
@@ -0,0 +1,23 @@
+int ip_addstr __P((SCR *, const char *, size_t));
+int ip_attr __P((SCR *, scr_attr_t, int));
+int ip_baud __P((SCR *, u_long *));
+int ip_bell __P((SCR *));
+void ip_busy __P((SCR *, const char *, busy_t));
+int ip_clrtoeol __P((SCR *));
+int ip_cursor __P((SCR *, size_t *, size_t *));
+int ip_deleteln __P((SCR *));
+int ip_ex_adjust __P((SCR *, exadj_t));
+int ip_insertln __P((SCR *));
+int ip_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+int ip_move __P((SCR *, size_t, size_t));
+int ip_refresh __P((SCR *, int));
+int ip_rename __P((SCR *));
+int ip_suspend __P((SCR *, int *));
+void ip_usage __P((void));
+int ip_event __P((SCR *, EVENT *, u_int32_t, int));
+int ip_screen __P((SCR *, u_int32_t));
+int ip_quit __P((GS *));
+int ip_term_init __P((SCR *));
+int ip_term_end __P((GS *));
+int ip_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+int ip_optchange __P((SCR *, int, char *, u_long *));
diff --git a/contrib/nvi/include/options_def.h b/contrib/nvi/include/options_def.h
new file mode 100644
index 000000000000..089fa7f4ea4c
--- /dev/null
+++ b/contrib/nvi/include/options_def.h
@@ -0,0 +1,79 @@
+#define O_ALTWERASE 0
+#define O_AUTOINDENT 1
+#define O_AUTOPRINT 2
+#define O_AUTOWRITE 3
+#define O_BACKUP 4
+#define O_BEAUTIFY 5
+#define O_CDPATH 6
+#define O_CEDIT 7
+#define O_COLUMNS 8
+#define O_COMMENT 9
+#define O_DIRECTORY 10
+#define O_EDCOMPATIBLE 11
+#define O_ESCAPETIME 12
+#define O_ERRORBELLS 13
+#define O_EXRC 14
+#define O_EXTENDED 15
+#define O_FILEC 16
+#define O_FLASH 17
+#define O_HARDTABS 18
+#define O_ICLOWER 19
+#define O_IGNORECASE 20
+#define O_KEYTIME 21
+#define O_LEFTRIGHT 22
+#define O_LINES 23
+#define O_LISP 24
+#define O_LIST 25
+#define O_LOCKFILES 26
+#define O_MAGIC 27
+#define O_MATCHTIME 28
+#define O_MESG 29
+#define O_MODELINE 30
+#define O_MSGCAT 31
+#define O_NOPRINT 32
+#define O_NUMBER 33
+#define O_OCTAL 34
+#define O_OPEN 35
+#define O_OPTIMIZE 36
+#define O_PARAGRAPHS 37
+#define O_PATH 38
+#define O_PRINT 39
+#define O_PROMPT 40
+#define O_READONLY 41
+#define O_RECDIR 42
+#define O_REDRAW 43
+#define O_REMAP 44
+#define O_REPORT 45
+#define O_RULER 46
+#define O_SCROLL 47
+#define O_SEARCHINCR 48
+#define O_SECTIONS 49
+#define O_SECURE 50
+#define O_SHELL 51
+#define O_SHELLMETA 52
+#define O_SHIFTWIDTH 53
+#define O_SHOWMATCH 54
+#define O_SHOWMODE 55
+#define O_SIDESCROLL 56
+#define O_SLOWOPEN 57
+#define O_SOURCEANY 58
+#define O_TABSTOP 59
+#define O_TAGLENGTH 60
+#define O_TAGS 61
+#define O_TERM 62
+#define O_TERSE 63
+#define O_TILDEOP 64
+#define O_TIMEOUT 65
+#define O_TTYWERASE 66
+#define O_VERBOSE 67
+#define O_W1200 68
+#define O_W300 69
+#define O_W9600 70
+#define O_WARN 71
+#define O_WINDOW 72
+#define O_WINDOWNAME 73
+#define O_WRAPLEN 74
+#define O_WRAPMARGIN 75
+#define O_WRAPSCAN 76
+#define O_WRITEANY 77
+#define O_OPTIONCOUNT 78
diff --git a/contrib/nvi/include/perl_extern.h b/contrib/nvi/include/perl_extern.h
new file mode 100644
index 000000000000..5d6dd0d33bb6
--- /dev/null
+++ b/contrib/nvi/include/perl_extern.h
@@ -0,0 +1,8 @@
+int perl_end __P((GS *));
+int perl_init __P((SCR *));
+int perl_screen_end __P((SCR*));
+int perl_ex_perl __P((SCR*, CHAR_T *, size_t, recno_t, recno_t));
+int perl_ex_perldo __P((SCR*, CHAR_T *, size_t, recno_t, recno_t));
+#ifdef USE_SFIO
+Sfdisc_t* sfdcnewnvi __P((SCR*));
+#endif
diff --git a/contrib/nvi/include/sys/queue.h b/contrib/nvi/include/sys/queue.h
new file mode 100644
index 000000000000..cbd75864ecda
--- /dev/null
+++ b/contrib/nvi/include/sys/queue.h
@@ -0,0 +1,259 @@
+/*
+ * 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.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_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 before
+ * or 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 before or
+ * 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_BEFORE(listelm, elm, field) { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->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) \
+ (head)->tqh_first->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_INSERT_BEFORE(listelm, elm, field) { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->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 /* !_SYS_QUEUE_H_ */
diff --git a/contrib/nvi/include/tcl_extern.h b/contrib/nvi/include/tcl_extern.h
new file mode 100644
index 000000000000..ac68ae4119fd
--- /dev/null
+++ b/contrib/nvi/include/tcl_extern.h
@@ -0,0 +1 @@
+int tcl_init __P((GS *));
diff --git a/contrib/nvi/include/tk_extern.h b/contrib/nvi/include/tk_extern.h
new file mode 100644
index 000000000000..20a4de459f5e
--- /dev/null
+++ b/contrib/nvi/include/tk_extern.h
@@ -0,0 +1,29 @@
+int tk_addstr __P((SCR *, const char *, size_t));
+int tk_attr __P((SCR *, scr_attr_t, int));
+int tk_baud __P((SCR *, u_long *));
+int tk_bell __P((SCR *));
+int tk_clrtoeol __P((SCR *));
+int tk_cursor __P((SCR *, size_t *, size_t *));
+int tk_deleteln __P((SCR *));
+int tk_ex_adjust __P((SCR *, exadj_t));
+int tk_insertln __P((SCR *));
+int tk_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+int tk_move __P((SCR *, size_t, size_t));
+int tk_refresh __P((SCR *, int));
+int tk_rename __P((SCR *));
+int tk_suspend __P((SCR *, int *));
+void tk_usage __P((void));
+int tk_event __P((SCR *, EVENT *, u_int32_t, int));
+int tk_key __P((ClientData, Tcl_Interp *, int, char *[]));
+int tk_screen __P((SCR *, u_int32_t));
+int tk_quit __P((GS *));
+int tk_term_init __P((SCR *));
+int tk_term_end __P((GS *));
+int tk_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+int tk_optchange __P((SCR *, int, char *, u_long *));
+int tk_ssize __P((SCR *, int, size_t *, size_t *, int *));
+int tk_op __P((ClientData, Tcl_Interp *, int, char *[]));
+int tk_opt_init __P((ClientData, Tcl_Interp *, int, char *[]));
+int tk_opt_set __P((ClientData, Tcl_Interp *, int, char *[]));
+int tk_version __P((ClientData, Tcl_Interp *, int, char *[]));
+void tk_msg __P((SCR *, mtype_t, char *, size_t));
diff --git a/contrib/nvi/include/vi_extern.h b/contrib/nvi/include/vi_extern.h
new file mode 100644
index 000000000000..b3c335e6c580
--- /dev/null
+++ b/contrib/nvi/include/vi_extern.h
@@ -0,0 +1,142 @@
+int cs_init __P((SCR *, VCS *));
+int cs_next __P((SCR *, VCS *));
+int cs_fspace __P((SCR *, VCS *));
+int cs_fblank __P((SCR *, VCS *));
+int cs_prev __P((SCR *, VCS *));
+int cs_bblank __P((SCR *, VCS *));
+int v_at __P((SCR *, VICMD *));
+int v_chrepeat __P((SCR *, VICMD *));
+int v_chrrepeat __P((SCR *, VICMD *));
+int v_cht __P((SCR *, VICMD *));
+int v_chf __P((SCR *, VICMD *));
+int v_chT __P((SCR *, VICMD *));
+int v_chF __P((SCR *, VICMD *));
+int v_delete __P((SCR *, VICMD *));
+int v_again __P((SCR *, VICMD *));
+int v_exmode __P((SCR *, VICMD *));
+int v_join __P((SCR *, VICMD *));
+int v_shiftl __P((SCR *, VICMD *));
+int v_shiftr __P((SCR *, VICMD *));
+int v_suspend __P((SCR *, VICMD *));
+int v_switch __P((SCR *, VICMD *));
+int v_tagpush __P((SCR *, VICMD *));
+int v_tagpop __P((SCR *, VICMD *));
+int v_filter __P((SCR *, VICMD *));
+int v_event_exec __P((SCR *, VICMD *));
+int v_ex __P((SCR *, VICMD *));
+int v_ecl_exec __P((SCR *));
+int v_increment __P((SCR *, VICMD *));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+int v_optchange __P((SCR *, int, char *, u_long *));
+int v_iA __P((SCR *, VICMD *));
+int v_ia __P((SCR *, VICMD *));
+int v_iI __P((SCR *, VICMD *));
+int v_ii __P((SCR *, VICMD *));
+int v_iO __P((SCR *, VICMD *));
+int v_io __P((SCR *, VICMD *));
+int v_change __P((SCR *, VICMD *));
+int v_Replace __P((SCR *, VICMD *));
+int v_subst __P((SCR *, VICMD *));
+int v_left __P((SCR *, VICMD *));
+int v_cfirst __P((SCR *, VICMD *));
+int v_first __P((SCR *, VICMD *));
+int v_ncol __P((SCR *, VICMD *));
+int v_zero __P((SCR *, VICMD *));
+int v_mark __P((SCR *, VICMD *));
+int v_bmark __P((SCR *, VICMD *));
+int v_fmark __P((SCR *, VICMD *));
+int v_match __P((SCR *, VICMD *));
+int v_paragraphf __P((SCR *, VICMD *));
+int v_paragraphb __P((SCR *, VICMD *));
+int v_buildps __P((SCR *, char *, char *));
+int v_Put __P((SCR *, VICMD *));
+int v_put __P((SCR *, VICMD *));
+int v_redraw __P((SCR *, VICMD *));
+int v_replace __P((SCR *, VICMD *));
+int v_right __P((SCR *, VICMD *));
+int v_dollar __P((SCR *, VICMD *));
+int v_screen __P((SCR *, VICMD *));
+int v_lgoto __P((SCR *, VICMD *));
+int v_home __P((SCR *, VICMD *));
+int v_middle __P((SCR *, VICMD *));
+int v_bottom __P((SCR *, VICMD *));
+int v_up __P((SCR *, VICMD *));
+int v_cr __P((SCR *, VICMD *));
+int v_down __P((SCR *, VICMD *));
+int v_hpageup __P((SCR *, VICMD *));
+int v_hpagedown __P((SCR *, VICMD *));
+int v_pagedown __P((SCR *, VICMD *));
+int v_pageup __P((SCR *, VICMD *));
+int v_lineup __P((SCR *, VICMD *));
+int v_linedown __P((SCR *, VICMD *));
+int v_searchb __P((SCR *, VICMD *));
+int v_searchf __P((SCR *, VICMD *));
+int v_searchN __P((SCR *, VICMD *));
+int v_searchn __P((SCR *, VICMD *));
+int v_searchw __P((SCR *, VICMD *));
+int v_correct __P((SCR *, VICMD *, int));
+int v_sectionf __P((SCR *, VICMD *));
+int v_sectionb __P((SCR *, VICMD *));
+int v_sentencef __P((SCR *, VICMD *));
+int v_sentenceb __P((SCR *, VICMD *));
+int v_status __P((SCR *, VICMD *));
+int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int));
+int v_txt __P((SCR *, VICMD *, MARK *,
+ const char *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t));
+int v_txt_auto __P((SCR *, recno_t, TEXT *, size_t, TEXT *));
+int v_ulcase __P((SCR *, VICMD *));
+int v_mulcase __P((SCR *, VICMD *));
+int v_Undo __P((SCR *, VICMD *));
+int v_undo __P((SCR *, VICMD *));
+void v_eof __P((SCR *, MARK *));
+void v_eol __P((SCR *, MARK *));
+void v_nomove __P((SCR *));
+void v_sof __P((SCR *, MARK *));
+void v_sol __P((SCR *));
+int v_isempty __P((char *, size_t));
+void v_emsg __P((SCR *, char *, vim_t));
+int v_wordW __P((SCR *, VICMD *));
+int v_wordw __P((SCR *, VICMD *));
+int v_wordE __P((SCR *, VICMD *));
+int v_worde __P((SCR *, VICMD *));
+int v_wordB __P((SCR *, VICMD *));
+int v_wordb __P((SCR *, VICMD *));
+int v_xchar __P((SCR *, VICMD *));
+int v_Xchar __P((SCR *, VICMD *));
+int v_yank __P((SCR *, VICMD *));
+int v_z __P((SCR *, VICMD *));
+int vs_crel __P((SCR *, long));
+int v_zexit __P((SCR *, VICMD *));
+int vi __P((SCR **));
+int vs_line __P((SCR *, SMAP *, size_t *, size_t *));
+int vs_number __P((SCR *));
+void vs_busy __P((SCR *, const char *, busy_t));
+void vs_home __P((SCR *));
+void vs_update __P((SCR *, const char *, const char *));
+void vs_msg __P((SCR *, mtype_t, char *, size_t));
+int vs_ex_resolve __P((SCR *, int *));
+int vs_resolve __P((SCR *, SCR *, int));
+int vs_repaint __P((SCR *, EVENT *));
+int vs_refresh __P((SCR *, int));
+int vs_column __P((SCR *, size_t *));
+size_t vs_screens __P((SCR *, recno_t, size_t *));
+size_t vs_columns __P((SCR *, char *, recno_t, size_t *, size_t *));
+size_t vs_rcm __P((SCR *, recno_t, int));
+size_t vs_colpos __P((SCR *, recno_t, size_t));
+int vs_change __P((SCR *, recno_t, lnop_t));
+int vs_sm_fill __P((SCR *, recno_t, pos_t));
+int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t));
+int vs_sm_1up __P((SCR *));
+int vs_sm_1down __P((SCR *));
+int vs_sm_next __P((SCR *, SMAP *, SMAP *));
+int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
+int vs_sm_cursor __P((SCR *, SMAP **));
+int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
+recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t));
+int vs_split __P((SCR *, SCR *, int));
+int vs_discard __P((SCR *, SCR **));
+int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
+int vs_bg __P((SCR *));
+int vs_swap __P((SCR *, SCR **, char *));
+int vs_resize __P((SCR *, long, adj_t));
diff --git a/contrib/nvi/ip/IP_INSTRUCTIONS b/contrib/nvi/ip/IP_INSTRUCTIONS
new file mode 100644
index 000000000000..7745c8858d6f
--- /dev/null
+++ b/contrib/nvi/ip/IP_INSTRUCTIONS
@@ -0,0 +1,41 @@
+1: Add:
+ -DRUNNING_IP
+
+ to the Makefile CFLAGS line and rebuild cl_main.o if it's already
+ been compiled.
+
+2: Add:
+
+ IPOBJS= ip_funcs.o ip_main.o ip_read.o ip_screen.o ip_term.o
+
+ after the other object lists in the Makefile.
+
+3: Add
+ $(IPOBJS)
+
+ to the end of the NVIALL= line in the Makefile.
+
+4: Add:
+
+ # Vi IP sources.
+ ip_funcs.o: $(srcdir)/ip/ip_funcs.c
+ $(CC) $(CFLAGS) $?
+ ip_main.o: $(srcdir)/ip/ip_main.c
+ $(CC) $(CFLAGS) $?
+ ip_read.o: $(srcdir)/ip/ip_read.c
+ $(CC) $(CFLAGS) $?
+ ip_screen.o: $(srcdir)/ip/ip_screen.c
+ $(CC) $(CFLAGS) $?
+ ip_term.o: $(srcdir)/ip/ip_term.c
+ $(CC) $(CFLAGS) $?
+
+ at the end of the Makefile.
+
+5: Remove cl_main.o if it exists, and make nvi.
+
+6: Go to ip_cl and change the entries in the Makefile to reflect
+ where the nvi binary was just built.
+
+7: Build ip_cl.
+
+8: Enter ip_cl and you should be running vi over a pipe.
diff --git a/contrib/nvi/ip/ip.h b/contrib/nvi/ip/ip.h
new file mode 100644
index 000000000000..f7798c3121b4
--- /dev/null
+++ b/contrib/nvi/ip/ip.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)ip.h 8.3 (Berkeley) 10/13/96
+ */
+
+typedef struct _ip_private {
+ int i_fd; /* Input file descriptor. */
+ int o_fd; /* Output file descriptor. */
+
+ size_t row; /* Current row. */
+ size_t col; /* Current column. */
+
+ size_t iblen; /* Input buffer length. */
+ size_t iskip; /* Returned input buffer. */
+ char ibuf[256]; /* Input buffer. */
+
+#define IP_SCR_VI_INIT 0x0001 /* Vi screen initialized. */
+ u_int32_t flags;
+} IP_PRIVATE;
+
+#define IPP(sp) ((IP_PRIVATE *)((sp)->gp->ip_private))
+#define GIPP(gp) ((IP_PRIVATE *)((gp)->ip_private))
+
+/* The screen line relative to a specific window. */
+#define RLNO(sp, lno) (sp)->woff + (lno)
+
+/*
+ * The IP protocol consists of frames, each containing:
+ *
+ * <IPO_><object>
+ *
+ * XXX
+ * We should have a marking byte, 0xaa to delimit frames.
+ *
+ */
+#define IPO_CODE 1 /* An event specification. */
+#define IPO_INT 2 /* 4-byte, network order integer. */
+#define IPO_STR 3 /* IPO_INT: followed by N bytes. */
+
+#define IPO_CODE_LEN 1
+#define IPO_INT_LEN 4
+
+/* A structure that can hold the information for any frame. */
+typedef struct _ip_buf {
+ int code; /* Event code. */
+ const char *str; /* String. */
+ size_t len; /* String length. */
+ u_int32_t val1; /* First value. */
+ u_int32_t val2; /* Second value. */
+} IP_BUF;
+
+/*
+ * Screen/editor IP_CODE's.
+ *
+ * The program structure depends on the event loop being able to return
+ * IPO_EOF/IPOE_ERR multiple times -- eventually enough things will end
+ * due to the events that vi will reach the command level for the screen,
+ * at which point the exit flags will be set and vi will exit.
+ *
+ * IP events sent from the screen to vi.
+ */
+#define IPO_EOF 1 /* End of input (NOT ^D). */
+#define IPO_ERR 2 /* Input error. */
+#define IPO_INTERRUPT 3 /* Interrupt. */
+#define IPO_QUIT 4 /* Quit. */
+#define IPO_RESIZE 5 /* Screen resize: IPO_INT, IPO_INT. */
+#define IPO_SIGHUP 6 /* SIGHUP. */
+#define IPO_SIGTERM 7 /* SIGTERM. */
+#define IPO_STRING 8 /* Input string: IPO_STR. */
+#define IPO_WRITE 9 /* Write. */
+
+/*
+ * IP events sent from vi to the screen.
+ */
+#define IPO_ADDSTR 1 /* Add a string: IPO_STR. */
+#define IPO_ATTRIBUTE 2 /* Set screen attribute: IPO_INT, IPO_INT. */
+#define IPO_BELL 3 /* Beep/bell/flash the terminal. */
+#define IPO_BUSY 4 /* Display a busy message: IPO_STR. */
+#define IPO_CLRTOEOL 5 /* Clear to the end of the line. */
+#define IPO_DELETELN 6 /* Delete a line. */
+#define IPO_INSERTLN 7 /* Insert a line. */
+#define IPO_MOVE 8 /* Move the cursor: IPO_INT, IPO_INT. */
+#define IPO_REDRAW 9 /* Redraw the screen. */
+#define IPO_REFRESH 10 /* Refresh the screen. */
+#define IPO_RENAME 11 /* Rename the screen: IPO_STR. */
+#define IPO_REWRITE 12 /* Rewrite a line: IPO_INT. */
+
+#include "ip_extern.h"
diff --git a/contrib/nvi/ip/ip_funcs.c b/contrib/nvi/ip/ip_funcs.c
new file mode 100644
index 000000000000..c92f1eda2439
--- /dev/null
+++ b/contrib/nvi/ip/ip_funcs.c
@@ -0,0 +1,443 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_funcs.c 8.4 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "ip.h"
+
+static int ip_send __P((SCR *, char *, IP_BUF *));
+
+/*
+ * ip_addstr --
+ * Add len bytes from the string at the cursor, advancing the cursor.
+ *
+ * PUBLIC: int ip_addstr __P((SCR *, const char *, size_t));
+ */
+int
+ip_addstr(sp, str, len)
+ SCR *sp;
+ const char *str;
+ size_t len;
+{
+ IP_BUF ipb;
+ IP_PRIVATE *ipp;
+ int iv, rval;
+
+ ipp = IPP(sp);
+
+ /*
+ * If ex isn't in control, it's the last line of the screen and
+ * it's a split screen, use inverse video.
+ */
+ iv = 0;
+ if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
+ ipp->row == LASTLINE(sp) && IS_SPLIT(sp)) {
+ iv = 1;
+ ip_attr(sp, SA_INVERSE, 1);
+ }
+ ipb.code = IPO_ADDSTR;
+ ipb.len = len;
+ ipb.str = str;
+ rval = ip_send(sp, "s", &ipb);
+
+ if (iv)
+ ip_attr(sp, SA_INVERSE, 0);
+ return (rval);
+}
+
+/*
+ * ip_attr --
+ * Toggle a screen attribute on/off.
+ *
+ * PUBLIC: int ip_attr __P((SCR *, scr_attr_t, int));
+ */
+int
+ip_attr(sp, attribute, on)
+ SCR *sp;
+ scr_attr_t attribute;
+ int on;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_ATTRIBUTE;
+ ipb.val1 = attribute;
+ ipb.val2 = on;
+
+ return (ip_send(sp, "12", &ipb));
+}
+
+/*
+ * ip_baud --
+ * Return the baud rate.
+ *
+ * PUBLIC: int ip_baud __P((SCR *, u_long *));
+ */
+int
+ip_baud(sp, ratep)
+ SCR *sp;
+ u_long *ratep;
+{
+ *ratep = 9600; /* XXX: Translation: fast. */
+ return (0);
+}
+
+/*
+ * ip_bell --
+ * Ring the bell/flash the screen.
+ *
+ * PUBLIC: int ip_bell __P((SCR *));
+ */
+int
+ip_bell(sp)
+ SCR *sp;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_BELL;
+
+ return (ip_send(sp, NULL, &ipb));
+}
+
+/*
+ * ip_busy --
+ * Display a busy message.
+ *
+ * PUBLIC: void ip_busy __P((SCR *, const char *, busy_t));
+ */
+void
+ip_busy(sp, str, bval)
+ SCR *sp;
+ const char *str;
+ busy_t bval;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_BUSY;
+ if (str == NULL) {
+ ipb.len = 0;
+ ipb.str = "";
+ } else {
+ ipb.len = strlen(str);
+ ipb.str = str;
+ }
+ ipb.val1 = bval;
+
+ (void)ip_send(sp, "s1", &ipb);
+}
+
+/*
+ * ip_clrtoeol --
+ * Clear from the current cursor to the end of the line.
+ *
+ * PUBLIC: int ip_clrtoeol __P((SCR *));
+ */
+int
+ip_clrtoeol(sp)
+ SCR *sp;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_CLRTOEOL;
+
+ return (ip_send(sp, NULL, &ipb));
+}
+
+/*
+ * ip_cursor --
+ * Return the current cursor position.
+ *
+ * PUBLIC: int ip_cursor __P((SCR *, size_t *, size_t *));
+ */
+int
+ip_cursor(sp, yp, xp)
+ SCR *sp;
+ size_t *yp, *xp;
+{
+ IP_PRIVATE *ipp;
+
+ ipp = IPP(sp);
+ *yp = ipp->row;
+ *xp = ipp->col;
+ return (0);
+}
+
+/*
+ * ip_deleteln --
+ * Delete the current line, scrolling all lines below it.
+ *
+ * PUBLIC: int ip_deleteln __P((SCR *));
+ */
+int
+ip_deleteln(sp)
+ SCR *sp;
+{
+ IP_BUF ipb;
+
+ /*
+ * This clause is required because the curses screen uses reverse
+ * video to delimit split screens. If the screen does not do this,
+ * this code won't be necessary.
+ *
+ * If the bottom line was in reverse video, rewrite it in normal
+ * video before it's scrolled.
+ */
+ if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
+ ipb.code = IPO_REWRITE;
+ ipb.val1 = RLNO(sp, LASTLINE(sp));
+ if (ip_send(sp, "1", &ipb))
+ return (1);
+ }
+
+ /*
+ * The bottom line is expected to be blank after this operation,
+ * and other screens must support that semantic.
+ */
+ ipb.code = IPO_DELETELN;
+ return (ip_send(sp, NULL, &ipb));
+}
+
+/*
+ * ip_ex_adjust --
+ * Adjust the screen for ex.
+ *
+ * PUBLIC: int ip_ex_adjust __P((SCR *, exadj_t));
+ */
+int
+ip_ex_adjust(sp, action)
+ SCR *sp;
+ exadj_t action;
+{
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * ip_insertln --
+ * Push down the current line, discarding the bottom line.
+ *
+ * PUBLIC: int ip_insertln __P((SCR *));
+ */
+int
+ip_insertln(sp)
+ SCR *sp;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_INSERTLN;
+
+ return (ip_send(sp, NULL, &ipb));
+}
+
+/*
+ * ip_keyval --
+ * Return the value for a special key.
+ *
+ * PUBLIC: int ip_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+ */
+int
+ip_keyval(sp, val, chp, dnep)
+ SCR *sp;
+ scr_keyval_t val;
+ CHAR_T *chp;
+ int *dnep;
+{
+ /*
+ * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
+ * VWERASE is a 4BSD extension.
+ */
+ switch (val) {
+ case KEY_VEOF:
+ *dnep = '\004'; /* ^D */
+ break;
+ case KEY_VERASE:
+ *dnep = '\b'; /* ^H */
+ break;
+ case KEY_VKILL:
+ *dnep = '\025'; /* ^U */
+ break;
+#ifdef VWERASE
+ case KEY_VWERASE:
+ *dnep = '\027'; /* ^W */
+ break;
+#endif
+ default:
+ *dnep = 1;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ip_move --
+ * Move the cursor.
+ *
+ * PUBLIC: int ip_move __P((SCR *, size_t, size_t));
+ */
+int
+ip_move(sp, lno, cno)
+ SCR *sp;
+ size_t lno, cno;
+{
+ IP_PRIVATE *ipp;
+ IP_BUF ipb;
+
+ ipp = IPP(sp);
+ ipp->row = lno;
+ ipp->col = cno;
+
+ ipb.code = IPO_MOVE;
+ ipb.val1 = RLNO(sp, lno);
+ ipb.val2 = cno;
+ return (ip_send(sp, "12", &ipb));
+}
+
+/*
+ * ip_refresh --
+ * Refresh the screen.
+ *
+ * PUBLIC: int ip_refresh __P((SCR *, int));
+ */
+int
+ip_refresh(sp, repaint)
+ SCR *sp;
+ int repaint;
+{
+ IP_BUF ipb;
+
+ ipb.code = repaint ? IPO_REDRAW : IPO_REFRESH;
+
+ return (ip_send(sp, NULL, &ipb));
+}
+
+/*
+ * ip_rename --
+ * Rename the file.
+ *
+ * PUBLIC: int ip_rename __P((SCR *));
+ */
+int
+ip_rename(sp)
+ SCR *sp;
+{
+ IP_BUF ipb;
+
+ ipb.code = IPO_RENAME;
+ ipb.len = strlen(sp->frp->name);
+ ipb.str = sp->frp->name;
+
+ return (ip_send(sp, "s", &ipb));
+}
+
+/*
+ * ip_suspend --
+ * Suspend a screen.
+ *
+ * PUBLIC: int ip_suspend __P((SCR *, int *));
+ */
+int
+ip_suspend(sp, allowedp)
+ SCR *sp;
+ int *allowedp;
+{
+ *allowedp = 0;
+ return (0);
+}
+
+/*
+ * ip_usage --
+ * Print out the ip usage messages.
+ *
+ * PUBLIC: void ip_usage __P((void));
+ */
+void
+ip_usage()
+{
+#define USAGE "\
+usage: vi [-eFlRrSv] [-c command] [-I ifd.ofd] [-t tag] [-w size] [file ...]\n"
+ (void)fprintf(stderr, "%s", USAGE);
+#undef USAGE
+}
+
+/*
+ * ip_send --
+ * Construct and send an IP buffer.
+ */
+static int
+ip_send(sp, fmt, ipbp)
+ SCR *sp;
+ char *fmt;
+ IP_BUF *ipbp;
+{
+ IP_PRIVATE *ipp;
+ size_t blen, off;
+ u_int32_t ilen;
+ int nlen, n, nw, rval;
+ char *bp, *p;
+
+ ipp = IPP(sp);
+
+ GET_SPACE_RET(sp, bp, blen, 128);
+
+ p = bp;
+ nlen = 0;
+ *p++ = ipbp->code;
+ nlen += IPO_CODE_LEN;
+
+ if (fmt != NULL)
+ for (; *fmt != '\0'; ++fmt)
+ switch (*fmt) {
+ case '1': /* Value 1. */
+ ilen = htonl(ipbp->val1);
+ goto value;
+ case '2': /* Value 2. */
+ ilen = htonl(ipbp->val2);
+value: nlen += IPO_INT_LEN;
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, nlen);
+ p = bp + off;
+ memmove(p, &ilen, IPO_INT_LEN);
+ p += IPO_INT_LEN;
+ break;
+ case 's': /* String. */
+ ilen = ipbp->len; /* XXX: conversion. */
+ ilen = htonl(ilen);
+ nlen += IPO_INT_LEN + ipbp->len;
+ off = p - bp;
+ ADD_SPACE_RET(sp, bp, blen, nlen);
+ p = bp + off;
+ memmove(p, &ilen, IPO_INT_LEN);
+ p += IPO_INT_LEN;
+ memmove(p, ipbp->str, ipbp->len);
+ p += ipbp->len;
+ break;
+ }
+
+
+ rval = 0;
+ for (n = p - bp, p = bp; n > 0; n -= nw, p += nw)
+ if ((nw = write(ipp->o_fd, p, n)) < 0) {
+ rval = 1;
+ break;
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ return (rval);
+}
diff --git a/contrib/nvi/ip/ip_main.c b/contrib/nvi/ip/ip_main.c
new file mode 100644
index 000000000000..7ca9f55024fd
--- /dev/null
+++ b/contrib/nvi/ip/ip_main.c
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_main.c 8.3 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "ip.h"
+
+static void ip_func_std __P((GS *));
+static IP_PRIVATE *ip_init __P((GS *, char *));
+static void perr __P((char *, char *));
+
+/*
+ * main --
+ * This is the main loop for the vi-as-library editor.
+ */
+int
+ip_main(argc, argv, gp, ip_arg)
+ int argc;
+ char *argv[], *ip_arg;
+ GS *gp;
+{
+ EVENT ev;
+ IP_PRIVATE *ipp;
+ IP_BUF ipb;
+ int rval;
+
+ /* Create and partially initialize the IP structure. */
+ if ((ipp = ip_init(gp, ip_arg)) == NULL)
+ return (1);
+
+ /* Add the terminal type to the global structure. */
+ if ((OG_D_STR(gp, GO_TERM) =
+ OG_STR(gp, GO_TERM) = strdup("ip_curses")) == NULL)
+ perr(gp->progname, NULL);
+
+ /*
+ * Figure out how big the screen is -- read events until we get
+ * the rows and columns.
+ */
+ do {
+ if (ip_event(NULL, &ev, 0, 0))
+ return (1);
+ } while (ev.e_event != E_EOF && ev.e_event != E_ERR &&
+ ev.e_event != E_QUIT && ev.e_event != E_WRESIZE &&
+ ev.e_event != E_SIGHUP && ev.e_event != E_SIGTERM);
+ if (ev.e_event != E_WRESIZE)
+ return (1);
+
+ /* Run ex/vi. */
+ rval = editor(gp, argc, argv);
+
+ /* Clean up the screen. */
+ (void)ip_quit(gp);
+
+ /* Free the global and IP private areas. */
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ free(ipp);
+ free(gp);
+#endif
+
+ return (rval);
+}
+
+/*
+ * ip_init --
+ * Create and partially initialize the GS structure.
+ */
+static IP_PRIVATE *
+ip_init(gp, ip_arg)
+ GS *gp;
+ char *ip_arg;
+{
+ IP_PRIVATE *ipp;
+ char *ep;
+
+ /* Allocate the IP private structure. */
+ CALLOC_NOMSG(NULL, ipp, IP_PRIVATE *, 1, sizeof(IP_PRIVATE));
+ if (ipp == NULL)
+ perr(gp->progname, NULL);
+ gp->ip_private = ipp;
+
+ /*
+ * Crack ip_arg -- it's of the form #.#, where the first number is the
+ * file descriptor from the screen, the second is the file descriptor
+ * to the screen.
+ */
+ if (!isdigit(ip_arg[0]))
+ goto usage;
+ ipp->i_fd = strtol(ip_arg, &ep, 10);
+ if (ep[0] != '.' || !isdigit(ep[1]))
+ goto usage;
+ ipp->o_fd = strtol(++ep, &ep, 10);
+ if (ep[0] != '\0') {
+usage: ip_usage();
+ return (NULL);
+ }
+
+ /* Initialize the list of ip functions. */
+ ip_func_std(gp);
+
+ return (ipp);
+}
+
+/*
+ * ip_func_std --
+ * Initialize the standard ip functions.
+ */
+static void
+ip_func_std(gp)
+ GS *gp;
+{
+ gp->scr_addstr = ip_addstr;
+ gp->scr_attr = ip_attr;
+ gp->scr_baud = ip_baud;
+ gp->scr_bell = ip_bell;
+ gp->scr_busy = ip_busy;
+ gp->scr_clrtoeol = ip_clrtoeol;
+ gp->scr_cursor = ip_cursor;
+ gp->scr_deleteln = ip_deleteln;
+ gp->scr_event = ip_event;
+ gp->scr_ex_adjust = ip_ex_adjust;
+ gp->scr_fmap = ip_fmap;
+ gp->scr_insertln = ip_insertln;
+ gp->scr_keyval = ip_keyval;
+ gp->scr_move = ip_move;
+ gp->scr_msg = NULL;
+ gp->scr_optchange = ip_optchange;
+ gp->scr_refresh = ip_refresh;
+ gp->scr_rename = ip_rename;
+ gp->scr_screen = ip_screen;
+ gp->scr_suspend = ip_suspend;
+ gp->scr_usage = ip_usage;
+}
+
+/*
+ * perr --
+ * Print system error.
+ */
+static void
+perr(name, msg)
+ char *name, *msg;
+{
+ (void)fprintf(stderr, "%s:", name);
+ if (msg != NULL)
+ (void)fprintf(stderr, "%s:", msg);
+ (void)fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+}
diff --git a/contrib/nvi/ip/ip_read.c b/contrib/nvi/ip/ip_read.c
new file mode 100644
index 000000000000..9fd977774173
--- /dev/null
+++ b/contrib/nvi/ip/ip_read.c
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_read.c 8.3 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <stdio.h>
+#include <termios.h>
+#include <time.h>
+
+#include "../common/common.h"
+#include "../ex/script.h"
+#include "ip.h"
+
+extern GS *__global_list;
+
+typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_TIMEOUT } input_t;
+
+static input_t ip_read __P((SCR *, IP_PRIVATE *, struct timeval *));
+static int ip_resize __P((SCR *, size_t, size_t));
+static int ip_trans __P((SCR *, IP_PRIVATE *, EVENT *));
+
+/*
+ * ip_event --
+ * Return a single event.
+ *
+ * PUBLIC: int ip_event __P((SCR *, EVENT *, u_int32_t, int));
+ */
+int
+ip_event(sp, evp, flags, ms)
+ SCR *sp;
+ EVENT *evp;
+ u_int32_t flags;
+ int ms;
+{
+ IP_PRIVATE *ipp;
+ struct timeval t, *tp;
+
+ if (LF_ISSET(EC_INTERRUPT)) { /* XXX */
+ evp->e_event = E_TIMEOUT;
+ return (0);
+ }
+
+ ipp = sp == NULL ? GIPP(__global_list) : IPP(sp);
+
+ /* Discard the last command. */
+ if (ipp->iskip != 0) {
+ ipp->iblen -= ipp->iskip;
+ memmove(ipp->ibuf, ipp->ibuf + ipp->iskip, ipp->iblen);
+ ipp->iskip = 0;
+ }
+
+ /* Set timer. */
+ if (ms == 0)
+ tp = NULL;
+ else {
+ t.tv_sec = ms / 1000;
+ t.tv_usec = (ms % 1000) * 1000;
+ tp = &t;
+ }
+
+ /* Read input events. */
+ for (;;) {
+ switch (ip_read(sp, ipp, tp)) {
+ case INP_OK:
+ if (!ip_trans(sp, ipp, evp))
+ continue;
+ break;
+ case INP_EOF:
+ evp->e_event = E_EOF;
+ break;
+ case INP_ERR:
+ evp->e_event = E_ERR;
+ break;
+ case INP_TIMEOUT:
+ evp->e_event = E_TIMEOUT;
+ break;
+ default:
+ abort();
+ }
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ip_read --
+ * Read characters from the input.
+ */
+static input_t
+ip_read(sp, ipp, tp)
+ SCR *sp;
+ IP_PRIVATE *ipp;
+ struct timeval *tp;
+{
+ struct timeval poll;
+ GS *gp;
+ SCR *tsp;
+ fd_set rdfd;
+ input_t rval;
+ size_t blen;
+ int maxfd, nr;
+ char *bp;
+
+ gp = sp == NULL ? __global_list : sp->gp;
+ bp = ipp->ibuf + ipp->iblen;
+ blen = sizeof(ipp->ibuf) - ipp->iblen;
+
+ /*
+ * 1: A read with an associated timeout, e.g., trying to complete
+ * a map sequence. If input exists, we fall into #2.
+ */
+ FD_ZERO(&rdfd);
+ poll.tv_sec = 0;
+ poll.tv_usec = 0;
+ if (tp != NULL) {
+ FD_SET(ipp->i_fd, &rdfd);
+ switch (select(ipp->i_fd + 1,
+ &rdfd, NULL, NULL, tp == NULL ? &poll : tp)) {
+ case 0:
+ return (INP_TIMEOUT);
+ case -1:
+ goto err;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * 2: Wait for input.
+ *
+ * Select on the command input and scripting window file descriptors.
+ * It's ugly that we wait on scripting file descriptors here, but it's
+ * the only way to keep from locking out scripting windows.
+ */
+ if (sp != NULL && F_ISSET(gp, G_SCRWIN)) {
+loop: FD_ZERO(&rdfd);
+ FD_SET(ipp->i_fd, &rdfd);
+ maxfd = ipp->i_fd;
+ for (tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (F_ISSET(sp, SC_SCRIPT)) {
+ FD_SET(sp->script->sh_master, &rdfd);
+ if (sp->script->sh_master > maxfd)
+ maxfd = sp->script->sh_master;
+ }
+ switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
+ case 0:
+ abort();
+ case -1:
+ goto err;
+ default:
+ break;
+ }
+ if (!FD_ISSET(ipp->i_fd, &rdfd)) {
+ if (sscr_input(sp))
+ return (INP_ERR);
+ goto loop;
+ }
+ }
+
+ /*
+ * 3: Read the input.
+ */
+ switch (nr = read(ipp->i_fd, bp, blen)) {
+ case 0: /* EOF. */
+ rval = INP_EOF;
+ break;
+ case -1: /* Error or interrupt. */
+err: rval = INP_ERR;
+ msgq(sp, M_SYSERR, "input");
+ break;
+ default: /* Input characters. */
+ ipp->iblen += nr;
+ rval = INP_OK;
+ break;
+ }
+ return (rval);
+}
+
+/*
+ * ip_trans --
+ * Translate messages into events.
+ */
+static int
+ip_trans(sp, ipp, evp)
+ SCR *sp;
+ IP_PRIVATE *ipp;
+ EVENT *evp;
+{
+ u_int32_t val1, val2;
+
+ switch (ipp->ibuf[0]) {
+ case IPO_EOF:
+ evp->e_event = E_EOF;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_ERR:
+ evp->e_event = E_ERR;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_INTERRUPT:
+ evp->e_event = E_INTERRUPT;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_QUIT:
+ evp->e_event = E_QUIT;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_RESIZE:
+ if (ipp->iblen < IPO_CODE_LEN + IPO_INT_LEN * 2)
+ return (0);
+ evp->e_event = E_WRESIZE;
+ memcpy(&val1, ipp->ibuf + IPO_CODE_LEN, IPO_INT_LEN);
+ val1 = ntohl(val1);
+ memcpy(&val2,
+ ipp->ibuf + IPO_CODE_LEN + IPO_INT_LEN, IPO_INT_LEN);
+ val2 = ntohl(val2);
+ ip_resize(sp, val1, val2);
+ ipp->iskip = IPO_CODE_LEN + IPO_INT_LEN * 2;
+ return (1);
+ case IPO_SIGHUP:
+ evp->e_event = E_SIGHUP;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_SIGTERM:
+ evp->e_event = E_SIGTERM;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ case IPO_STRING:
+ evp->e_event = E_STRING;
+string: if (ipp->iblen < IPO_CODE_LEN + IPO_INT_LEN)
+ return (0);
+ memcpy(&val1, ipp->ibuf + IPO_CODE_LEN, IPO_INT_LEN);
+ val1 = ntohl(val1);
+ if (ipp->iblen < IPO_CODE_LEN + IPO_INT_LEN + val1)
+ return (0);
+ ipp->iskip = IPO_CODE_LEN + IPO_INT_LEN + val1;
+ evp->e_csp = ipp->ibuf + IPO_CODE_LEN + IPO_INT_LEN;
+ evp->e_len = val1;
+ return (1);
+ case IPO_WRITE:
+ evp->e_event = E_WRITE;
+ ipp->iskip = IPO_CODE_LEN;
+ return (1);
+ default:
+ /*
+ * XXX: Protocol is out of sync?
+ */
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * ip_resize --
+ * Reset the options for a resize event.
+ */
+static int
+ip_resize(sp, lines, columns)
+ SCR *sp;
+ size_t lines, columns;
+{
+ GS *gp;
+ ARGS *argv[2], a, b;
+ char b1[1024];
+
+ /*
+ * XXX
+ * The IP screen has to know the lines and columns before anything
+ * else happens. So, we may not have a valid SCR pointer, and we
+ * have to deal with that.
+ */
+ if (sp == NULL) {
+ gp = __global_list;
+ OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = lines;
+ OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = columns;
+ return (0);
+ }
+
+ a.bp = b1;
+ b.bp = NULL;
+ a.len = b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+ (void)snprintf(b1, sizeof(b1), "lines=%lu", (u_long)lines);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ (void)snprintf(b1, sizeof(b1), "columns=%lu", (u_long)columns);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ return (0);
+}
diff --git a/contrib/nvi/ip/ip_screen.c b/contrib/nvi/ip/ip_screen.c
new file mode 100644
index 000000000000..71578e0d1b39
--- /dev/null
+++ b/contrib/nvi/ip/ip_screen.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_screen.c 8.2 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "ip.h"
+
+/*
+ * ip_screen --
+ * Initialize/shutdown the IP screen.
+ *
+ * PUBLIC: int ip_screen __P((SCR *, u_int32_t));
+ */
+int
+ip_screen(sp, flags)
+ SCR *sp;
+ u_int32_t flags;
+{
+ GS *gp;
+ IP_PRIVATE *ipp;
+
+ gp = sp->gp;
+ ipp = IPP(sp);
+
+ /* See if the current information is incorrect. */
+ if (F_ISSET(gp, G_SRESTART)) {
+ if (ip_quit(gp))
+ return (1);
+ F_CLR(gp, G_SRESTART);
+ }
+
+ /* See if we're already in the right mode. */
+ if (LF_ISSET(SC_VI) && F_ISSET(ipp, IP_SCR_VI_INIT))
+ return (0);
+
+ /* Ex isn't possible. */
+ if (LF_ISSET(SC_EX))
+ return (1);
+
+ /* Initialize terminal based information. */
+ if (ip_term_init(sp))
+ return (1);
+
+ /* Put up the first file name. */
+ if (ip_rename(sp))
+ return (1);
+
+ F_SET(ipp, IP_SCR_VI_INIT);
+ return (0);
+}
+
+/*
+ * ip_quit --
+ * Shutdown the screens.
+ *
+ * PUBLIC: int ip_quit __P((GS *));
+ */
+int
+ip_quit(gp)
+ GS *gp;
+{
+ IP_PRIVATE *ipp;
+ int rval;
+
+ /* Clean up the terminal mappings. */
+ rval = ip_term_end(gp);
+
+ ipp = GIPP(gp);
+ F_CLR(ipp, IP_SCR_VI_INIT);
+
+ return (rval);
+}
diff --git a/contrib/nvi/ip/ip_term.c b/contrib/nvi/ip/ip_term.c
new file mode 100644
index 000000000000..28e686dddd28
--- /dev/null
+++ b/contrib/nvi/ip/ip_term.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_term.c 8.2 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "ip.h"
+
+/*
+ * ip_term_init --
+ * Initialize the terminal special keys.
+ *
+ * PUBLIC: int ip_term_init __P((SCR *));
+ */
+int
+ip_term_init(sp)
+ SCR *sp;
+{
+ SEQ *qp;
+
+ /*
+ * Rework any function key mappings that were set before the
+ * screen was initialized.
+ */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next)
+ if (F_ISSET(qp, SEQ_FUNCMAP))
+ (void)ip_fmap(sp, qp->stype,
+ qp->input, qp->ilen, qp->output, qp->olen);
+ return (0);
+}
+
+/*
+ * ip_term_end --
+ * End the special keys defined by the termcap/terminfo entry.
+ *
+ * PUBLIC: int ip_term_end __P((GS *));
+ */
+int
+ip_term_end(gp)
+ GS *gp;
+{
+ SEQ *qp, *nqp;
+
+ /* Delete screen specific mappings. */
+ for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) {
+ nqp = qp->q.le_next;
+ if (F_ISSET(qp, SEQ_SCREEN))
+ (void)seq_mdel(qp);
+ }
+ return (0);
+}
+
+/*
+ * ip_fmap --
+ * Map a function key.
+ *
+ * PUBLIC: int ip_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+ */
+int
+ip_fmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ seq_t stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ /* Bind a function key to a string sequence. */
+ return (1);
+}
+
+/*
+ * ip_optchange --
+ * IP screen specific "option changed" routine.
+ *
+ * PUBLIC: int ip_optchange __P((SCR *, int, char *, u_long *));
+ */
+int
+ip_optchange(sp, opt, str, valp)
+ SCR *sp;
+ int opt;
+ char *str;
+ u_long *valp;
+{
+ switch (opt) {
+ case O_COLUMNS:
+ case O_LINES:
+ F_SET(sp->gp, G_SRESTART);
+ F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
+ break;
+ case O_TERM:
+ msgq(sp, M_ERR, "The screen type may not be changed");
+ return (1);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/ip_cl/Makefile b/contrib/nvi/ip_cl/Makefile
new file mode 100644
index 000000000000..9503c4dac501
--- /dev/null
+++ b/contrib/nvi/ip_cl/Makefile
@@ -0,0 +1,20 @@
+# TR turns on tracing, to the specified file.
+TR= -DTR=\"/dev/ttypa\"
+#TR= -DTR=\"__log\"
+
+# VI is the binary that ip_cl runs.
+VI= -DVI=\"../build.local/nvi\"
+
+DEBUG= -DDEBUG -g
+INC= -I. -I../build.local -I../include
+CFLAGS= $(DEBUG) $(TR) $(VI) $(INC)
+
+OBJS= ip_cl.o
+
+LIBS= -lcurses -ltermcap
+
+ip_cl: ${OBJS}
+ ${CC} ${OBJS} -o $@ ${LIBS}
+
+clean:
+ rm -f ip_cl ${OBJS}
diff --git a/contrib/nvi/ip_cl/ip_cl.c b/contrib/nvi/ip_cl/ip_cl.c
new file mode 100644
index 000000000000..5137b3f56abc
--- /dev/null
+++ b/contrib/nvi/ip_cl/ip_cl.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)ip_cl.c 8.4 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/select.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../ip/ip.h"
+#include "pathnames.h"
+
+size_t cols, rows; /* Screen columns, rows. */
+int die; /* Child died. */
+int i_fd, o_fd; /* Input/output fd's. */
+int resize; /* Window resized. */
+
+void arg_format __P((int *, char **[], int, int));
+void attach __P((void));
+void ip_cur_end __P((void));
+void ip_cur_init __P((void));
+void ip_read __P((void));
+void ip_resize __P((void));
+int ip_send __P((char *, IP_BUF *));
+void ip_siginit __P((void));
+int ip_trans __P((char *, size_t, size_t *));
+void nomem __P((void));
+void onchld __P((int));
+void onintr __P((int));
+void onwinch __P((int));
+void trace __P((const char *, ...));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ fd_set fdset;
+ pid_t pid;
+ size_t blen, len, skip;
+ int ch, nr, rpipe[2], wpipe[2];
+ char *bp;
+
+ while ((ch = getopt(argc, argv, "D")) != EOF)
+ switch (ch) {
+ case 'D':
+ attach();
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Open the communications pipes. The pipes are named from our
+ * viewpoint, so we read from rpipe[0] and write to wpipe[1].
+ * Vi reads from wpipe[0], and writes to rpipe[1].
+ */
+ if (pipe(rpipe) == -1 || pipe(wpipe) == -1) {
+ perror("ip_cl: pipe");
+ exit (1);
+ }
+ i_fd = rpipe[0];
+ o_fd = wpipe[1];
+
+ /*
+ * Format our arguments, adding a -I to the list. The first file
+ * descriptor to the -I argument is vi's input, and the second is
+ * vi's output.
+ */
+ arg_format(&argc, &argv, wpipe[0], rpipe[1]);
+
+ /* Run vi. */
+ switch (pid = fork()) {
+ case -1: /* Error. */
+ perror("ip_cl: fork");
+ exit (1);
+ case 0: /* Vi. */
+ execv(VI, argv);
+ perror("ip_cl: execv ../build/nvi");
+ exit (1);
+ default: /* Ip_cl. */
+ break;
+ }
+
+ /*
+ * Allocate initial input buffer.
+ * XXX
+ * We don't dynamically resize, so there better not be any individual
+ * messages larger than this buffer.
+ */
+ blen = 4 * 1024;
+ if ((bp = malloc(blen)) == NULL)
+ nomem();
+
+ /* Clear the file descriptor mask. */
+ FD_ZERO(&fdset);
+
+ /* Initialize signals. */
+ ip_siginit();
+
+ /* Initialize the curses screen. */
+ ip_cur_init();
+
+ /* The first thing vi wants is the screen size. */
+ ip_resize();
+
+ /* Main loop. */
+ for (len = 0;;) {
+ if (die)
+ break;
+ /*
+ * XXX
+ * Race #1: if there's an event coming from vi that requires
+ * that we know what size the screen is, and we take a resize
+ * signal, we'll differ from vi in the size of the screen for
+ * that event. Fixing this will requires information attached
+ * to message as to what set of state was in place when the
+ * message was sent. Not hard, but not worth doing now.
+ *
+ * Race #2: we cycle, handling resize events until there aren't
+ * any waiting. We then do a select. If the resize signal
+ * arrives after we exit the loop but before we enter select,
+ * we'll wait on the user to enter a keystroke, handle it and
+ * then handle the resize.
+ */
+ while (resize) {
+ resize = 0;
+ ip_resize();
+ ip_cur_end();
+ ip_cur_init();
+ }
+
+ /* Wait until vi or the screen wants to talk. */
+ FD_SET(i_fd, &fdset);
+ FD_SET(STDIN_FILENO, &fdset);
+ errno = 0;
+ switch (select(i_fd + 1, &fdset, NULL, NULL, NULL)) {
+ case 0:
+ abort(); /* Timeout. */
+ /* NOTREACHED */
+ case -1:
+ if (errno == EINTR)
+ continue;
+ perror("ip_cl: select");
+ exit (1);
+ default:
+ break;
+ }
+
+ /* Read waiting tty characters and send them to vi. */
+ if (FD_ISSET(STDIN_FILENO, &fdset)) {
+ ip_read();
+ continue;
+ }
+
+ /* Read waiting vi messages and translate to curses calls. */
+ switch (nr = read(i_fd, bp + len, blen - len)) {
+ case 0:
+ continue;
+ case -1:
+ perror("ip_cl: read");
+ exit (1);
+ default:
+ break;
+ }
+
+ /* Parse to data end or partial message. */
+ for (len += nr, skip = 0; len > skip &&
+ ip_trans(bp + skip, len - skip, &skip) == 1;);
+
+ /* Copy any partial messages down in the buffer. */
+ len -= skip;
+ if (len > 0)
+ memmove(bp, bp + skip, len);
+ }
+
+ /* End the screen. */
+ ip_cur_end();
+
+ exit (0);
+}
+
+/*
+ * ip_read --
+ * Read characters from the screen and send them to vi.
+ */
+void
+ip_read()
+{
+ IP_BUF ipb;
+ int nr;
+ char bp[1024];
+
+ /* Read waiting tty characters. */
+ switch (nr = read(STDIN_FILENO, bp, sizeof(bp))) {
+ case 0:
+ return;
+ case -1:
+ perror("ip_cl: read");
+ exit (1);
+ default:
+ break;
+ }
+
+ ipb.code = IPO_STRING;
+ ipb.len = nr;
+ ipb.str = bp;
+ ip_send("s", &ipb);
+}
+
+/*
+ * ip_trans --
+ * Translate vi messages into curses calls.
+ */
+int
+ip_trans(bp, len, skipp)
+ char *bp;
+ size_t len, *skipp;
+{
+ IP_BUF ipb;
+ size_t cno, lno, nlen, oldy, oldx, spcnt;
+ int ch;
+ char *fmt, *p;
+
+ switch (bp[0]) {
+ case IPO_ADDSTR:
+ case IPO_RENAME:
+ fmt = "s";
+ break;
+ case IPO_BUSY:
+ fmt = "s1";
+ break;
+ case IPO_ATTRIBUTE:
+ case IPO_MOVE:
+ fmt = "12";
+ break;
+ case IPO_REWRITE:
+ fmt = "1";
+ break;
+ default:
+ fmt = "";
+ }
+
+ nlen = IPO_CODE_LEN;
+ p = bp + IPO_CODE_LEN;
+ for (; *fmt != '\0'; ++fmt)
+ switch (*fmt) {
+ case '1':
+ nlen += IPO_INT_LEN;
+ if (len < nlen)
+ return (0);
+ memcpy(&ipb.val1, p, IPO_INT_LEN);
+ ipb.val1 = ntohl(ipb.val1);
+ p += IPO_INT_LEN;
+ break;
+ case '2':
+ nlen += IPO_INT_LEN;
+ if (len < nlen)
+ return (0);
+ memcpy(&ipb.val2, p, IPO_INT_LEN);
+ ipb.val2 = ntohl(ipb.val2);
+ p += IPO_INT_LEN;
+ break;
+ case 's':
+ nlen += IPO_INT_LEN;
+ if (len < nlen)
+ return (0);
+ memcpy(&ipb.len, p, IPO_INT_LEN);
+ ipb.len = ntohl(ipb.len);
+ p += IPO_INT_LEN;
+ nlen += ipb.len;
+ if (len < nlen)
+ return (0);
+ ipb.str = p;
+ p += ipb.len;
+ break;
+ }
+ *skipp += nlen;
+
+ switch (bp[0]) {
+ case IPO_ADDSTR:
+#ifdef TR
+ trace("addnstr {%.*s}\n", (int)ipb.len, ipb.str);
+#endif
+ (void)addnstr(ipb.str, ipb.len);
+ break;
+ case IPO_ATTRIBUTE:
+ switch (ipb.val1) {
+ case SA_ALTERNATE:
+#ifdef TR
+ trace("attr: alternate\n");
+#endif
+ /*
+ * XXX
+ * Nothing.
+ */
+ break;
+ case SA_INVERSE:
+#ifdef TR
+ trace("attr: inverse\n");
+#endif
+ if (ipb.val2)
+ (void)standout();
+ else
+ (void)standend();
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ break;
+ case IPO_BELL:
+#ifdef TR
+ trace("bell\n");
+#endif
+ (void)write(1, "\007", 1); /* '\a' */
+ break;
+ case IPO_BUSY:
+#ifdef TR
+ trace("busy {%.*s}\n", (int)ipb.len, ipb.str);
+#endif
+ /*
+ * XXX
+ * Nothing...
+ * ip_busy(ipb.str, ipb.len);
+ */
+ break;
+ case IPO_CLRTOEOL:
+#ifdef TR
+ trace("clrtoeol\n");
+#endif
+ clrtoeol();
+ break;
+ case IPO_DELETELN:
+#ifdef TR
+ trace("deleteln\n");
+#endif
+ deleteln();
+ break;
+ case IPO_INSERTLN:
+#ifdef TR
+ trace("insertln\n");
+#endif
+ insertln();
+ break;
+ case IPO_MOVE:
+#ifdef TR
+ trace("move: %lu %lu\n", (u_long)ipb.val1, (u_long)ipb.val2);
+#endif
+ (void)move(ipb.val1, ipb.val2);
+ break;
+ case IPO_REDRAW:
+#ifdef TR
+ trace("redraw\n");
+#endif
+ clearok(curscr, 1);
+ refresh();
+ break;
+ case IPO_REFRESH:
+#ifdef TR
+ trace("refresh\n");
+#endif
+ refresh();
+ break;
+ case IPO_RENAME:
+#ifdef TR
+ trace("rename {%.*s}\n", (int)ipb.len, ipb.str);
+#endif
+ /*
+ * XXX
+ * Nothing...
+ * ip_rename(ipb.str, ipb.len);
+ */
+ break;
+ case IPO_REWRITE:
+#ifdef TR
+ trace("rewrite {%lu}\n", (u_long)ipb.val1);
+#endif
+ getyx(stdscr, oldy, oldx);
+ for (lno = ipb.val1, cno = spcnt = 0;;) {
+ (void)move(lno, cno);
+ ch = winch(stdscr);
+ if (isblank(ch))
+ ++spcnt;
+ else {
+ (void)move(lno, cno - spcnt);
+ for (; spcnt > 0; --spcnt)
+ (void)addch(' ');
+ (void)addch(ch);
+ }
+ if (++cno >= cols)
+ break;
+ }
+ (void)move(oldy, oldx);
+ break;
+ default:
+ /*
+ * XXX: Protocol is out of sync?
+ */
+ abort();
+ }
+
+ return (1);
+}
+
+/*
+ * arg_format
+ */
+void
+arg_format(argcp, argvp, i_fd, o_fd)
+ int *argcp, i_fd, o_fd;
+ char **argvp[];
+{
+ char **largv, *iarg, *p;
+
+ /* Get space for the argument array and the -I argument. */
+ if ((iarg = malloc(64)) == NULL ||
+ (largv = malloc((*argcp + 3) * sizeof(char *))) == NULL) {
+ perror("ip_cl");
+ exit (1);
+ }
+ memcpy(largv + 2, *argvp, *argcp * sizeof(char *) + 1);
+
+ /* Reset argv[0] to be the exec'd program. */
+ if ((p = strrchr(VI, '/')) == NULL)
+ largv[0] = VI;
+ else
+ largv[0] = p + 1;
+
+ /* Create the -I argument. */
+ (void)sprintf(iarg, "-I%d%s%d", i_fd, ".", o_fd);
+ largv[1] = iarg;
+
+ /* Reset the argument array. */
+ *argvp = largv;
+}
+
+/*
+ * ip_cur_init --
+ * Initialize the curses screen.
+ */
+void
+ip_cur_init()
+{
+ /*
+ * XXX
+ * This is 4BSD curses' specific -- if this is to be a real program
+ * we'll have to do all the stuff that we do in the cl directory to
+ * run with different curses variants.
+ */
+ if (initscr() == ERR) {
+ perror("ip_cl: initscr");
+ exit (1);
+ }
+ noecho();
+ nonl();
+ raw();
+ idlok(stdscr, 1);
+}
+
+/*
+ * ip_cur_end --
+ * End the curses screen.
+ */
+void
+ip_cur_end()
+{
+ (void)move(0, 0);
+ (void)deleteln();
+ (void)move(rows - 1, 0);
+ (void)refresh();
+ (void)endwin();
+}
+
+/*
+ * ip_siginit --
+ * Initialize the signals.
+ */
+void
+ip_siginit()
+{
+ /* We need to know if vi dies horribly. */
+ (void)signal(SIGCHLD, onchld);
+
+ /* We want to allow interruption at least for now. */
+ (void)signal(SIGINT, onintr);
+
+#ifdef SIGWINCH
+ /* We need to know if the screen is resized. */
+ (void)signal(SIGWINCH, onwinch);
+#endif
+}
+
+/*
+ * ip_resize --
+ * Send the window size.
+ */
+void
+ip_resize()
+{
+ struct winsize win;
+ IP_BUF ipb;
+
+ if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) == -1) {
+ perror("ip_cl: TIOCGWINSZ");
+ exit(1);
+ }
+
+ if (rows == win.ws_row && cols == win.ws_col)
+ return;
+
+ ipb.val1 = rows = win.ws_row;
+ ipb.val2 = cols = win.ws_col;
+ ipb.code = IPO_RESIZE;
+ ip_send("12", &ipb);
+}
+
+/*
+ * ip_send --
+ * Construct and send an IP buffer.
+ */
+int
+ip_send(fmt, ipbp)
+ char *fmt;
+ IP_BUF *ipbp;
+{
+ static char *bp;
+ static size_t blen;
+ size_t off;
+ u_int32_t ilen;
+ int nlen, n, nw;
+ char *p;
+
+ if (blen == 0 && (bp = malloc(blen = 512)) == NULL)
+ nomem();
+
+ p = bp;
+ nlen = 0;
+ *p++ = ipbp->code;
+ nlen += IPO_CODE_LEN;
+
+ if (fmt != NULL)
+ for (; *fmt != '\0'; ++fmt)
+ switch (*fmt) {
+ case '1': /* Value 1. */
+ ilen = htonl(ipbp->val1);
+ goto value;
+ case '2': /* Value 2. */
+ ilen = htonl(ipbp->val2);
+value: nlen += IPO_INT_LEN;
+ if (nlen >= blen) {
+ blen = blen * 2 + nlen;
+ off = p - bp;
+ if ((bp = realloc(bp, blen)) == NULL)
+ nomem();
+ p = bp + off;
+ }
+ memmove(p, &ilen, IPO_INT_LEN);
+ p += IPO_INT_LEN;
+ break;
+ case 's': /* String. */
+ ilen = ipbp->len; /* XXX: conversion. */
+ ilen = htonl(ilen);
+ nlen += IPO_INT_LEN + ipbp->len;
+ if (nlen >= blen) {
+ blen = blen * 2 + nlen;
+ off = p - bp;
+ if ((bp = realloc(bp, blen)) == NULL)
+ nomem();
+ p = bp + off;
+ }
+ memmove(p, &ilen, IPO_INT_LEN);
+ p += IPO_INT_LEN;
+ memmove(p, ipbp->str, ipbp->len);
+ p += ipbp->len;
+ break;
+ }
+#ifdef TR
+ trace("WROTE: ");
+ for (n = p - bp, p = bp; n > 0; --n, ++p)
+ if (isprint(*p))
+ (void)trace("%c", *p);
+ else
+ trace("<%x>", (u_char)*p);
+ trace("\n");
+#endif
+
+ for (n = p - bp, p = bp; n > 0; n -= nw, p += nw)
+ if ((nw = write(o_fd, p, n)) < 0) {
+ perror("ip_cl: write");
+ exit(1);
+ }
+
+ return (0);
+}
+
+void
+nomem()
+{
+ perror("ip_cl");
+ exit (1);
+}
+
+/*
+ * onchld --
+ * Handle SIGCHLD.
+ */
+void
+onchld(signo)
+ int signo;
+{
+ die = 1;
+
+#ifdef TR
+ trace("SIGCHLD\n");
+#endif
+
+ /* Interrupt select if it's running. */
+ (void)kill(getpid(), SIGINT);
+}
+
+/*
+ * onintr --
+ * Handle SIGINT.
+ */
+void
+onintr(signo)
+ int signo;
+{
+ /*
+ * If we receive an interrupt, we may have sent it ourselves.
+ * If not, die from the signal.
+ */
+ if (die)
+ return;
+ (void)signal(SIGINT, SIG_DFL);
+ kill(getpid(), SIGINT);
+}
+
+/*
+ * onwinch --
+ * Handle SIGWINCH.
+ */
+void
+onwinch(signo)
+ int signo;
+{
+ resize = 1;
+}
+
+void
+attach()
+{
+ int fd;
+ char ch;
+
+ (void)printf("process %lu waiting, enter <CR> to continue: ",
+ (u_long)getpid());
+ (void)fflush(stdout);
+
+ if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
+ perror(_PATH_TTY);
+ exit (1);;
+ }
+ do {
+ if (read(fd, &ch, 1) != 1) {
+ (void)close(fd);
+ return;
+ }
+ } while (ch != '\n' && ch != '\r');
+ (void)close(fd);
+}
+
+#ifdef TR
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * TR --
+ * debugging trace routine.
+ */
+void
+#ifdef __STDC__
+trace(const char *fmt, ...)
+#else
+trace(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static FILE *tfp;
+ va_list ap;
+
+ if (tfp == NULL && (tfp = fopen(TR, "w")) == NULL)
+ tfp = stderr;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(tfp, fmt, ap);
+ va_end(ap);
+
+ (void)fflush(tfp);
+}
+#endif
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: ip_cl [-D]\n");
+ exit(1);
+}
diff --git a/contrib/nvi/perl_api/VI.pod b/contrib/nvi/perl_api/VI.pod
new file mode 100644
index 000000000000..a87e24d911d8
--- /dev/null
+++ b/contrib/nvi/perl_api/VI.pod
@@ -0,0 +1,218 @@
+=head1 NAME
+
+VI - VI module within perl embedded nvi
+
+=head1 SYNOPSIS
+
+ sub wc {
+ my $words;
+ $i = $VI::StartLine;
+ while ($i <= $VI::StopLine) {
+ $_ = VI::GetLine($VI::ScreenId, $i++);
+ $words+=split;
+ }
+ VI::Msg($VI::ScreenId,"$words words");
+ }
+
+=head1 DESCRIPTION
+
+This pseudo module is available to perl programs run from within nvi and
+provides access to the files being edited and some internal data.
+
+Beware that you should not use this module from within a C<perldo> or
+from within an C<END> block or a C<DESTROY> method.
+
+=head2 Variables
+
+These are set by nvi before starting each perl command.
+
+=over 8
+
+=item * $ScreenId
+
+Screen id of the current screen.
+
+=item * $StartLine
+
+Line number of the first line of the selected range or of the file if no
+range was specified.
+
+=item * $StopLine
+
+Line number of the last line of the selected range or of the file if no
+range was specified.
+
+=back
+
+=head2 Functions
+
+=over 8
+
+=item * AppendLine
+
+ VI::AppendLine(screenId,lineNumber,text);
+
+Append the string text after the line in lineNumber.
+
+=item * DelLine
+
+ VI::DelLine(screenId,lineNum);
+
+Delete lineNum.
+
+=item * EndScreen
+
+VI::EndScreen(screenId);
+
+End a screen.
+
+=item * FindScreen
+
+ VI::FindScreen(file);
+
+Return the screen id associated with file name.
+
+=item * GetCursor
+
+ ($line, $column) = VI::GetCursor(screenId);
+
+Return the current cursor position as a list with two elements.
+
+=item * GetLine
+
+ VI::GetLine(screenId,lineNumber);
+
+Return lineNumber.
+
+=item * GetMark
+
+ ($line, $column) = VI::GetMark(screenId,mark);
+
+Return the mark's cursor position as a list with two elements.
+
+=item * GetOpt
+
+ VI::GetOpt(screenId,option);
+
+Return the value of an option.
+
+=item * InsertLine
+
+ VI::InsertLine(screenId,lineNumber,text);
+
+Insert the string text before the line in lineNumber.
+
+=item * LastLine
+
+ VI::LastLine(screenId);
+
+Return the last line in the screen.
+
+=item * MapKey
+
+ VI::MapKey(screenId,key,perlproc);
+
+Associate a key with a perl procedure.
+
+=item * Msg
+
+ VI::Msg(screenId,text);
+
+Set the message line to text.
+
+=item * NewScreen
+
+ VI::NewScreen(screenId);
+ VI::NewScreen(screenId,file);
+
+Create a new screen. If a filename is specified then the screen is
+opened with that file.
+
+=item * Run
+
+ VI::Run(screenId,cmd);
+
+Run the ex command cmd.
+
+=item * SetCursor
+
+ VI::SetCursor(screenId,line,column);
+
+Set the cursor to the line and column numbers supplied.
+
+=item * SetLine
+
+ VI::SetLine(screenId,lineNumber,text);
+
+Set lineNumber to the text supplied.
+
+=item * SetMark
+
+ VI::SetMark(screenId,mark,line,column);
+
+Set the mark to the line and column numbers supplied.
+
+=item * SetOpt
+
+ VI::SetOpt(screenId,command);
+
+Set an option.
+
+=item * SwitchScreen
+
+ VI::SwitchScreen(screenId,screenId);
+
+Change the current focus to screen.
+
+=item * UnmapKey
+
+ VI::UnmmapKey(screenId,key);
+
+Unmap a key.
+
+=item * Warn
+
+This is the default warning handler.
+It adds any warnings to the error string.
+
+=back
+
+=head1 EXAMPLES
+
+ sub showmarks {
+ my ($mark, $all);
+ for $mark ('a' .. 'z') {
+ eval {VI::GetMark($VI::ScreenId, $mark)};
+ $all .= $mark unless ($@);
+ }
+ VI::Msg($VI::ScreenId,"Set marks: $all");
+ }
+
+ sub forall {
+ my ($code) = shift;
+ my ($i) = $VI::StartLine-1;
+ while (++$i <= $VI::StopLine) {
+ $_ = VI::GetLine($VI::ScreenId, $i);
+ VI::SetLine($VI::ScreenId, $i, $_) if(&$code);
+ }
+ }
+
+Now you can do
+
+ :perl forall sub{s/perlre/substitution/}
+
+Although you'll probably use
+
+ :perldo s/perlre/substitution/
+
+instead.
+
+See L<perlre> for perl regular expressions.
+
+=head1 SEE ALSO
+
+L<nviperl>
+
+=head1 AUTHOR
+
+Sven Verdoolaege <skimo@dns.ufsia.ac.be>
diff --git a/contrib/nvi/perl_api/nviperl.pod b/contrib/nvi/perl_api/nviperl.pod
new file mode 100644
index 000000000000..43850d8b6363
--- /dev/null
+++ b/contrib/nvi/perl_api/nviperl.pod
@@ -0,0 +1,43 @@
+=head1 NAME
+
+nviperl - nvi with embedded perl
+
+=head1 SYNOPSIS
+
+ :perl require 'wc.pl'
+ :perl wc
+ :,$perldo $_=reverse($_)
+
+=head1 DESCRIPTION
+
+nvi with embedded perl allows you to run perl commands from within nvi.
+Two additional commands are made available when you enable the perl
+interpreter:
+
+=over 8
+
+=item * perl cmd
+
+The perl command passes the specified commands to the perl interpreter.
+The C<$VI::ScreenId>, C<$VI::StartLine> and C<$VI::StopLine> are set.
+To find out how to maniplulate the nvi screens, see L<VI>.
+
+=item * perldo cmd
+
+The perldo command runs the specified commands on each line of the range
+(every line of the file if no range specified). Before running the
+command the line is copied into $_. If the command returns a true value
+the line is replaced by the new value of $_.
+
+The perldo commando does B<not> set the C<VI> variables. (If you think
+this is a bad idea, tell me.)
+
+=back
+
+=head1 SEE ALSO
+
+L<VI>
+
+=head1 AUTHOR
+
+Sven Verdoolaege <skimo@dns.ufsia.ac.be>
diff --git a/contrib/nvi/perl_api/perl.xs b/contrib/nvi/perl_api/perl.xs
new file mode 100644
index 000000000000..0b48cded5bfc
--- /dev/null
+++ b/contrib/nvi/perl_api/perl.xs
@@ -0,0 +1,1115 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1995
+ * George V. Neville-Neil. All rights reserved.
+ * Copyright (c) 1996
+ * Sven Verdoolaege. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)perl.xs 8.27 (Berkeley) 10/16/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#include "perl_extern.h"
+
+static void msghandler __P((SCR *, mtype_t, char *, size_t));
+
+extern GS *__global_list; /* XXX */
+
+static char *errmsg = 0;
+
+/*
+ * INITMESSAGE --
+ * Macros to point messages at the Perl message handler.
+ */
+#define INITMESSAGE \
+ scr_msg = __global_list->scr_msg; \
+ __global_list->scr_msg = msghandler;
+#define ENDMESSAGE \
+ __global_list->scr_msg = scr_msg; \
+ if (rval) croak(errmsg);
+
+static void xs_init __P((void));
+
+/*
+ * perl_end --
+ * Clean up perl interpreter
+ *
+ * PUBLIC: int perl_end __P((GS *));
+ */
+int
+perl_end(gp)
+ GS *gp;
+{
+ /*
+ * Call perl_run and perl_destuct to call END blocks and DESTROY
+ * methods.
+ */
+ if (gp->perl_interp) {
+ /*Irestartop = 0; / * XXX */
+ perl_run(gp->perl_interp);
+ perl_destruct(gp->perl_interp);
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ perl_free(gp->perl_interp);
+#endif
+ }
+}
+
+/*
+ * perl_eval
+ * Evaluate a string
+ * We don't use mortal SVs because no one will clean up after us
+ */
+static void
+perl_eval(string)
+ char *string;
+{
+#ifdef HAVE_PERL_5_003_01
+ SV* sv = newSVpv(string, 0);
+
+ perl_eval_sv(sv, G_DISCARD | G_NOARGS);
+ SvREFCNT_dec(sv);
+#else
+ char *argv[2];
+
+ argv[0] = string;
+ argv[1] = NULL;
+ perl_call_argv("_eval_", G_EVAL | G_DISCARD | G_KEEPERR, argv);
+#endif
+}
+
+/*
+ * perl_init --
+ * Create the perl commands used by nvi.
+ *
+ * PUBLIC: int perl_init __P((SCR *));
+ */
+int
+perl_init(scrp)
+ SCR *scrp;
+{
+ AV * av;
+ GS *gp;
+ char *bootargs[] = { "VI", NULL };
+#ifndef USE_SFIO
+ SV *svcurscr;
+#endif
+
+#ifndef HAVE_PERL_5_003_01
+ static char *args[] = { "", "-e", "sub _eval_ { eval $_[0] }" };
+#else
+ static char *args[] = { "", "-e", "" };
+#endif
+ STRLEN length;
+ char *file = __FILE__;
+
+ gp = scrp->gp;
+ gp->perl_interp = perl_alloc();
+ perl_construct(gp->perl_interp);
+ if (perl_parse(gp->perl_interp, xs_init, 3, args, 0)) {
+ perl_destruct(gp->perl_interp);
+ perl_free(gp->perl_interp);
+ gp->perl_interp = NULL;
+ return 1;
+ }
+ perl_call_argv("VI::bootstrap", G_DISCARD, bootargs);
+ perl_eval("$SIG{__WARN__}='VI::Warn'");
+
+ av_unshift(av = GvAVn(incgv), 1);
+ av_store(av, 0, newSVpv(_PATH_PERLSCRIPTS,
+ sizeof(_PATH_PERLSCRIPTS)-1));
+
+#ifdef USE_SFIO
+ sfdisc(PerlIO_stdout(), sfdcnewnvi(scrp));
+ sfdisc(PerlIO_stderr(), sfdcnewnvi(scrp));
+#else
+ svcurscr = perl_get_sv("curscr", TRUE);
+ sv_magic((SV *)gv_fetchpv("STDOUT",TRUE, SVt_PVIO), svcurscr,
+ 'q', Nullch, 0);
+ sv_magic((SV *)gv_fetchpv("STDERR",TRUE, SVt_PVIO), svcurscr,
+ 'q', Nullch, 0);
+#endif /* USE_SFIO */
+ return (0);
+}
+
+/*
+ * perl_screen_end
+ * Remove all refences to the screen to be destroyed
+ *
+ * PUBLIC: int perl_screen_end __P((SCR*));
+ */
+int
+perl_screen_end(scrp)
+ SCR *scrp;
+{
+ if (scrp->perl_private) {
+ sv_setiv((SV*) scrp->perl_private, 0);
+ }
+ return 0;
+}
+
+static void
+my_sighandler(i)
+ int i;
+{
+ croak("Perl command interrupted by SIGINT");
+}
+
+/* Create a new reference to an SV pointing to the SCR structure
+ * The perl_private part of the SCR structure points to the SV,
+ * so there can only be one such SV for a particular SCR structure.
+ * When the last reference has gone (DESTROY is called),
+ * perl_private is reset; When the screen goes away before
+ * all references are gone, the value of the SV is reset;
+ * any subsequent use of any of those reference will produce
+ * a warning. (see typemap)
+ */
+static SV *
+newVIrv(rv, screen)
+ SV *rv;
+ SCR *screen;
+{
+ sv_upgrade(rv, SVt_RV);
+ if (!screen->perl_private) {
+ screen->perl_private = newSV(0);
+ sv_setiv(screen->perl_private, (IV) screen);
+ }
+ else SvREFCNT_inc(screen->perl_private);
+ SvRV(rv) = screen->perl_private;
+ SvROK_on(rv);
+ return sv_bless(rv, gv_stashpv("VI", TRUE));
+}
+
+
+/*
+ * perl_ex_perl -- :[line [,line]] perl [command]
+ * Run a command through the perl interpreter.
+ *
+ * PUBLIC: int perl_ex_perl __P((SCR*, CHAR_T *, size_t, recno_t, recno_t));
+ */
+int
+perl_ex_perl(scrp, cmdp, cmdlen, f_lno, t_lno)
+ SCR *scrp;
+ CHAR_T *cmdp;
+ size_t cmdlen;
+ recno_t f_lno, t_lno;
+{
+ static SV *svcurscr = 0, *svstart, *svstop, *svid;
+ GS *gp;
+ STRLEN length;
+ size_t len;
+ char *err;
+ Signal_t (*istat)();
+
+ /* Initialize the interpreter. */
+ gp = scrp->gp;
+ if (!svcurscr) {
+ if (gp->perl_interp == NULL && perl_init(scrp))
+ return (1);
+ SvREADONLY_on(svcurscr = perl_get_sv("curscr", TRUE));
+ SvREADONLY_on(svstart = perl_get_sv("VI::StartLine", TRUE));
+ SvREADONLY_on(svstop = perl_get_sv("VI::StopLine", TRUE));
+ SvREADONLY_on(svid = perl_get_sv("VI::ScreenId", TRUE));
+ }
+
+ sv_setiv(svstart, f_lno);
+ sv_setiv(svstop, t_lno);
+ newVIrv(svcurscr, scrp);
+ /* Backwards compatibility. */
+ newVIrv(svid, scrp);
+
+ istat = signal(SIGINT, my_sighandler);
+ perl_eval(cmdp);
+ signal(SIGINT, istat);
+
+ SvREFCNT_dec(SvRV(svcurscr));
+ SvROK_off(svcurscr);
+ SvREFCNT_dec(SvRV(svid));
+ SvROK_off(svid);
+
+ err = SvPV(GvSV(errgv), length);
+ if (!length)
+ return (0);
+
+ err[length - 1] = '\0';
+ msgq(scrp, M_ERR, "perl: %s", err);
+ return (1);
+}
+
+/*
+ * replace_line
+ * replace a line with the contents of the perl variable $_
+ * lines are split at '\n's
+ * if $_ is undef, the line is deleted
+ * returns possibly adjusted linenumber
+ */
+static int
+replace_line(scrp, line, t_lno)
+ SCR *scrp;
+ recno_t line, *t_lno;
+{
+ char *str, *next;
+ size_t len;
+
+ if (SvOK(GvSV(defgv))) {
+ str = SvPV(GvSV(defgv),len);
+ next = memchr(str, '\n', len);
+ api_sline(scrp, line, str, next ? (next - str) : len);
+ while (next++) {
+ len -= next - str;
+ next = memchr(str = next, '\n', len);
+ api_iline(scrp, ++line, str, next ? (next - str) : len);
+ (*t_lno)++;
+ }
+ } else {
+ api_dline(scrp, line--);
+ (*t_lno)--;
+ }
+ return line;
+}
+
+/*
+ * perl_ex_perldo -- :[line [,line]] perl [command]
+ * Run a set of lines through the perl interpreter.
+ *
+ * PUBLIC: int perl_ex_perldo __P((SCR*, CHAR_T *, size_t, recno_t, recno_t));
+ */
+int
+perl_ex_perldo(scrp, cmdp, cmdlen, f_lno, t_lno)
+ SCR *scrp;
+ CHAR_T *cmdp;
+ size_t cmdlen;
+ recno_t f_lno, t_lno;
+{
+ static SV *svcurscr = 0, *svstart, *svstop, *svid;
+ CHAR_T *p;
+ GS *gp;
+ STRLEN length;
+ size_t len;
+ recno_t i;
+ char *str;
+#ifndef HAVE_PERL_5_003_01
+ char *argv[2];
+#else
+ SV* sv;
+#endif
+ dSP;
+
+ /* Initialize the interpreter. */
+ gp = scrp->gp;
+ if (!svcurscr) {
+ if (gp->perl_interp == NULL && perl_init(scrp))
+ return (1);
+ SPAGAIN;
+ SvREADONLY_on(svcurscr = perl_get_sv("curscr", TRUE));
+ SvREADONLY_on(svstart = perl_get_sv("VI::StartLine", TRUE));
+ SvREADONLY_on(svstop = perl_get_sv("VI::StopLine", TRUE));
+ SvREADONLY_on(svid = perl_get_sv("VI::ScreenId", TRUE));
+ }
+
+#ifndef HAVE_PERL_5_003_01
+ argv[0] = cmdp;
+ argv[1] = NULL;
+#else
+ length = strlen(cmdp);
+ sv = newSV(length + sizeof("sub VI::perldo {")-1 + 1 /* } */);
+ sv_setpvn(sv, "sub VI::perldo {", sizeof("sub VI::perldo {")-1);
+ sv_catpvn(sv, cmdp, length);
+ sv_catpvn(sv, "}", 1);
+ perl_eval_sv(sv, G_DISCARD | G_NOARGS);
+ SvREFCNT_dec(sv);
+ str = SvPV(GvSV(errgv),length);
+ if (length)
+ goto err;
+#endif
+
+ newVIrv(svcurscr, scrp);
+ /* Backwards compatibility. */
+ newVIrv(svid, scrp);
+
+ ENTER;
+ SAVETMPS;
+ for (i = f_lno; i <= t_lno && !api_gline(scrp, i, &str, &len); i++) {
+ sv_setpvn(GvSV(defgv),str,len);
+ sv_setiv(svstart, i);
+ sv_setiv(svstop, i);
+#ifndef HAVE_PERL_5_003_01
+ perl_call_argv("_eval_", G_SCALAR | G_EVAL | G_KEEPERR, argv);
+#else
+ PUSHMARK(sp);
+ perl_call_pv("VI::perldo", G_SCALAR | G_EVAL);
+#endif
+ str = SvPV(GvSV(errgv), length);
+ if (length) break;
+ SPAGAIN;
+ if(SvTRUEx(POPs))
+ i = replace_line(scrp, i, &t_lno);
+ PUTBACK;
+ }
+ FREETMPS;
+ LEAVE;
+
+ SvREFCNT_dec(SvRV(svcurscr));
+ SvROK_off(svcurscr);
+ SvREFCNT_dec(SvRV(svid));
+ SvROK_off(svid);
+
+ if (!length)
+ return (0);
+
+err: str[length - 1] = '\0';
+ msgq(scrp, M_ERR, "perl: %s", str);
+ return (1);
+}
+
+/*
+ * msghandler --
+ * Perl message routine so that error messages are processed in
+ * Perl, not in nvi.
+ */
+static void
+msghandler(sp, mtype, msg, len)
+ SCR *sp;
+ mtype_t mtype;
+ char *msg;
+ size_t len;
+{
+ /* Replace the trailing <newline> with an EOS. */
+ /* Let's do that later instead */
+ if (errmsg) free (errmsg);
+ errmsg = malloc(len + 1);
+ memcpy(errmsg, msg, len);
+ errmsg[len] = '\0';
+}
+
+/* Register any extra external extensions */
+
+extern void boot_DynaLoader _((CV* cv));
+extern void boot_VI _((CV* cv));
+
+static void
+xs_init()
+{
+#ifdef HAVE_PERL_5_003_01
+ dXSUB_SYS;
+#endif
+ char *file = __FILE__;
+
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+ newXS("VI::bootstrap", boot_VI, file);
+}
+
+typedef SCR * VI;
+typedef SCR * VI__OPT;
+typedef SCR * VI__MAP;
+typedef SCR * VI__MARK;
+typedef AV * AVREF;
+
+MODULE = VI PACKAGE = VI
+
+# msg --
+# Set the message line to text.
+#
+# Perl Command: VI::Msg
+# Usage: VI::Msg screenId text
+
+void
+Msg(screen, text)
+ VI screen
+ char * text
+
+ ALIAS:
+ PRINT = 1
+
+ CODE:
+ api_imessage(screen, text);
+
+# XS_VI_escreen --
+# End a screen.
+#
+# Perl Command: VI::EndScreen
+# Usage: VI::EndScreen screenId
+
+void
+EndScreen(screen)
+ VI screen
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_escreen(screen);
+ ENDMESSAGE;
+
+# XS_VI_iscreen --
+# Create a new screen. If a filename is specified then the screen
+# is opened with that file.
+#
+# Perl Command: VI::NewScreen
+# Usage: VI::NewScreen screenId [file]
+
+VI
+Edit(screen, ...)
+ VI screen
+
+ ALIAS:
+ NewScreen = 1
+
+ PROTOTYPE: $;$
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *file;
+ SCR *nsp;
+
+ CODE:
+ file = (items == 1) ? NULL : (char *)SvPV(ST(1),na);
+ INITMESSAGE;
+ rval = api_edit(screen, file, &nsp, ix);
+ ENDMESSAGE;
+
+ RETVAL = ix ? nsp : screen;
+
+ OUTPUT:
+ RETVAL
+
+# XS_VI_fscreen --
+# Return the screen id associated with file name.
+#
+# Perl Command: VI::FindScreen
+# Usage: VI::FindScreen file
+
+VI
+FindScreen(file)
+ char *file
+
+ PREINIT:
+ SCR *fsp;
+ CODE:
+ RETVAL = api_fscreen(0, file);
+
+# XS_VI_aline --
+# -- Append the string text after the line in lineNumber.
+#
+# Perl Command: VI::AppendLine
+# Usage: VI::AppendLine screenId lineNumber text
+
+void
+AppendLine(screen, linenumber, text)
+ VI screen
+ int linenumber
+ char *text
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ STRLEN length;
+
+ CODE:
+ SvPV(ST(2), length);
+ INITMESSAGE;
+ rval = api_aline(screen, linenumber, text, length);
+ ENDMESSAGE;
+
+# XS_VI_dline --
+# Delete lineNum.
+#
+# Perl Command: VI::DelLine
+# Usage: VI::DelLine screenId lineNum
+
+void
+DelLine(screen, linenumber)
+ VI screen
+ int linenumber
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_dline(screen, (recno_t)linenumber);
+ ENDMESSAGE;
+
+# XS_VI_gline --
+# Return lineNumber.
+#
+# Perl Command: VI::GetLine
+# Usage: VI::GetLine screenId lineNumber
+
+char *
+GetLine(screen, linenumber)
+ VI screen
+ int linenumber
+
+ PREINIT:
+ size_t len;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *line, *p;
+
+ PPCODE:
+ INITMESSAGE;
+ rval = api_gline(screen, (recno_t)linenumber, &p, &len);
+ ENDMESSAGE;
+
+ EXTEND(sp,1);
+ PUSHs(sv_2mortal(newSVpv(p, len)));
+
+# XS_VI_sline --
+# Set lineNumber to the text supplied.
+#
+# Perl Command: VI::SetLine
+# Usage: VI::SetLine screenId lineNumber text
+
+void
+SetLine(screen, linenumber, text)
+ VI screen
+ int linenumber
+ char *text
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ STRLEN length;
+
+ CODE:
+ SvPV(ST(2), length);
+ INITMESSAGE;
+ rval = api_sline(screen, linenumber, text, length);
+ ENDMESSAGE;
+
+# XS_VI_iline --
+# Insert the string text before the line in lineNumber.
+#
+# Perl Command: VI::InsertLine
+# Usage: VI::InsertLine screenId lineNumber text
+
+void
+InsertLine(screen, linenumber, text)
+ VI screen
+ int linenumber
+ char *text
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ STRLEN length;
+
+ CODE:
+ SvPV(ST(2), length);
+ INITMESSAGE;
+ rval = api_iline(screen, linenumber, text, length);
+ ENDMESSAGE;
+
+# XS_VI_lline --
+# Return the last line in the screen.
+#
+# Perl Command: VI::LastLine
+# Usage: VI::LastLine screenId
+
+int
+LastLine(screen)
+ VI screen
+
+ PREINIT:
+ recno_t last;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_lline(screen, &last);
+ ENDMESSAGE;
+ RETVAL=last;
+
+ OUTPUT:
+ RETVAL
+
+# XS_VI_getmark --
+# Return the mark's cursor position as a list with two elements.
+# {line, column}.
+#
+# Perl Command: VI::GetMark
+# Usage: VI::GetMark screenId mark
+
+void
+GetMark(screen, mark)
+ VI screen
+ char mark
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ PPCODE:
+ INITMESSAGE;
+ rval = api_getmark(screen, (int)mark, &cursor);
+ ENDMESSAGE;
+
+ EXTEND(sp,2);
+ PUSHs(sv_2mortal(newSViv(cursor.lno)));
+ PUSHs(sv_2mortal(newSViv(cursor.cno)));
+
+# XS_VI_setmark --
+# Set the mark to the line and column numbers supplied.
+#
+# Perl Command: VI::SetMark
+# Usage: VI::SetMark screenId mark line column
+
+void
+SetMark(screen, mark, line, column)
+ VI screen
+ char mark
+ int line
+ int column
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ cursor.lno = line;
+ cursor.cno = column;
+ rval = api_setmark(screen, (int)mark, &cursor);
+ ENDMESSAGE;
+
+# XS_VI_getcursor --
+# Return the current cursor position as a list with two elements.
+# {line, column}.
+#
+# Perl Command: VI::GetCursor
+# Usage: VI::GetCursor screenId
+
+void
+GetCursor(screen)
+ VI screen
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ PPCODE:
+ INITMESSAGE;
+ rval = api_getcursor(screen, &cursor);
+ ENDMESSAGE;
+
+ EXTEND(sp,2);
+ PUSHs(sv_2mortal(newSViv(cursor.lno)));
+ PUSHs(sv_2mortal(newSViv(cursor.cno)));
+
+# XS_VI_setcursor --
+# Set the cursor to the line and column numbers supplied.
+#
+# Perl Command: VI::SetCursor
+# Usage: VI::SetCursor screenId line column
+
+void
+SetCursor(screen, line, column)
+ VI screen
+ int line
+ int column
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ cursor.lno = line;
+ cursor.cno = column;
+ rval = api_setcursor(screen, &cursor);
+ ENDMESSAGE;
+
+# XS_VI_swscreen --
+# Change the current focus to screen.
+#
+# Perl Command: VI::SwitchScreen
+# Usage: VI::SwitchScreen screenId screenId
+
+void
+SwitchScreen(screenFrom, screenTo)
+ VI screenFrom
+ VI screenTo
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_swscreen(screenFrom, screenTo);
+ ENDMESSAGE;
+
+# XS_VI_map --
+# Associate a key with a perl procedure.
+#
+# Perl Command: VI::MapKey
+# Usage: VI::MapKey screenId key perlproc
+
+void
+MapKey(screen, key, perlproc)
+ VI screen
+ char *key
+ SV *perlproc
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ int length;
+ char *command;
+ SV *svc;
+
+ CODE:
+ INITMESSAGE;
+ svc = sv_2mortal(newSVpv(":perl ", 6));
+ sv_catsv(svc, perlproc);
+ command = SvPV(svc, length);
+ rval = api_map(screen, key, command, length);
+ ENDMESSAGE;
+
+# XS_VI_unmap --
+# Unmap a key.
+#
+# Perl Command: VI::UnmapKey
+# Usage: VI::UnmmapKey screenId key
+
+void
+UnmapKey(screen, key)
+ VI screen
+ char *key
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_unmap(screen, key);
+ ENDMESSAGE;
+
+# XS_VI_opts_set --
+# Set an option.
+#
+# Perl Command: VI::SetOpt
+# Usage: VI::SetOpt screenId setting
+
+void
+SetOpt(screen, setting)
+ VI screen
+ char *setting
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ SV *svc;
+
+ CODE:
+ INITMESSAGE;
+ svc = sv_2mortal(newSVpv(":set ", 5));
+ sv_catpv(svc, setting);
+ rval = api_run_str(screen, SvPV(svc, na));
+ ENDMESSAGE;
+
+# XS_VI_opts_get --
+# Return the value of an option.
+#
+# Perl Command: VI::GetOpt
+# Usage: VI::GetOpt screenId option
+
+void
+GetOpt(screen, option)
+ VI screen
+ char *option
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *value;
+
+ PPCODE:
+ INITMESSAGE;
+ rval = api_opts_get(screen, option, &value, NULL);
+ ENDMESSAGE;
+
+ EXTEND(SP,1);
+ PUSHs(sv_2mortal(newSVpv(value, 0)));
+ free(value);
+
+# XS_VI_run --
+# Run the ex command cmd.
+#
+# Perl Command: VI::Run
+# Usage: VI::Run screenId cmd
+
+void
+Run(screen, command)
+ VI screen
+ char *command;
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_run_str(screen, command);
+ ENDMESSAGE;
+
+void
+DESTROY(screen)
+ VI screen
+
+ CODE:
+ screen->perl_private = 0;
+
+void
+Warn(warning)
+ char *warning;
+
+ PREINIT:
+ int i;
+ CODE:
+ sv_catpv(GvSV(errgv),warning);
+
+#define TIED(package) \
+ sv_magic((SV *) (hv = \
+ (HV *)sv_2mortal((SV *)newHV())), \
+ sv_setref_pv(sv_newmortal(), package, \
+ newVIrv(newSV(0), screen)),\
+ 'P', Nullch, 0);\
+ RETVAL = newRV((SV *)hv)
+
+SV *
+Opt(screen)
+ VI screen;
+ PREINIT:
+ HV *hv;
+ CODE:
+ TIED("VI::OPT");
+ OUTPUT:
+ RETVAL
+
+SV *
+Map(screen)
+ VI screen;
+ PREINIT:
+ HV *hv;
+ CODE:
+ TIED("VI::MAP");
+ OUTPUT:
+ RETVAL
+
+SV *
+Mark(screen)
+ VI screen
+ PREINIT:
+ HV *hv;
+ CODE:
+ TIED("VI::MARK");
+ OUTPUT:
+ RETVAL
+
+MODULE = VI PACKAGE = VI::OPT
+
+void
+DESTROY(screen)
+ VI::OPT screen
+
+ CODE:
+ # typemap did all the checking
+ SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0))));
+
+void
+FETCH(screen, key)
+ VI::OPT screen
+ char *key
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *value;
+ int boolvalue;
+
+ PPCODE:
+ INITMESSAGE;
+ rval = api_opts_get(screen, key, &value, &boolvalue);
+ if (!rval) {
+ EXTEND(SP,1);
+ PUSHs(sv_2mortal((boolvalue == -1) ? newSVpv(value, 0)
+ : newSViv(boolvalue)));
+ free(value);
+ } else ST(0) = &sv_undef;
+ rval = 0;
+ ENDMESSAGE;
+
+void
+STORE(screen, key, value)
+ VI::OPT screen
+ char *key
+ SV *value
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_opts_set(screen, key, SvPV(value, na), SvIV(value),
+ SvTRUEx(value));
+ ENDMESSAGE;
+
+MODULE = VI PACKAGE = VI::MAP
+
+void
+DESTROY(screen)
+ VI::MAP screen
+
+ CODE:
+ # typemap did all the checking
+ SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0))));
+
+void
+STORE(screen, key, perlproc)
+ VI::MAP screen
+ char *key
+ SV *perlproc
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ int length;
+ char *command;
+ SV *svc;
+
+ CODE:
+ INITMESSAGE;
+ svc = sv_2mortal(newSVpv(":perl ", 6));
+ sv_catsv(svc, perlproc);
+ command = SvPV(svc, length);
+ rval = api_map(screen, key, command, length);
+ ENDMESSAGE;
+
+void
+DELETE(screen, key)
+ VI::MAP screen
+ char *key
+
+ PREINIT:
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_unmap(screen, key);
+ ENDMESSAGE;
+
+MODULE = VI PACKAGE = VI::MARK
+
+void
+DESTROY(screen)
+ VI::MARK screen
+
+ CODE:
+ # typemap did all the checking
+ SvREFCNT_dec((SV*)SvIV((SV*)SvRV(ST(0))));
+
+AV *
+FETCH(screen, mark)
+ VI::MARK screen
+ char mark
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ INITMESSAGE;
+ rval = api_getmark(screen, (int)mark, &cursor);
+ ENDMESSAGE;
+ RETVAL = newAV();
+ av_push(RETVAL, newSViv(cursor.lno));
+ av_push(RETVAL, newSViv(cursor.cno));
+
+ OUTPUT:
+ RETVAL
+
+void
+STORE(screen, mark, pos)
+ VI::MARK screen
+ char mark
+ AVREF pos
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ CODE:
+ if (av_len(pos) < 1)
+ croak("cursor position needs 2 elements");
+ INITMESSAGE;
+ cursor.lno = SvIV(*av_fetch(pos, 0, 0));
+ cursor.cno = SvIV(*av_fetch(pos, 1, 0));
+ rval = api_setmark(screen, (int)mark, &cursor);
+ ENDMESSAGE;
+
+void
+FIRSTKEY(screen, ...)
+ VI::MARK screen
+
+ ALIAS:
+ NEXTKEY = 1
+
+ PROTOTYPE: $;$
+
+ PREINIT:
+ struct _mark cursor;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int next;
+ char key[] = {0, 0};
+
+ PPCODE:
+ if (items == 2) {
+ next = 1;
+ *key = *(char *)SvPV(ST(1),na);
+ } else next = 0;
+ if (api_nextmark(screen, next, key) != 1) {
+ EXTEND(sp, 1);
+ PUSHs(sv_2mortal(newSVpv(key, 1)));
+ } else ST(0) = &sv_undef;
diff --git a/contrib/nvi/perl_api/perlsfio.c b/contrib/nvi/perl_api/perlsfio.c
new file mode 100644
index 000000000000..20ff4773495a
--- /dev/null
+++ b/contrib/nvi/perl_api/perlsfio.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1996
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1996
+ * Sven Verdoolaege. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)perlsfio.c 8.1 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#include "perl_extern.h"
+
+/*
+ * PUBLIC: #ifdef USE_SFIO
+ */
+#ifdef USE_SFIO
+
+#define NIL(type) ((type)0)
+
+static int
+sfnviwrite(f, buf, n, disc)
+Sfio_t* f; /* stream involved */
+char* buf; /* buffer to read into */
+int n; /* number of bytes to read */
+Sfdisc_t* disc; /* discipline */
+{
+ SCR *scrp;
+
+ scrp = (SCR *)SvIV((SV*)SvRV(perl_get_sv("curscr", FALSE)));
+ msgq(scrp, M_INFO, "%.*s", n, buf);
+ return n;
+}
+
+/*
+ * sfdcnewnvi --
+ * Create nvi discipline
+ *
+ * PUBLIC: Sfdisc_t* sfdcnewnvi __P((SCR*));
+ */
+
+Sfdisc_t *
+sfdcnewnvi(scrp)
+ SCR *scrp;
+{
+ Sfdisc_t* disc;
+
+ MALLOC(scrp, disc, Sfdisc_t*, sizeof(Sfdisc_t));
+ if (!disc) return disc;
+
+ disc->readf = (Sfread_f)NULL;
+ disc->writef = sfnviwrite;
+ disc->seekf = (Sfseek_f)NULL;
+ disc->exceptf = (Sfexcept_f)NULL;
+ return disc;
+}
+
+/*
+ * PUBLIC: #endif
+ */
+#endif /* USE_SFIO */
diff --git a/contrib/nvi/perl_api/typemap b/contrib/nvi/perl_api/typemap
new file mode 100644
index 000000000000..0e38a9c07bc7
--- /dev/null
+++ b/contrib/nvi/perl_api/typemap
@@ -0,0 +1,42 @@
+TYPEMAP
+# Grr can't let it end in OBJ 'cause xsubpp would
+# s/OBJ$/REF/ that for the DESTROY function
+VI T_VIOBJNOMUNGE
+VI::OPT T_VIOBJREF
+VI::MAP T_VIOBJREF
+VI::MARK T_VIOBJREF
+AVREF T_AVREFREF
+
+INPUT
+T_AVREFREF
+ if (SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVAV)
+ $var = (AV *)SvRV($arg);
+ else
+ croak(\"$var is not a reference to an array\")
+T_VIOBJNOMUNGE
+ if (sv_isa($arg, \"VI\")) {
+ IV tmp = SvIV((SV*)SvRV($arg));
+ $var = ($type) tmp;
+ if (!tmp)
+ croak(\"screen no longer exists\");
+ }
+ else
+ croak(\"$var is not of type ${ntype}\")
+T_VIOBJREF
+ if (sv_isa($arg, \"${ntype}\")) {
+ IV tmp = SvIV((SV*)SvRV($arg));
+ if (sv_isa((SV *)tmp, \"VI\")) {
+ IV tmp2 = SvIV((SV*)SvRV((SV *)tmp));
+ $var = ($type) tmp2;
+ if (!tmp2)
+ croak(\"screen no longer exists\");
+ }
+ else
+ croak(\"$var is not of type ${ntype}\");
+ }
+ else
+ croak(\"$var is not of type ${ntype}\")
+
+OUTPUT
+T_VIOBJNOMUNGE
+ newVIrv($arg, $var);
diff --git a/contrib/nvi/perl_scripts/forall.pl b/contrib/nvi/perl_scripts/forall.pl
new file mode 100644
index 000000000000..b9f85013b17d
--- /dev/null
+++ b/contrib/nvi/perl_scripts/forall.pl
@@ -0,0 +1,10 @@
+sub forall {
+ my ($code) = shift;
+ my ($i) = $VI::StartLine-1;
+ while (++$i <= $VI::StopLine) {
+ $_ = $curscr->GetLine($i);
+ VI::SetLine($VI::ScreenId, $i, $_) if(&$code);
+ }
+}
+
+1;
diff --git a/contrib/nvi/perl_scripts/make.pl b/contrib/nvi/perl_scripts/make.pl
new file mode 100644
index 000000000000..118dd99a424c
--- /dev/null
+++ b/contrib/nvi/perl_scripts/make.pl
@@ -0,0 +1,27 @@
+sub make {
+ open MAKE, "make 2>&1 1>/dev/null |";
+ while(<MAKE>) {
+ if (($file, $line, $msg) = /([^: ]*):(\d*):(.+)/) {
+ if ($file == $prevfile && $line == $prevline) {
+ $error[-1]->[2] .= "\n$msg";
+ } else {
+ push @error, [$file, $line, $msg];
+ ($prevline, $prevfile) = ($line, $file);
+ }
+ }
+ }
+ close MAKE;
+}
+
+sub nexterror {
+ if ($index <= $#error) {
+ my $error = $error[$index++];
+ $curscr->Edit($error->[0]);
+ $curscr->SetCursor($error->[1],0);
+ $curscr->Msg($error->[2]);
+ }
+}
+
+# preverror is left as an exercise
+
+1;
diff --git a/contrib/nvi/perl_scripts/tk.pl b/contrib/nvi/perl_scripts/tk.pl
new file mode 100644
index 000000000000..f8d1bc068df3
--- /dev/null
+++ b/contrib/nvi/perl_scripts/tk.pl
@@ -0,0 +1,20 @@
+# make sure every subprocess has it's exit and that the main one
+# hasn't
+sub fun {
+ unless ($pid = fork) {
+ unless (fork) {
+ use Tk;
+ $MW = MainWindow->new;
+ $hello = $MW->Button(
+ -text => 'Hello, world',
+ -command => sub {exit;},
+ );
+ $hello->pack;
+ MainLoop;
+ }
+ exit 0;
+ }
+ waitpid($pid, 0);
+}
+
+1;
diff --git a/contrib/nvi/perl_scripts/wc.pl b/contrib/nvi/perl_scripts/wc.pl
new file mode 100644
index 000000000000..0a5015987d10
--- /dev/null
+++ b/contrib/nvi/perl_scripts/wc.pl
@@ -0,0 +1,11 @@
+sub wc {
+ my $words;
+ $i = $VI::StartLine;
+ while ($i <= $VI::StopLine) {
+ $_ = $curscr->GetLine($i++);
+ $words+=split;
+ }
+ $curscr->Msg("$words words");
+}
+
+1;
diff --git a/contrib/nvi/tcl_api/tcl.c b/contrib/nvi/tcl_api/tcl.c
new file mode 100644
index 000000000000..8f4a430bac4c
--- /dev/null
+++ b/contrib/nvi/tcl_api/tcl.c
@@ -0,0 +1,852 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995
+ * Keith Bostic. All rights reserved.
+ * Copyright (c) 1995
+ * George V. Neville-Neil. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tcl.c 8.16 (Berkeley) 10/16/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tcl_extern.h"
+
+static int getint __P((Tcl_Interp *, char *, char *, int *));
+static int getscreenid __P((Tcl_Interp *, SCR **, char *, char *));
+static void msghandler __P((SCR *, mtype_t, char *, size_t));
+
+extern GS *__global_list; /* XXX */
+
+/*
+ * INITMESSAGE --
+ * Macros to point messages at the Tcl message handler.
+ */
+#define INITMESSAGE \
+ scr_msg = __global_list->scr_msg; \
+ __global_list->scr_msg = msghandler;
+#define ENDMESSAGE \
+ __global_list->scr_msg = scr_msg;
+
+/*
+ * tcl_fscreen --
+ * Return the screen id associated with file name.
+ *
+ * Tcl Command: viFindScreen
+ * Usage: viFindScreen file
+ */
+static int
+tcl_fscreen(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+
+ if (argc != 2) {
+ Tcl_SetResult(interp, "Usage: viFindScreen file", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, NULL, argv[1]))
+ return (TCL_ERROR);
+
+ (void)sprintf(interp->result, "%d", sp->id);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_aline --
+ * -- Append the string text after the line in lineNumber.
+ *
+ * Tcl Command: viAppendLine
+ * Usage: viAppendLine screenId lineNumber text
+ */
+static int
+tcl_aline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int lno, rval;
+
+ if (argc != 4) {
+ Tcl_SetResult(interp,
+ "Usage: viAppendLine screenId lineNumber text", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL) ||
+ getint(interp, "line number", argv[2], &lno))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_aline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_dline --
+ * Delete lineNum.
+ *
+ * Tcl Command: viDelLine
+ * Usage: viDelLine screenId lineNum
+ */
+static int
+tcl_dline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int lno, rval;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viDelLine screenId lineNumber", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL) ||
+ getint(interp, "line number", argv[2], &lno))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_dline(sp, (recno_t)lno);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_gline --
+ * Return lineNumber.
+ *
+ * Tcl Command: viGetLine
+ * Usage: viGetLine screenId lineNumber
+ */
+static int
+tcl_gline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ size_t len;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int lno, rval;
+ char *line, *p;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viGetLine screenId lineNumber", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+ if (getscreenid(interp, &sp, argv[1], NULL) ||
+ getint(interp, "line number", argv[2], &lno))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_gline(sp, (recno_t)lno, &p, &len);
+ ENDMESSAGE;
+
+ if (rval)
+ return (TCL_ERROR);
+
+ if ((line = malloc(len + 1)) == NULL)
+ exit(1); /* XXX */
+ memmove(line, p, len);
+ line[len] = '\0';
+ Tcl_SetResult(interp, line, TCL_DYNAMIC);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_iline --
+ * Insert the string text after the line in lineNumber.
+ *
+ * Tcl Command: viInsertLine
+ * Usage: viInsertLine screenId lineNumber text
+ */
+static int
+tcl_iline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int lno, rval;
+
+ if (argc != 4) {
+ Tcl_SetResult(interp,
+ "Usage: viInsertLine screenId lineNumber text", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL) ||
+ getint(interp, "line number", argv[2], &lno))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_iline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_lline --
+ * Return the last line in the screen.
+ *
+ * Tcl Command: viLastLine
+ * Usage: viLastLine screenId
+ */
+static int
+tcl_lline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ recno_t last;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ if (argc != 2) {
+ Tcl_SetResult(interp, "Usage: viLastLine screenId", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_lline(sp, &last);
+ ENDMESSAGE;
+ if (rval)
+ return (TCL_ERROR);
+
+ (void)sprintf(interp->result, "%lu", (unsigned long)last);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_sline --
+ * Set lineNumber to the text supplied.
+ *
+ * Tcl Command: viSetLine
+ * Usage: viSetLine screenId lineNumber text
+ */
+static int
+tcl_sline(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int lno, rval;
+
+ if (argc != 4) {
+ Tcl_SetResult(interp,
+ "Usage: viSetLine screenId lineNumber text", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL) ||
+ getint(interp, "line number", argv[2], &lno))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_sline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_getmark --
+ * Return the mark's cursor position as a list with two elements.
+ * {line, column}.
+ *
+ * Tcl Command: viGetMark
+ * Usage: viGetMark screenId mark
+ */
+static int
+tcl_getmark(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ MARK cursor;
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char buf[20];
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viGetMark screenId mark", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_getmark(sp, (int)argv[2][0], &cursor);
+ ENDMESSAGE;
+
+ if (rval)
+ return (TCL_ERROR);
+
+ (void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.lno);
+ Tcl_AppendElement(interp, buf);
+ (void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.cno);
+ Tcl_AppendElement(interp, buf);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_setmark --
+ * Set the mark to the line and column numbers supplied.
+ *
+ * Tcl Command: viSetMark
+ * Usage: viSetMark screenId mark line column
+ */
+static int
+tcl_setmark(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ MARK cursor;
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int i, rval;
+
+ if (argc != 5) {
+ Tcl_SetResult(interp,
+ "Usage: viSetMark screenId mark line column", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ if (getint(interp, "line number", argv[3], &i))
+ return (TCL_ERROR);
+ cursor.lno = i;
+ if (getint(interp, "column number", argv[4], &i))
+ return (TCL_ERROR);
+ cursor.cno = i;
+ INITMESSAGE;
+ rval = api_setmark(sp, (int)argv[2][0], &cursor);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_getcursor --
+ * Return the current cursor position as a list with two elements.
+ * {line, column}.
+ *
+ * Tcl Command: viGetCursor
+ * Usage: viGetCursor screenId
+ */
+static int
+tcl_getcursor(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ MARK cursor;
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char buf[20];
+
+ if (argc != 2) {
+ Tcl_SetResult(interp,
+ "Usage: viGetCursor screenId", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_getcursor(sp, &cursor);
+ ENDMESSAGE;
+
+ if (rval)
+ return (TCL_ERROR);
+
+ (void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.lno);
+ Tcl_AppendElement(interp, buf);
+ (void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.cno);
+ Tcl_AppendElement(interp, buf);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_setcursor --
+ * Set the cursor to the line and column numbers supplied.
+ *
+ * Tcl Command: viSetCursor
+ * Usage: viSetCursor screenId line column
+ */
+static int
+tcl_setcursor(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ MARK cursor;
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int i, rval;
+
+ if (argc != 4) {
+ Tcl_SetResult(interp,
+ "Usage: viSetCursor screenId line column", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ if (getint(interp, "screen id", argv[2], &i))
+ return (TCL_ERROR);
+ cursor.lno = i;
+ if (getint(interp, "screen id", argv[3], &i))
+ return (TCL_ERROR);
+ cursor.cno = i;
+ INITMESSAGE;
+ rval = api_setcursor(sp, &cursor);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_msg --
+ * Set the message line to text.
+ *
+ * Tcl Command: viMsg
+ * Usage: viMsg screenId text
+ */
+static int
+tcl_msg(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp, "Usage: viMsg screenId text", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ api_imessage(sp, argv[2]);
+
+ return (TCL_OK);
+}
+
+/*
+ * tcl_iscreen --
+ * Create a new screen. If a filename is specified then the screen
+ * is opened with that file.
+ *
+ * Tcl Command: viNewScreen
+ * Usage: viNewScreen screenId [file]
+ */
+static int
+tcl_iscreen(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp, *nsp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ if (argc != 2 && argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viNewScreen screenId [file]", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_edit(sp, argv[2], &nsp, 1);
+ ENDMESSAGE;
+
+ if (rval)
+ return (TCL_ERROR);
+
+ (void)sprintf(interp->result, "%d", nsp->id);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_escreen --
+ * End a screen.
+ *
+ * Tcl Command: viEndScreen
+ * Usage: viEndScreen screenId
+ */
+static int
+tcl_escreen(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ if (argc != 2) {
+ Tcl_SetResult(interp,
+ "Usage: viEndScreen screenId", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_escreen(sp);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_swscreen --
+ * Change the current focus to screen.
+ *
+ * Tcl Command: viSwitchScreen
+ * Usage: viSwitchScreen screenId screenId
+ */
+static int
+tcl_swscreen(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp, *new;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viSwitchScreen cur_screenId new_screenId",
+ TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ if (getscreenid(interp, &new, argv[2], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_swscreen(sp, new);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_map --
+ * Associate a key with a tcl procedure.
+ *
+ * Tcl Command: viMapKey
+ * Usage: viMapKey screenId key tclproc
+ */
+static int
+tcl_map(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char command[256];
+
+ if (argc != 4) {
+ Tcl_SetResult(interp,
+ "Usage: viMapKey screenId key tclproc", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ (void)snprintf(command, sizeof(command), ":tcl %s\n", argv[3]);
+ rval = api_map(sp, argv[2], command, strlen(command));
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_unmap --
+ * Unmap a key.
+ *
+ * Tcl Command: viUnmapKey
+ * Usage: viUnmMapKey screenId key
+ */
+static int
+tcl_unmap(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viUnmapKey screenId key", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_unmap(sp, argv[2]);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_opts_set --
+ * Set an option.
+ *
+ * Tcl Command: viSetOpt
+ * Usage: viSetOpt screenId command
+ */
+static int
+tcl_opts_set(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *setting;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viSetOpt screenId command", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ /*rval = api_opts_set(sp, argv[2]);*/
+ MALLOC(sp, setting, char *, strlen(argv[2])+6);
+ strcpy(setting, ":set ");
+ strcpy(setting+5, argv[2]);
+ rval=api_run_str(sp, setting);
+ free(setting);
+ ENDMESSAGE;
+
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tcl_opts_get --
+ Return the value of an option.
+ *
+ * Tcl Command: viGetOpt
+ * Usage: viGetOpt screenId option
+ */
+static int
+tcl_opts_get(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SCR *sp;
+ void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
+ int rval;
+ char *value;
+
+ if (argc != 3) {
+ Tcl_SetResult(interp,
+ "Usage: viGetOpt screenId option", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if (getscreenid(interp, &sp, argv[1], NULL))
+ return (TCL_ERROR);
+ INITMESSAGE;
+ rval = api_opts_get(sp, argv[2], &value, NULL);
+ ENDMESSAGE;
+ if (rval)
+ return (TCL_ERROR);
+
+ Tcl_SetResult(interp, value, TCL_DYNAMIC);
+ return (TCL_OK);
+}
+
+/*
+ * tcl_init --
+ * Create the TCL commands used by nvi.
+ *
+ * PUBLIC: int tcl_init __P((GS *));
+ */
+int
+tcl_init(gp)
+ GS *gp;
+{
+ gp->tcl_interp = Tcl_CreateInterp();
+ if (Tcl_Init(gp->tcl_interp) == TCL_ERROR)
+ return (1);
+
+#define TCC(name, function) { \
+ Tcl_CreateCommand(gp->tcl_interp, name, function, \
+ (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); \
+}
+ TCC("viAppendLine", tcl_aline);
+ TCC("viDelLine", tcl_dline);
+ TCC("viEndScreen", tcl_escreen);
+ TCC("viFindScreen", tcl_fscreen);
+ TCC("viGetCursor", tcl_getcursor);
+ TCC("viGetLine", tcl_gline);
+ TCC("viGetMark", tcl_getmark);
+ TCC("viGetOpt", tcl_opts_get);
+ TCC("viInsertLine", tcl_iline);
+ TCC("viLastLine", tcl_lline);
+ TCC("viMapKey", tcl_map);
+ TCC("viMsg", tcl_msg);
+ TCC("viNewScreen", tcl_iscreen);
+ TCC("viSetCursor", tcl_setcursor);
+ TCC("viSetLine", tcl_sline);
+ TCC("viSetMark", tcl_setmark);
+ TCC("viSetOpt", tcl_opts_set);
+ TCC("viSwitchScreen", tcl_swscreen);
+ TCC("viUnmapKey", tcl_unmap);
+
+ return (0);
+}
+
+/*
+ * getscreenid --
+ * Get the specified screen pointer.
+ *
+ * XXX
+ * This is fatal. We can't post a message into vi that we're unable to find
+ * the screen without first finding the screen... So, this must be the first
+ * thing a Tcl routine does, and, if it fails, the last as well.
+ */
+static int
+getscreenid(interp, spp, id, name)
+ Tcl_Interp *interp;
+ SCR **spp;
+ char *id, *name;
+{
+ int scr_no;
+ char buf[64];
+
+ if (id != NULL && getint(interp, "screen id", id, &scr_no))
+ return (1);
+ if ((*spp = api_fscreen(scr_no, name)) == NULL) {
+ (void)snprintf(buf, sizeof(buf),
+ "unknown screen id: %s", name == NULL ? id : name);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * getint --
+ * Get a Tcl integer.
+ *
+ * XXX
+ * This code assumes that both recno_t and size_t are larger than ints.
+ */
+static int
+getint(interp, msg, s, intp)
+ Tcl_Interp *interp;
+ char *msg, *s;
+ int *intp;
+{
+ char buf[64];
+
+ if (Tcl_GetInt(interp, s, intp) == TCL_ERROR)
+ return (1);
+ if (*intp < 0) {
+ (void)snprintf(buf, sizeof(buf),
+ "illegal %s %s: may not be negative", msg, s);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * msghandler --
+ * Tcl message routine so that error messages are processed in
+ * Tcl, not in nvi.
+ */
+static void
+msghandler(sp, mtype, msg, len)
+ SCR *sp;
+ mtype_t mtype;
+ char *msg;
+ size_t len;
+{
+ /* Replace the trailing <newline> with an EOS. */
+ msg[len - 1] = '\0';
+
+ Tcl_SetResult(sp->gp->tcl_interp, msg, TCL_VOLATILE);
+}
diff --git a/contrib/nvi/tcl_scripts/errors.tcl b/contrib/nvi/tcl_scripts/errors.tcl
new file mode 100644
index 000000000000..94a1e6a8c3c9
--- /dev/null
+++ b/contrib/nvi/tcl_scripts/errors.tcl
@@ -0,0 +1,44 @@
+# @(#)errors.tcl 8.2 (Berkeley) 11/18/95
+#
+# File: errors.tcl
+#
+# Author: George V. Neville-Neil
+#
+# Purpose: This file contains vi/tcl code that allows a vi user to parse
+# compiler errors and warnings from a make.out file.
+
+proc findErr {} {
+ global errScreen
+ global currFile
+ global fileScreen
+ set errLine [lindex [viGetCursor $errScreen] 0]
+ set currLine [split [viGetLine $errScreen $errLine] :]
+ set currFile [lindex $currLine 0]
+ set fileScreen [viNewScreen $errScreen $currFile]
+ viSetCursor $fileScreen [lindex $currLine 1] 1
+ viMapKey $viScreenId  nextErr
+}
+
+proc nextErr {} {
+ global errScreen
+ global fileScreen
+ global currFile
+ set errLine [lindex [viGetCursor $errScreen] 0]
+ set currLine [split [viGetLine $errScreen $errLine] :]
+ if {[string match $currFile [lindex $currLine 0]]} {
+ viSetCursor $fileScreen [lindex $currLine 1] 0
+ viSwitchScreen $fileScreen
+ } else {
+ viEndScreen $fileScreen
+ set currFile [lindex $currLine 0]
+ set fileScreen[viNewScreen $errScreen $currFile]
+ viSetCursor $fileScreen [lindex $currLine 1] 0
+ }
+}
+
+proc initErr {} {
+ global viScreenId
+ global errScreen
+ set errScreen [viNewScreen $viScreenId make.out]
+ viMapKey $viScreenId  findErr
+}
diff --git a/contrib/nvi/tcl_scripts/gnats.tcl b/contrib/nvi/tcl_scripts/gnats.tcl
new file mode 100644
index 000000000000..61ecb6e2cda0
--- /dev/null
+++ b/contrib/nvi/tcl_scripts/gnats.tcl
@@ -0,0 +1,95 @@
+# @(#)gnats.tcl 8.2 (Berkeley) 11/18/95
+#
+proc init {catFile} {
+ global viScreenId
+ global categories
+ set categories {}
+ set categoriesFile [open $catFile r]
+ while {[gets $categoriesFile line] >= 0} {
+ lappend categories $line
+ }
+ close $categoriesFile
+ viMsg $viScreenId $categories
+ viMapKey $viScreenId  next
+}
+
+proc next {} {
+ global viScreenId
+ set cursor [viGetCursor $viScreenId]
+ set lineNum [lindex $cursor 0]
+ set line [viGetLine $viScreenId $lineNum]
+ viMsg $viScreenId [lindex $line 0]
+ if {[lindex $line 0] == ">Confidential:"} {
+ confNext $lineNum $line
+ } elseif {[lindex $line 0] == ">Severity:"} {
+ sevNext $lineNum $line
+ } elseif {[lindex $line 0] == ">Priority:"} {
+ priNext $lineNum $line
+ } elseif {[lindex $line 0] == ">Class:"} {
+ classNext $lineNum $line
+ } elseif {[lindex $line 0] == ">Category:"} {
+ catNext $lineNum $line
+ }
+}
+
+proc confNext {lineNum line} {
+ global viScreenId
+ viMsg $viScreenId [lindex $line 1]
+ if {[lindex $line 1] == "yes"} {
+ viSetLine $viScreenId $lineNum ">Confidential: no"
+ } else {
+ viSetLine $viScreenId $lineNum ">Confidential: yes"
+ }
+}
+
+proc sevNext {lineNum line} {
+ global viScreenId
+ viMsg $viScreenId [lindex $line 1]
+ if {[lindex $line 1] == "non-critical"} {
+ viSetLine $viScreenId $lineNum ">Severity: serious"
+ } elseif {[lindex $line 1] == "serious"} {
+ viSetLine $viScreenId $lineNum ">Severity: critical"
+ } elseif {[lindex $line 1] == "critical"} {
+ viSetLine $viScreenId $lineNum ">Severity: non-critical"
+ }
+}
+
+proc priNext {lineNum line} {
+ global viScreenId
+ viMsg $viScreenId [lindex $line 1]
+ if {[lindex $line 1] == "low"} {
+ viSetLine $viScreenId $lineNum ">Priority: medium"
+ } elseif {[lindex $line 1] == "medium"} {
+ viSetLine $viScreenId $lineNum ">Priority: high"
+ } elseif {[lindex $line 1] == "high"} {
+ viSetLine $viScreenId $lineNum ">Priority: low"
+ }
+}
+
+proc classNext {lineNum line} {
+ global viScreenId
+ viMsg $viScreenId [lindex $line 1]
+ if {[lindex $line 1] == "sw-bug"} {
+ viSetLine $viScreenId $lineNum ">Class: doc-bug"
+ } elseif {[lindex $line 1] == "doc-bug"} {
+ viSetLine $viScreenId $lineNum ">Class: change-request"
+ } elseif {[lindex $line 1] == "change-request"} {
+ viSetLine $viScreenId $lineNum ">Class: support"
+ } elseif {[lindex $line 1] == "support"} {
+ viSetLine $viScreenId $lineNum ">Class: sw-bug"
+ }
+}
+
+proc catNext {lineNum line} {
+ global viScreenId
+ global categories
+ viMsg $viScreenId [lindex $line 1]
+ set curr [lsearch -exact $categories [lindex $line 1]]
+ if {$curr == -1} {
+ set curr 0
+ }
+ viMsg $viScreenId $curr
+ viSetLine $viScreenId $lineNum ">Class: [lindex $categories $curr]"
+}
+
+init abekas
diff --git a/contrib/nvi/tcl_scripts/mailprocs.tcl b/contrib/nvi/tcl_scripts/mailprocs.tcl
new file mode 100644
index 000000000000..eccb9cdfcf74
--- /dev/null
+++ b/contrib/nvi/tcl_scripts/mailprocs.tcl
@@ -0,0 +1,115 @@
+# @(#)mailprocs.tcl 8.3 (Berkeley) 4/29/96
+#
+proc validLine {} {
+ global viScreenId
+ set line [viGetLine $viScreenId [lindex [viGetCursor $viScreenId] 0]]
+ if {[string compare [lindex [split $line :] 0] "To"] == 0} {
+ set addrs [lindex [split $line :] 1]
+ foreach name [split $addrs ,] {
+ isValid [string trim $name]
+ }
+ }
+}
+
+proc valid {target} {
+ set found 0
+ set aliasFile [open "~/Mail/aliases" r]
+ while {[gets $aliasFile line] >= 0} {
+ set name [lindex [split $line :] 0]
+ set address [lindex [split $line :] 1]
+ if {[string compare $target $name] == 0} {
+ set found 1
+ break
+ }
+ }
+ close $aliasFile
+ if {$found == 1} {
+ return $address
+ } else {
+ return $found
+ }
+}
+
+proc isValid {target} {
+ global viScreenId
+ set address [valid $target]
+ if {$address != 0} {
+ viMsg $viScreenId "$target is [string trim $address]"
+ } else {
+ viMsg $viScreenId "$target not found"
+ }
+}
+
+proc isAliasedLine {} {
+ global viScreenId
+ set line [viGetLine $viScreenId [lindex [viGetCursor $viScreenId] 0]]
+ if {[string match [lindex [split $line :] 0] "*To"] == 0} {
+ set addrs [lindex [split $line :] 1]
+ foreach name [split $addrs ,] {
+ isAliased [string trim $name]
+ }
+ }
+}
+
+proc aliased {target} {
+ set found 0
+ set aliasFile [open "~/Mail/aliases" r]
+ while {[gets $aliasFile line] >= 0} {
+ set name [lindex [split $line :] 0]
+ set address [lindex [split $line :] 1]
+ if {[string compare $target [string trim $address]] == 0} {
+ set found 1
+ break
+ }
+ }
+ close $aliasFile
+
+ return $found
+}
+
+proc isAliased {target} {
+ global viScreenId
+ set found [aliased $target]
+
+ if {$found} {
+ viMsg $viScreenId "$target is aliased to [string trim $name]"
+ } else {
+ viMsg $viScreenId "$target not aliased"
+ }
+}
+
+proc appendAlias {target address} {
+ if {![aliased $target]} {
+ set aliasFile [open "~/Mail/aliases" a]
+ puts $aliasFile "$target: $address"
+ }
+ close $aliasFile
+}
+
+proc expand {} {
+ global viScreenId
+ set row [lindex [viGetCursor $viScreenId] 0]]
+ set column [lindex [viGetCursor $viScreenId] 1]]
+ set line [viGetLine $viScreenId $row]
+ while {$column < [string length $line] && \
+ [string index $line $column] != ' '} {
+ append $target [string index $line $column]
+ incr $column
+ }
+ set found [isValid $target]
+}
+
+proc cite {} {
+ global viScreenId
+ global viStartLine
+ global viStopLine
+ for {set i $viStartLine} {$i <= $viStopLine} {incr i} {
+ set newLine "> "
+ append newLine [viGetLine $viScreenId $i]
+ viSetLine $viScreenId $i $newLine
+ }
+}
+
+global viScreenId
+viMapKey $viScreenId  isAliasedLine
+viMapKey $viScreenId  validLine
diff --git a/contrib/nvi/tcl_scripts/wc.tcl b/contrib/nvi/tcl_scripts/wc.tcl
new file mode 100644
index 000000000000..25d0f6254391
--- /dev/null
+++ b/contrib/nvi/tcl_scripts/wc.tcl
@@ -0,0 +1,16 @@
+# @(#)wc.tcl 8.2 (Berkeley) 11/18/95
+#
+proc wc {} {
+ global viScreenId
+ global viStartLine
+ global viStopLine
+
+ set lines [viLastLine $viScreenId]
+ set output ""
+ set words 0
+ for {set i $viStartLine} {$i <= $viStopLine} {incr i} {
+ set outLine [split [string trim [viGetLine $viScreenId $i]]]
+ set words [expr $words + [llength $outLine]]
+ }
+ viMsg $viScreenId "$words words"
+}
diff --git a/contrib/nvi/tk/init.tcl b/contrib/nvi/tk/init.tcl
new file mode 100644
index 000000000000..78c5a115c0a7
--- /dev/null
+++ b/contrib/nvi/tk/init.tcl
@@ -0,0 +1,1096 @@
+# @(#)init.tcl 8.10 (Berkeley) 7/19/96
+proc screen {} {
+ global tk_ssize_row
+ global tk_ssize_col
+
+ # Build menubar with File, Options and Help entries.
+ frame .menu -relief raised -borderwidth 1
+ pack append . .menu {top fillx}
+
+ # File pull-down menu
+ menubutton .menu.file -text "File" \
+ -menu .menu.file.fileops -underline 0
+ menu .menu.file.fileops
+ .menu.file.fileops add command -label "Edit ..." \
+ -command "tk_edit" -underline 0
+ .menu.file.fileops add command -label "Save File" \
+ -command "tk_write" -underline 0
+ .menu.file.fileops add command -label "Save File as ..." \
+ -command "tk_writeas" -underline 1
+ .menu.file.fileops add command -label "Save and Quit" \
+ -command "tk_writequit" -underline 7
+ .menu.file.fileops add command -label "Quit" \
+ -command "tk_quit" -underline 0
+
+ # Options pull-down menu
+ menubutton .menu.option -text "Options" \
+ -menu .menu.option.optionops -underline 0
+ menu .menu.option.optionops
+ .menu.option.optionops add command -label "Set all" \
+ -command tk_options -underline 0
+
+ # Help pull-down menu
+ menubutton .menu.help -text "Help" \
+ -menu .menu.help.helpops -underline 0
+ menu .menu.help.helpops
+ .menu.help.helpops add command -label "On Help" -underline 3 \
+ -command tk_help
+ .menu.help.helpops add command -label "On Version" -underline 3 \
+ -command tk_version
+
+ pack append .menu \
+ .menu.file {left} .menu.option {left} .menu.help {right}
+
+ # Set up for keyboard-based menu traversal
+ tk_bindForTraversal .
+ bind . <Any-Enter> {focus .}
+ focus .
+ tk_menuBar .menu .menu.file .menu.help
+
+ # Create text window
+ text .t -relief raised -bd 1 -setgrid true -yscrollcommand ".s set"
+ scrollbar .s -relief flat -command ".t yview"
+ pack append . .s {right filly} .t {expand fill}
+
+ # Use tags to build a cursor for the text window.
+ set bg [lindex [.t config -background] 4]
+ set fg [lindex [.t config -foreground] 4]
+ .t tag configure tk_cursor -background $fg -foreground $bg
+ .t mark set tk_cursor_indx insert
+ .t tag add tk_cursor tk_cursor_indx
+
+ # Bind the keys.
+ bind .t <Any-KeyPress> {tk_flash; break}
+ bind .t 0 {tk_key_enter "0"; break}
+ bind .t 1 {tk_key_enter "1"; break}
+ bind .t 2 {tk_key_enter "2"; break}
+ bind .t 3 {tk_key_enter "3"; break}
+ bind .t 4 {tk_key_enter "4"; break}
+ bind .t 5 {tk_key_enter "5"; break}
+ bind .t 6 {tk_key_enter "6"; break}
+ bind .t 7 {tk_key_enter "7"; break}
+ bind .t 8 {tk_key_enter "8"; break}
+ bind .t 9 {tk_key_enter "9"; break}
+ bind .t <BackSpace> {tk_key_enter "\010"; break}
+ bind .t <Control-a> {tk_key_enter "\001"; break}
+ bind .t <Control-b> {tk_key_enter "\002"; break}
+ bind .t <Control-c> {tk_key_enter "\003"; break}
+ bind .t <Control-d> {tk_key_enter "\004"; break}
+ bind .t <Control-e> {tk_key_enter "\005"; break}
+ bind .t <Control-f> {tk_key_enter "\006"; break}
+ bind .t <Control-g> {tk_key_enter "\007"; break}
+ bind .t <Control-h> {tk_key_enter "\010"; break}
+ bind .t <Control-i> {tk_key_enter "\011"; break}
+ bind .t <Control-j> {tk_key_enter "\012"; break}
+ bind .t <Control-k> {tk_key_enter "\013"; break}
+ bind .t <Control-l> {tk_key_enter "\014"; break}
+ bind .t <Control-m> {tk_key_enter "\015"; break}
+ bind .t <Control-n> {tk_key_enter "\016"; break}
+ bind .t <Control-o> {tk_key_enter "\017"; break}
+ bind .t <Control-p> {tk_key_enter "\020"; break}
+ bind .t <Control-q> {tk_key_enter "\021"; break}
+ bind .t <Control-r> {tk_key_enter "\022"; break}
+ bind .t <Control-s> {tk_key_enter "\023"; break}
+ bind .t <Control-t> {tk_key_enter "\024"; break}
+ bind .t <Control-u> {tk_key_enter "\025"; break}
+ bind .t <Control-v> {tk_key_enter "\026"; break}
+ bind .t <Control-w> {tk_key_enter "\027"; break}
+ bind .t <Control-x> {tk_key_enter "\030"; break}
+ bind .t <Control-y> {tk_key_enter "\031"; break}
+ bind .t <Control-z> {tk_key_enter "\032"; break}
+ bind .t <Control_L> {tk_noop; break}
+ bind .t <Control_R> {tk_noop; break}
+ bind .t <Delete> {tk_key_enter "x"; break}
+ bind .t <Down> {tk_key_enter "j"; break}
+ bind .t <End> {tk_key_enter "G"; break}
+ bind .t <Escape> {tk_key_enter "\033"; break}
+ bind .t <Home> {tk_key_enter "1G"; break}
+ bind .t <Insert> {tk_key_enter "i"; break}
+ bind .t <Left> {tk_key_enter "h"; break}
+ bind .t <Next> {tk_key_enter "\006"; break}
+ bind .t <Prior> {tk_key_enter "\002"; break}
+ bind .t <Return> {tk_key_enter "\015"; break}
+ bind .t <Right> {tk_key_enter "l"; break}
+ bind .t <Shift_L> {tk_noop; break}
+ bind .t <Shift_Lock> {tk_noop; break}
+ bind .t <Shift_R> {tk_noop; break}
+ bind .t <Tab> {tk_key_enter "\011"; break}
+ bind .t <Up> {tk_key_enter "k"; break}
+ bind .t <ampersand> {tk_key_enter "&"; break}
+ bind .t <asciicircum> {tk_key_enter "^"; break}
+ bind .t <asciitilde> {tk_key_enter "~"; break}
+ bind .t <asterisk> {tk_key_enter "*"; break}
+ bind .t <at> {tk_key_enter "@"; break}
+ bind .t <backslash> {tk_key_enter "\\"; break}
+ bind .t <bar> {tk_key_enter "|"; break}
+ bind .t <braceleft> {tk_key_enter "{"; break}
+ bind .t <braceright> {tk_key_enter "; break}"}
+ bind .t <bracketleft> {tk_key_enter "\["; break}
+ bind .t <bracketright> {tk_key_enter "]"; break}
+ bind .t <colon> {tk_key_enter ":"; break}
+ bind .t <comma> {tk_key_enter ","; break}
+ bind .t <dollar> {tk_key_enter "$"; break}
+ bind .t <equal> {tk_key_enter "="; break}
+ bind .t <exclam> {tk_key_enter "!"; break}
+ bind .t <greater> {tk_key_enter ">"; break}
+ bind .t <less> {tk_key_enter "<"; break}
+ bind .t <minus> {tk_key_enter "-"; break}
+ bind .t <numbersign> {tk_key_enter "#"; break}
+ bind .t <parenleft> {tk_key_enter "("; break}
+ bind .t <parenright> {tk_key_enter ")"; break}
+ bind .t <percent> {tk_key_enter "%"; break}
+ bind .t <period> {tk_key_enter "."; break}
+ bind .t <plus> {tk_key_enter "+"; break}
+ bind .t <question> {tk_key_enter "?"; break}
+ bind .t <quotedbl> {tk_key_enter "\""; break}
+ bind .t <quoteright> {tk_key_enter "'"; break}
+ bind .t <semicolon> {tk_key_enter ";"; break}
+ bind .t <slash> {tk_key_enter "/"; break}
+ bind .t <space> {tk_key_enter " "; break}
+ bind .t <underscore> {tk_key_enter "_"; break}
+ bind .t A {tk_key_enter "A"; break}
+ bind .t B {tk_key_enter "B"; break}
+ bind .t C {tk_key_enter "C"; break}
+ bind .t D {tk_key_enter "D"; break}
+ bind .t E {tk_key_enter "E"; break}
+ bind .t F {tk_key_enter "F"; break}
+ bind .t G {tk_key_enter "G"; break}
+ bind .t H {tk_key_enter "H"; break}
+ bind .t I {tk_key_enter "I"; break}
+ bind .t J {tk_key_enter "J"; break}
+ bind .t K {tk_key_enter "K"; break}
+ bind .t L {tk_key_enter "L"; break}
+ bind .t M {tk_key_enter "M"; break}
+ bind .t N {tk_key_enter "N"; break}
+ bind .t O {tk_key_enter "O"; break}
+ bind .t P {tk_key_enter "P"; break}
+ bind .t Q {tk_key_enter "Q"; break}
+ bind .t R {tk_key_enter "R"; break}
+ bind .t S {tk_key_enter "S"; break}
+ bind .t T {tk_key_enter "T"; break}
+ bind .t U {tk_key_enter "U"; break}
+ bind .t V {tk_key_enter "V"; break}
+ bind .t W {tk_key_enter "W"; break}
+ bind .t X {tk_key_enter "X"; break}
+ bind .t Y {tk_key_enter "Y"; break}
+ bind .t Z {tk_key_enter "Z"; break}
+ bind .t a {tk_key_enter "a"; break}
+ bind .t b {tk_key_enter "b"; break}
+ bind .t c {tk_key_enter "c"; break}
+ bind .t d {tk_key_enter "d"; break}
+ bind .t e {tk_key_enter "e"; break}
+ bind .t f {tk_key_enter "f"; break}
+ bind .t g {tk_key_enter "g"; break}
+ bind .t h {tk_key_enter "h"; break}
+ bind .t i {tk_key_enter "i"; break}
+ bind .t j {tk_key_enter "j"; break}
+ bind .t k {tk_key_enter "k"; break}
+ bind .t l {tk_key_enter "l"; break}
+ bind .t m {tk_key_enter "m"; break}
+ bind .t n {tk_key_enter "n"; break}
+ bind .t o {tk_key_enter "o"; break}
+ bind .t p {tk_key_enter "p"; break}
+ bind .t q {tk_key_enter "q"; break}
+ bind .t r {tk_key_enter "r"; break}
+ bind .t s {tk_key_enter "s"; break}
+ bind .t t {tk_key_enter "t"; break}
+ bind .t u {tk_key_enter "u"; break}
+ bind .t v {tk_key_enter "v"; break}
+ bind .t w {tk_key_enter "w"; break}
+ bind .t x {tk_key_enter "x"; break}
+ bind .t y {tk_key_enter "y"; break}
+ bind .t z {tk_key_enter "z"; break}
+
+ # XXX
+ # I haven't been able to make Tcl/Tk write uninitialized portions
+ # of the text window. Fill in the screen.
+ tk_ssize
+ .t mark set insert 1.0
+ for {set i 1} {$i <= $tk_ssize_row} {incr i} {
+ for {set j 1} {$j <= $tk_ssize_col} {incr j} {
+ .t insert insert " "
+ }
+ .t insert insert "\n"
+ }
+}
+
+# tk_noop --
+# Do nothing.
+#
+# XXX
+# I can't figure out how to get a binding that does nothing without
+# calling a function, so this stub does it for me.
+proc tk_noop {} {
+}
+
+# tk_key_enter --
+# Enter a key.
+proc tk_key_enter {val} {
+ global newkey
+ global waiting
+
+ set waiting 0
+ tk_key $val
+ set newkey 1
+}
+
+# tk_key_wait --
+# Wait for a key.
+proc tk_key_wait {timeout} {
+ global newkey
+ global waiting
+
+ if { $timeout != 0 } {
+ after $timeout "set newkey 1"
+ }
+ set waiting 1
+ tkwait variable newkey
+}
+
+# Callback functions for the File menu.
+# tk_edit
+# Edit another file.
+proc tk_edit {} {
+}
+
+# tk_quit
+# Quit.
+proc tk_quit {} {
+ global newkey
+ global waiting
+
+ tk_op quit
+ if { $waiting != 0 } {
+ set newkey 1
+ }
+}
+
+# tk_write
+# Write the edit buffer.
+proc tk_write {} {
+ global newkey
+ global waiting
+
+ tk_op write
+ if { $waiting != 0 } {
+ set newkey 1
+ }
+}
+
+# tk_writeas
+# Write the edit buffer to a named file.
+proc tk_writeas {} {
+}
+
+# tk_writequit
+# Write and quit.
+proc tk_writequit {} {
+ global newkey
+ global waiting
+
+ tk_op writequit
+ if { $waiting != 0 } {
+ set newkey 1
+ }
+}
+
+# Callback functions for the Help menu.
+#
+# tk_help --
+# Present a help screen.
+proc tk_help {} {
+ tk_dialog .d {} "No help screen currently available." {} 0 Continue
+}
+
+# tk_options
+# Contains the option selector box. It is divided into three parts, the
+# checkbuttons for the boolean options, the entry fields for the string
+# numeric options, and a control area containing buttons. There is only
+# one function.
+proc tk_options {} {
+
+ # Build option selector box with three subframes for boolean,
+ # numeric, and string options. Make it a toplevel window.
+ toplevel .os
+ wm title .os options
+
+ # Option variables.
+ global tko_altwerase
+ global tko_autoindent
+ global tko_autoprint
+ global tko_autowrite
+ global tko_backup
+ global tko_beautify
+ global tko_cdpath
+ global tko_cedit
+ global tko_columns
+ global tko_comment
+ global tko_directory
+ global tko_edcompatible
+ global tko_escapetime
+ global tko_errorbells
+ global tko_exrc
+ global tko_extended
+ global tko_filec
+ global tko_flash
+ global tko_hardtabs
+ global tko_iclower
+ global tko_ignorecase
+ global tko_keytime
+ global tko_leftright
+ global tko_lines
+ global tko_lisp
+ global tko_list
+ global tko_lock
+ global tko_magic
+ global tko_matchtime
+ global tko_mesg
+ global tko_modeline
+ global tko_msgcat
+ global tko_noprint
+ global tko_number
+ global tko_octal
+ global tko_open
+ global tko_optimize
+ global tko_paragraphs
+ global tko_print
+ global tko_prompt
+ global tko_readonly
+ global tko_recdir
+ global tko_redraw
+ global tko_remap
+ global tko_report
+ global tko_ruler
+ global tko_scroll
+ global tko_searchincr
+ global tko_sections
+ global tko_secure
+ global tko_shell
+ global tko_shellmeta
+ global tko_shiftwidth
+ global tko_showmatch
+ global tko_showmode
+ global tko_sidescroll
+ global tko_slowopen
+ global tko_sourceany
+ global tko_tabstop
+ global tko_taglength
+ global tko_tags
+ global tko_term
+ global tko_terse
+ global tko_tildeop
+ global tko_timeout
+ global tko_ttywerase
+ global tko_verbose
+ global tko_warn
+ global tko_window
+ global tko_windowname
+ global tko_wraplen
+ global tko_wrapmargin
+ global tko_wrapscan
+ global tko_writeany
+
+ # Initialize option values.
+ tk_opt_init
+
+ # Build subframe for boolean options.
+ frame .os.bopts
+
+ # This is the width of the edcompatible button.
+ set buttonwidth 13
+
+ # Pack the boolean os, 5 to a frame.
+ frame .os.bopts.f1
+ pack append .os.bopts .os.bopts.f1 {top}
+ checkbutton .os.bopts.f1.b1 \
+ -variable tko_altwerase -text "altwerase" \
+ -command "tk_opt_set altwerase $tko_altwerase" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f1.b2 \
+ -variable tko_autoindent -text "autoindent" \
+ -command "tk_opt_set autoindent $tko_autoindent" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f1.b3 \
+ -variable tko_autoprint -text "autoprint" \
+ -command "tk_opt_set autoprint $tko_autoprint" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f1.b4 \
+ -variable tko_autowrite -text "autowrite" \
+ -command "tk_opt_set autowrite $tko_autowrite" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f1.b5 \
+ -variable tko_beautify -text "beautify" \
+ -command "tk_opt_set beautify $tko_beautify" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f1 \
+ .os.bopts.f1.b1 {left frame w} \
+ .os.bopts.f1.b2 {left frame w} \
+ .os.bopts.f1.b3 {left frame w} \
+ .os.bopts.f1.b4 {left frame w} \
+ .os.bopts.f1.b5 {left frame w}
+
+ frame .os.bopts.f2
+ pack append .os.bopts .os.bopts.f2 {top}
+ checkbutton .os.bopts.f2.b1 \
+ -variable tko_comment -text "comment" \
+ -command "tk_opt_set comment $tko_comment" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f2.b2 \
+ -variable tko_edcompatible -text "edcompatible" \
+ -command "tk_opt_set edcompatible $tko_edcompatible" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f2.b3 \
+ -variable tko_errorbells -text "errorbells" \
+ -command "tk_opt_set errorbells $tko_errorbells" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f2.b4 \
+ -variable tko_exrc -text "exrc" \
+ -command "tk_opt_set exrc $tko_exrc" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f2.b5 \
+ -variable tko_extended -text "extended" \
+ -command "tk_opt_set extended $tko_extended" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f2 \
+ .os.bopts.f2.b1 {left frame w} \
+ .os.bopts.f2.b2 {left frame w} \
+ .os.bopts.f2.b3 {left frame w} \
+ .os.bopts.f2.b4 {left frame w} \
+ .os.bopts.f2.b5 {left frame w}
+
+ frame .os.bopts.f3
+ pack append .os.bopts .os.bopts.f3 {top}
+ checkbutton .os.bopts.f3.b1 \
+ -variable tko_flash -text "flash" \
+ -command "tk_opt_set flash $tko_flash" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f3.b2 \
+ -variable tko_iclower -text "iclower" \
+ -command "tk_opt_set iclower $tko_iclower" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f3.b3 \
+ -variable tko_ignorecase -text "ignorecase" \
+ -command "tk_opt_set ignorecase $tko_ignorecase" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f3.b4 \
+ -variable tko_leftright -text "leftright" \
+ -command "tk_opt_set leftright $tko_leftright" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f3.b5 \
+ -variable tko_lisp -text "lisp" \
+ -command "tk_opt_set lisp $tko_lisp" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f3 \
+ .os.bopts.f3.b1 {left frame w} \
+ .os.bopts.f3.b2 {left frame w} \
+ .os.bopts.f3.b3 {left frame w} \
+ .os.bopts.f3.b4 {left frame w} \
+ .os.bopts.f3.b5 {left frame w}
+
+ frame .os.bopts.f4
+ pack append .os.bopts .os.bopts.f4 {top}
+ checkbutton .os.bopts.f4.b1 \
+ -variable tko_list -text "list" \
+ -command "tk_opt_set list $tko_list" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f4.b2 \
+ -variable tko_lock -text "lock" \
+ -command "tk_opt_set lock $tko_lock" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f4.b3 \
+ -variable tko_magic -text "magic" \
+ -command "tk_opt_set magic $tko_magic" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f4.b4 \
+ -variable tko_mesg -text "mesg" \
+ -command "tk_opt_set mesg $tko_mesg" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f4.b5\
+ -variable tko_number -text "number" \
+ -command "tk_opt_set number $tko_number" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f4 \
+ .os.bopts.f4.b1 {left frame w} \
+ .os.bopts.f4.b2 {left frame w} \
+ .os.bopts.f4.b3 {left frame w} \
+ .os.bopts.f4.b4 {left frame w} \
+ .os.bopts.f4.b5 {left frame w}
+
+ frame .os.bopts.f5
+ pack append .os.bopts .os.bopts.f5 {top}
+ checkbutton .os.bopts.f5.b1 \
+ -variable tko_octal -text "octal" \
+ -command "tk_opt_set octal $tko_octal" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f5.b2 \
+ -variable tko_open -text "open" \
+ -command "tk_opt_set open $tko_open" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f5.b3 \
+ -variable tko_optimize -text "optimize" \
+ -command "tk_opt_set optimize $tko_optimize" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f5.b4 \
+ -variable tko_prompt -text "prompt" \
+ -command "tk_opt_set prompt $tko_prompt" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f5.b5 \
+ -variable tko_readonly -text "readonly" \
+ -command "tk_opt_set readonly $tko_readonly" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f5 \
+ .os.bopts.f5.b1 {left frame w} \
+ .os.bopts.f5.b2 {left frame w} \
+ .os.bopts.f5.b3 {left frame w} \
+ .os.bopts.f5.b4 {left frame w} \
+ .os.bopts.f5.b5 {left frame w}
+
+ frame .os.bopts.f6
+ pack append .os.bopts .os.bopts.f6 {top}
+ checkbutton .os.bopts.f6.b1 \
+ -variable tko_remap -text "remap" \
+ -command "tk_opt_set remap $tko_remap" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f6.b2 \
+ -variable tko_ruler -text "ruler" \
+ -command "tk_opt_set ruler $tko_ruler" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f6.b3 \
+ -variable tko_searchincr -text "searchincr" \
+ -command "tk_opt_set searchincr $tko_searchincr" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f6.b4 \
+ -variable tko_secure -text "secure" \
+ -command "tk_opt_set secure $tko_secure" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f6.b5 \
+ -variable tko_showmatch -text "showmatch" \
+ -command "tk_opt_set showmatch $tko_showmatch" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f6 \
+ .os.bopts.f6.b1 {left frame w} \
+ .os.bopts.f6.b2 {left frame w} \
+ .os.bopts.f6.b3 {left frame w} \
+ .os.bopts.f6.b4 {left frame w} \
+ .os.bopts.f6.b5 {left frame w}
+
+ frame .os.bopts.f7
+ pack append .os.bopts .os.bopts.f7 {top}
+ checkbutton .os.bopts.f7.b1 \
+ -variable tko_showmode -text "showmode" \
+ -command "tk_opt_set showmode $tko_showmode" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f7.b2 \
+ -variable tko_slowopen -text "slowopen" \
+ -command "tk_opt_set slowopen $tko_slowopen" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f7.b3 \
+ -variable tko_sourceany -text "sourceany" \
+ -command "tk_opt_set sourceany $tko_sourceany" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f7.b4 \
+ -variable tko_terse -text "terse" \
+ -command "tk_opt_set terse $tko_terse" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f7.b5 \
+ -variable tko_tildeop -text "tildeop" \
+ -command "tk_opt_set tildeope $tko_tildeop" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f7 \
+ .os.bopts.f7.b1 {left frame w} \
+ .os.bopts.f7.b2 {left frame w} \
+ .os.bopts.f7.b3 {left frame w} \
+ .os.bopts.f7.b4 {left frame w} \
+ .os.bopts.f7.b5 {left frame w}
+
+ frame .os.bopts.f8
+ pack append .os.bopts .os.bopts.f8 {top fillx}
+ checkbutton .os.bopts.f8.b1 \
+ -variable tko_timeout -text "timeout" \
+ -command "tk_opt_set timeout $tko_timeout" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f8.b2 \
+ -variable tko_ttywerase -text "ttywerase" \
+ -command "tk_opt_set ttywerase $tko_ttywerase" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f8.b3 \
+ -variable tko_verbose -text "verbose" \
+ -command "tk_opt_set verbose $tko_verbose" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f8.b4 \
+ -variable tko_warn -text "warn" \
+ -command "tk_opt_set warn $tko_warn" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f8.b5 \
+ -variable tko_windowname -text "windowname" \
+ -command "tk_opt_set windowname $tko_windowname" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f8 \
+ .os.bopts.f8.b1 {left frame w} \
+ .os.bopts.f8.b2 {left frame w} \
+ .os.bopts.f8.b3 {left frame w} \
+ .os.bopts.f8.b4 {left frame w} \
+ .os.bopts.f8.b5 {left frame w}
+
+ frame .os.bopts.f9
+ pack append .os.bopts .os.bopts.f9 {top fillx}
+ checkbutton .os.bopts.f9.b1 \
+ -variable tko_wrapscan -text "wrapscan" \
+ -command "tk_opt_set wrapscan $tko_wrapscan" \
+ -width $buttonwidth -anchor w
+ checkbutton .os.bopts.f9.b2 \
+ -variable tko_writeany -text "writeany" \
+ -command "tk_opt_set writeany $tko_writeany" \
+ -width $buttonwidth -anchor w
+ pack append .os.bopts.f9 \
+ .os.bopts.f9.b1 {left frame w} \
+ .os.bopts.f9.b2 {left frame w}
+
+ # Build frame for number options:
+ frame .os.nopts
+
+ # Label and entry widths.
+ set lwidth 12
+ set ewidth 3
+
+ frame .os.nopts.n1
+ label .os.nopts.n1.l -text "column:" -width $lwidth -anchor w
+ entry .os.nopts.n1.e -width $ewidth -relief raised \
+ -textvariable tko_columns
+ trace variable tko_columns w tk_opt_ew
+ pack append .os.nopts.n1 \
+ .os.nopts.n1.l {left} .os.nopts.n1.e {left frame w}
+
+ frame .os.nopts.n2
+ label .os.nopts.n2.l -text "escapetime:" -width $lwidth -anchor w
+ entry .os.nopts.n2.e -width $ewidth -textvariable tko_escapetime \
+ -relief raised
+ trace variable tko_escapetime w tk_opt_ew
+ pack append .os.nopts.n2 \
+ .os.nopts.n2.l {left} .os.nopts.n2.e {left frame w}
+
+ frame .os.nopts.n3
+ label .os.nopts.n3.l -text "hardtabs:" -width $lwidth -anchor w
+ entry .os.nopts.n3.e -width $ewidth -textvariable tko_hardtabs \
+ -relief raised
+ trace variable tko_hardtabs w tk_opt_ew
+ pack append .os.nopts.n3 \
+ .os.nopts.n3.l {left} .os.nopts.n3.e {left frame w}
+
+ frame .os.nopts.n4
+ label .os.nopts.n4.l -text "keytime:" -width $lwidth -anchor w
+ entry .os.nopts.n4.e -width $ewidth -textvariable tko_keytime \
+ -relief raised
+ trace variable tko_keytime w tk_opt_ew
+ pack append .os.nopts.n4 \
+ .os.nopts.n4.l {left} .os.nopts.n4.e {left frame w}
+
+ frame .os.nopts.n5
+ label .os.nopts.n5.l -text "lines:" -width $lwidth -anchor w
+ entry .os.nopts.n5.e -width $ewidth -textvariable tko_lines \
+ -relief raised
+ trace variable tko_lines w tk_opt_ew
+ pack append .os.nopts.n5 \
+ .os.nopts.n5.l {left} .os.nopts.n5.e {left frame w}
+
+ frame .os.nopts.n6
+ label .os.nopts.n6.l -text "matchtime:" -width $lwidth -anchor w
+ entry .os.nopts.n6.e -width $ewidth -textvariable tko_matchtime \
+ -relief raised
+ trace variable tko_matchtime w tk_opt_ew
+ pack append .os.nopts.n6 \
+ .os.nopts.n6.l {left} .os.nopts.n6.e {left frame w}
+
+ frame .os.nopts.n7
+ label .os.nopts.n7.l -text "report:" -width $lwidth -anchor w
+ entry .os.nopts.n7.e -width $ewidth -textvariable tko_report \
+ -relief raised
+ trace variable tko_report w tk_opt_ew
+ pack append .os.nopts.n7 \
+ .os.nopts.n7.l {left} .os.nopts.n7.e {left frame w}
+
+ frame .os.nopts.n8
+ label .os.nopts.n8.l -text "scroll:" -width $lwidth -anchor w
+ entry .os.nopts.n8.e -width $ewidth -textvariable tko_scroll \
+ -relief raised
+ trace variable tko_scroll w tk_opt_ew
+ pack append .os.nopts.n8 \
+ .os.nopts.n8.l {left} .os.nopts.n8.e {left frame w}
+
+ frame .os.nopts.n9
+ label .os.nopts.n9.l -text "shiftwidth:" -width $lwidth -anchor w
+ entry .os.nopts.n9.e -width $ewidth -textvariable tko_shiftwidth \
+ -relief raised
+ trace variable tko_shiftwidth w tk_opt_ew
+ pack append .os.nopts.n9 \
+ .os.nopts.n9.l {left} .os.nopts.n9.e {left frame w}
+
+ frame .os.nopts.n10
+ label .os.nopts.n10.l -text "sidescroll:" -width $lwidth -anchor w
+ entry .os.nopts.n10.e -width $ewidth -textvariable tko_sidescroll \
+ -relief raised
+ trace variable tko_sidescroll w tk_opt_ew
+ pack append .os.nopts.n10 \
+ .os.nopts.n10.l {left} .os.nopts.n10.e {left frame w}
+
+ frame .os.nopts.n11
+ label .os.nopts.n11.l -text "tabstop:" -width $lwidth -anchor w
+ entry .os.nopts.n11.e -width $ewidth -textvariable tko_tabstop \
+ -relief raised
+ trace variable tko_tabstop w tk_opt_ew
+ pack append .os.nopts.n11 \
+ .os.nopts.n11.l {left} .os.nopts.n11.e {left frame w}
+
+ frame .os.nopts.n12
+ label .os.nopts.n12.l -text "taglength:" -width $lwidth -anchor w
+ entry .os.nopts.n12.e -width $ewidth -textvariable tko_taglength \
+ -relief raised
+ trace variable tko_taglength w tk_opt_ew
+ pack append .os.nopts.n12 \
+ .os.nopts.n12.l {left} .os.nopts.n12.e {left frame w}
+
+ frame .os.nopts.n13
+ label .os.nopts.n13.l -text "window:" -width $lwidth -anchor w
+ entry .os.nopts.n13.e -width $ewidth -textvariable tko_window \
+ -relief raised
+ trace variable tko_window w tk_opt_ew
+ pack append .os.nopts.n13 \
+ .os.nopts.n13.l {left} .os.nopts.n13.e {left frame w}
+
+ frame .os.nopts.n14
+ label .os.nopts.n14.l -text "wraplen:" -width $lwidth -anchor w
+ entry .os.nopts.n14.e -width $ewidth -textvariable tko_wraplen \
+ -relief raised
+ trace variable tko_wraplen w tk_opt_ew
+ pack append .os.nopts.n14 \
+ .os.nopts.n14.l {left} .os.nopts.n14.e {left frame w}
+
+ frame .os.nopts.n15
+ label .os.nopts.n15.l -text "wrapmargin:" -width $lwidth -anchor w
+ entry .os.nopts.n15.e -width $ewidth -textvariable tko_wrapmargin \
+ -relief raised
+ trace variable tko_wrapmargin w tk_opt_ew
+ pack append .os.nopts.n15 \
+ .os.nopts.n15.l {left} .os.nopts.n15.e {left frame w}
+
+ pack append .os.nopts \
+ .os.nopts.n1 {top fillx} \
+ .os.nopts.n3 {top expand fillx} \
+ .os.nopts.n4 {top expand fillx} \
+ .os.nopts.n5 {top expand fillx} \
+ .os.nopts.n6 {top expand fillx} \
+ .os.nopts.n7 {top expand fillx} \
+ .os.nopts.n8 {top expand fillx} \
+ .os.nopts.n9 {top expand fillx} \
+ .os.nopts.n10 {top expand fillx} \
+ .os.nopts.n11 {top expand fillx} \
+ .os.nopts.n12 {top expand fillx} \
+ .os.nopts.n13 {top expand fillx} \
+ .os.nopts.n14 {top expand fillx} \
+ .os.nopts.n15 {top expand fillx}
+
+ # Build frame for string options
+ frame .os.sopts
+
+ # Entry width.
+ set ewidth 40
+
+ frame .os.sopts.s1
+ label .os.sopts.s1.l -text "backup:" -width $lwidth -anchor w
+ entry .os.sopts.s1.e -width $ewidth -textvariable tko_backup \
+ -relief raised
+ pack append .os.sopts.s1 \
+ .os.sopts.s1.l {left} .os.sopts.s1.e {left frame w}
+
+ frame .os.sopts.s2
+ label .os.sopts.s2.l -text "cdpath:" -width $lwidth -anchor w
+ entry .os.sopts.s2.e -width $ewidth -textvariable tko_cdpath \
+ -relief raised
+ pack append .os.sopts.s2 \
+ .os.sopts.s2.l {left} .os.sopts.s2.e {left frame w}
+
+ frame .os.sopts.s3
+ label .os.sopts.s3.l -text "directory:" -width $lwidth -anchor w
+ entry .os.sopts.s3.e -width $ewidth -textvariable tko_directory \
+ -relief raised
+ pack append .os.sopts.s3 \
+ .os.sopts.s3.l {left} .os.sopts.s3.e {left frame w}
+
+ frame .os.sopts.s4
+ label .os.sopts.s4.l -text "cedit:" -width $lwidth -anchor w
+ entry .os.sopts.s4.e -width $ewidth -textvariable tko_cedit \
+ -relief raised
+ pack append .os.sopts.s4 \
+ .os.sopts.s4.l {left} .os.sopts.s4.e {left frame w}
+
+ frame .os.sopts.s5
+ label .os.sopts.s5.l -text "filec:" -width $lwidth -anchor w
+ entry .os.sopts.s5.e -width $ewidth -textvariable tko_filec \
+ -relief raised
+ pack append .os.sopts.s5 \
+ .os.sopts.s5.l {left} .os.sopts.s5.e {left frame w}
+
+ frame .os.sopts.s6
+ label .os.sopts.s6.l -text "msgcat:" -width $lwidth -anchor w
+ entry .os.sopts.s6.e -width $ewidth -textvariable tko_msgcat \
+ -relief raised
+ pack append .os.sopts.s6 \
+ .os.sopts.s6.l {left} .os.sopts.s6.e {left frame w}
+
+ frame .os.sopts.s7
+ label .os.sopts.s7.l -text "noprint:" -width $lwidth -anchor w
+ entry .os.sopts.s7.e -width $ewidth -textvariable tko_noprint \
+ -relief raised
+ pack append .os.sopts.s7 \
+ .os.sopts.s7.l {left} .os.sopts.s7.e {left frame w}
+
+ frame .os.sopts.s8
+ label .os.sopts.s8.l -text "paragraphs:" -width $lwidth -anchor w
+ entry .os.sopts.s8.e -width $ewidth -textvariable tko_paragraphs \
+ -relief raised
+ pack append .os.sopts.s8 \
+ .os.sopts.s8.l {left} .os.sopts.s8.e {left frame w}
+
+ frame .os.sopts.s9
+ label .os.sopts.s9.l -text "print:" -width $lwidth -anchor w
+ entry .os.sopts.s9.e -width $ewidth -textvariable tko_print \
+ -relief raised
+ pack append .os.sopts.s9 \
+ .os.sopts.s9.l {left} .os.sopts.s9.e {left frame w}
+
+ frame .os.sopts.s10
+ label .os.sopts.s10.l -text "recdir:" -width $lwidth -anchor w
+ entry .os.sopts.s10.e -width $ewidth -textvariable tko_recdir \
+ -relief raised
+ pack append .os.sopts.s10 \
+ .os.sopts.s10.l {left} .os.sopts.s10.e {left frame w}
+
+ frame .os.sopts.s11
+ label .os.sopts.s11.l -text "sections:" -width $lwidth -anchor w
+ entry .os.sopts.s11.e -width $ewidth -textvariable tko_sections \
+ -relief raised
+ pack append .os.sopts.s11 \
+ .os.sopts.s11.l {left} .os.sopts.s11.e {left frame w}
+
+ frame .os.sopts.s12
+ label .os.sopts.s12.l -text "shell:" -width $lwidth -anchor w
+ entry .os.sopts.s12.e -width $ewidth -textvariable tko_shell \
+ -relief raised
+ pack append .os.sopts.s12 \
+ .os.sopts.s12.l {left} .os.sopts.s12.e {left frame w}
+
+ frame .os.sopts.s13
+ label .os.sopts.s13.l -text "shellmeta:" -width $lwidth -anchor w
+ entry .os.sopts.s13.e -width $ewidth -textvariable tko_shellmeta \
+ -relief raised
+ pack append .os.sopts.s13 \
+ .os.sopts.s13.l {left} .os.sopts.s13.e {left frame w}
+
+ frame .os.sopts.s14
+ label .os.sopts.s14.l -text "tags:" -width $lwidth -anchor w
+ entry .os.sopts.s14.e -width $ewidth -textvariable tko_tags \
+ -relief raised
+ pack append .os.sopts.s14 \
+ .os.sopts.s14.l {left} .os.sopts.s14.e {left frame w}
+
+ frame .os.sopts.s15
+ label .os.sopts.s15.l -text "term:" -width $lwidth -anchor w
+ entry .os.sopts.s15.e -width $ewidth -textvariable tko_term \
+ -relief raised
+ pack append .os.sopts.s15 \
+ .os.sopts.s15.l {left} .os.sopts.s15.e {left frame w}
+
+ pack append .os.sopts \
+ .os.sopts.s1 {top expand fillx} \
+ .os.sopts.s2 {top expand fillx} \
+ .os.sopts.s3 {top expand fillx} \
+ .os.sopts.s4 {top expand fillx} \
+ .os.sopts.s5 {top expand fillx} \
+ .os.sopts.s6 {top expand fillx} \
+ .os.sopts.s7 {top expand fillx} \
+ .os.sopts.s8 {top expand fillx} \
+ .os.sopts.s9 {top expand fillx} \
+ .os.sopts.s10 {top expand fillx} \
+ .os.sopts.s11 {top expand fillx} \
+ .os.sopts.s12 {top expand fillx} \
+ .os.sopts.s13 {top expand fillx} \
+ .os.sopts.s14 {top expand fillx} \
+ .os.sopts.s15 {top expand fillx}
+
+ # Build frame for continue button.
+ frame .os.control -bd 4
+ button .os.control.quit -text "Continue" -command "destroy .os"
+ bind .os <Return> ".os.control.quit flash; destroy .os"
+ pack append .os.control .os.control.quit {left}
+
+ # Pack everything together.
+ pack append .os \
+ .os.bopts {top} \
+ .os.control {bottom fillx} \
+ .os.nopts {left fillx padx 4m pady 4m} \
+ .os.sopts {left fillx pady 4m}
+
+ grab .os
+ focus .os
+}
+
+# tk_opt_ew --
+# Handle a change to an option entry widget.
+proc tk_opt_ew {name element op} {
+ upvar $name x
+ tk_opt_set "$name=$x"
+}
+
+# tk_err --
+# Display a Tcl/Tk error message.
+proc tk_err {msg} {
+ tk_dialog .d {} "$msg" {} 0 Continue
+
+ #puts "msg: $msg"
+}
+
+# tk_addstr --
+# Add a string to the screen.
+proc tk_addstr {len str} {
+ global tk_cursor_row
+ global tk_cursor_col
+
+ # Delete the current characters, then insert the new ones.
+ .t mark set insert $tk_cursor_row.$tk_cursor_col
+ .t delete insert "insert + $len chars"
+ .t insert insert "$str"
+ incr tk_cursor_col $len
+
+ #puts "tk_addstr: row $tk_cursor_row col $tk_cursor_col: insert $str"
+}
+
+# tk_clrtoeol --
+# Clear to the end of the line.
+proc tk_clrtoeol {} {
+ global tk_cursor_row
+ global tk_cursor_col
+ global tk_ssize_col
+
+ # Overwrite to the end of the line with spaces.
+ .t mark set insert $tk_cursor_row.$tk_cursor_col
+ .t delete insert "insert lineend"
+ for {set j $tk_cursor_col} {$j < $tk_ssize_col} {incr j} {
+ .t insert insert " "
+ }
+
+ #puts "tk_clrtoel: row $tk_cursor_row col $tk_cursor_col"
+}
+
+# tk_deleteln --
+# Delete the line.
+proc tk_deleteln {} {
+ global tk_cursor_row
+ global tk_cursor_col
+ global tk_ssize_col
+
+ # Delete the line.
+ .t mark set insert $tk_cursor_row.$tk_cursor_col
+ .t delete insert "insert lineend + 1 chars"
+
+ # Append a new, blank line at the end of the screen.
+ .t mark set insert end
+ for {set j 1} {$j <= $tk_ssize_col} {incr j} {
+ .t insert insert " "
+ }
+ .t insert insert "\n"
+
+ #puts "tk_deleteln: row $tk_cursor_row"
+}
+
+# tk_flash --
+# Flash the screen.
+proc tk_flash {} {
+ set bg [lindex [.t config -background] 4]
+ set fg [lindex [.t config -foreground] 4]
+ .t configure -background $fg -foreground $bg
+ update idletasks
+ .t configure -background $bg -foreground $fg
+ update idletasks
+}
+
+# tk_insertln --
+# Insert the line.
+proc tk_insertln {} {
+ global tk_cursor_row
+ global tk_cursor_col
+ global tk_ssize_row
+ global tk_ssize_col
+
+ # Delete the last line on the screen.
+ .t mark set insert $tk_ssize_row.0
+ .t delete insert "insert lineend + 1 chars"
+
+ # Insert a new, blank line.
+ .t mark set insert $tk_cursor_row.$tk_cursor_col
+ for {set j 1} {$j <= $tk_ssize_col} {incr j} {
+ .t insert insert " "
+ }
+ .t insert insert "\n"
+
+ #puts "tk_insertln: row $tk_cursor_row"
+}
+
+# tk_move --
+# Move the cursor.
+proc tk_move {row col} {
+ global tk_cursor_row
+ global tk_cursor_col
+
+ # Convert to Tcl/Tk coordinates, update the insert cursor.
+ set tk_cursor_row [ expr $row + 1 ]
+ set tk_cursor_col $col
+ .t mark set insert $tk_cursor_row.$tk_cursor_col
+
+ # Update the screen cursor.
+ .t tag remove tk_cursor tk_cursor_indx
+ .t mark set tk_cursor_indx insert
+ .t tag add tk_cursor tk_cursor_indx
+
+ #puts "tk_move: row $tk_cursor_row col $tk_cursor_col"
+}
+
+# tk_rename --
+# Rename the screen.
+proc tk_rename {name} {
+ wm title . "$name"
+}
+
+# tk_ssize --
+# Return the window size.
+proc tk_ssize {} {
+ global tk_ssize_col
+ global tk_ssize_row
+
+ set s [ .t configure -width ]
+ set tk_ssize_col [ lindex $s [ expr [ llength $s ] -1 ] ]
+ set s [ .t configure -height ]
+ set tk_ssize_row [ lindex $s [ expr [ llength $s ] -1 ] ]
+
+ #puts "tk_ssize: rows $tk_ssize_row, cols $tk_ssize_col"
+}
+
+# tk_standout --
+# Change into standout mode.
+proc tk_standout {} {
+}
+
+# tk_standend --
+# Change out of standout mode.
+proc tk_standend {} {
+}
+
+# Cursor
+set tk_cursor_row 1
+set tk_cursor_col 0
+
+# Screen size
+set tk_ssize_row 0
+set tk_ssize_col 0
+
+screen
+#tkwait window .
diff --git a/contrib/nvi/tk/tk_funcs.c b/contrib/nvi/tk/tk_funcs.c
new file mode 100644
index 000000000000..be2b0d96ea09
--- /dev/null
+++ b/contrib/nvi/tk/tk_funcs.c
@@ -0,0 +1,346 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_funcs.c 8.11 (Berkeley) 9/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../vi/vi.h"
+#include "tki.h"
+
+/*
+ * tk_addstr --
+ * Add len bytes from the string at the cursor, advancing the cursor.
+ *
+ * PUBLIC: int tk_addstr __P((SCR *, const char *, size_t));
+ */
+int
+tk_addstr(sp, str, len)
+ SCR *sp;
+ const char *str;
+ size_t len;
+{
+ TK_PRIVATE *tkp;
+ int iv;
+ char buf[20];
+
+ iv = 0;
+
+ tkp = TKP(sp);
+ if (iv)
+ (void)Tcl_Eval(tkp->interp, "tk_standout");
+
+ (void)snprintf(buf, sizeof(buf), "%d ", (int)len);
+ if ((Tcl_VarEval(tkp->interp,
+ "tk_addstr ", buf, "{", str, "}", NULL) != TCL_OK))
+ return (1);
+
+ if (iv)
+ (void)Tcl_Eval(tkp->interp, "tk_standend");
+ return (0);
+}
+
+/*
+ * tk_attr --
+ * Toggle a screen attribute on/off.
+ *
+ * PUBLIC: int tk_attr __P((SCR *, scr_attr_t, int));
+ */
+int
+tk_attr(sp, attribute, on)
+ SCR *sp;
+ scr_attr_t attribute;
+ int on;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ switch (attribute) {
+ case SA_ALTERNATE: /* No alternate screen. */
+ break;
+ case SA_INVERSE:
+ if (on)
+ (void)Tcl_Eval(tkp->interp, "tk_standout");
+ else
+ (void)Tcl_Eval(tkp->interp, "tk_standend");
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * tk_baud --
+ * Return the baud rate.
+ *
+ * PUBLIC: int tk_baud __P((SCR *, u_long *));
+ */
+int
+tk_baud(sp, ratep)
+ SCR *sp;
+ u_long *ratep;
+{
+ *ratep = 9600;
+ return (0);
+}
+
+/*
+ * tk_bell --
+ * Ring the bell/flash the screen.
+ *
+ * PUBLIC: int tk_bell __P((SCR *));
+ */
+int
+tk_bell(sp)
+ SCR *sp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ return (Tcl_Eval(tkp->interp, "tk_flash") != TCL_OK);
+}
+
+/*
+ * tk_clrtoeol --
+ * Clear from the current cursor to the end of the line.
+ *
+ * PUBLIC: int tk_clrtoeol __P((SCR *));
+ */
+int
+tk_clrtoeol(sp)
+ SCR *sp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ return (Tcl_Eval(tkp->interp, "tk_clrtoeol") != TCL_OK);
+}
+
+/*
+ * tk_cursor --
+ * Return the current cursor position.
+ *
+ * PUBLIC: int tk_cursor __P((SCR *, size_t *, size_t *));
+ */
+int
+tk_cursor(sp, yp, xp)
+ SCR *sp;
+ size_t *yp, *xp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ *yp = (tkp->tk_cursor_row - 1) - sp->woff;
+ *xp = tkp->tk_cursor_col;
+ return (0);
+}
+
+/*
+ * tk_deleteln --
+ * Delete the current line, scrolling all lines below it.
+ *
+ * PUBLIC: int tk_deleteln __P((SCR *));
+ */
+int
+tk_deleteln(sp)
+ SCR *sp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ return (Tcl_Eval(tkp->interp, "tk_deleteln") != TCL_OK);
+}
+
+/*
+ * tk_ex_adjust --
+ * Adjust the screen for ex.
+ *
+ * PUBLIC: int tk_ex_adjust __P((SCR *, exadj_t));
+ */
+int
+tk_ex_adjust(sp, action)
+ SCR *sp;
+ exadj_t action;
+{
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * tk_insertln --
+ * Push down the current line, discarding the bottom line.
+ *
+ * PUBLIC: int tk_insertln __P((SCR *));
+ */
+int
+tk_insertln(sp)
+ SCR *sp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ return (Tcl_Eval(tkp->interp, "tk_insertln") != TCL_OK);
+}
+
+/*
+ * tk_keyval --
+ * Return the value for a special key.
+ *
+ * PUBLIC: int tk_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
+ */
+int
+tk_keyval(sp, val, chp, dnep)
+ SCR *sp;
+ scr_keyval_t val;
+ CHAR_T *chp;
+ int *dnep;
+{
+ TK_PRIVATE *tkp;
+
+ /*
+ * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
+ * VWERASE is a 4BSD extension.
+ */
+ tkp = TKP(sp);
+ switch (val) {
+ case KEY_VEOF:
+ *dnep = (*chp = tkp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
+ break;
+ case KEY_VERASE:
+ *dnep = (*chp = tkp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
+ break;
+ case KEY_VKILL:
+ *dnep = (*chp = tkp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
+ break;
+#ifdef VWERASE
+ case KEY_VWERASE:
+ *dnep = (*chp = tkp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
+ break;
+#endif
+ default:
+ *dnep = 1;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * tk_move --
+ * Move the cursor.
+ *
+ * PUBLIC: int tk_move __P((SCR *, size_t, size_t));
+ */
+int
+tk_move(sp, lno, cno)
+ SCR *sp;
+ size_t lno, cno;
+{
+ TK_PRIVATE *tkp;
+ char buf[40];
+
+ (void)snprintf(buf, sizeof(buf), "%d %d", RLNO(sp, lno), cno);
+
+ tkp = TKP(sp);
+ return (Tcl_VarEval(tkp->interp, "tk_move ", buf, NULL) != TCL_OK);
+}
+
+/*
+ * tk_refresh --
+ * Refresh the screen.
+ *
+ * PUBLIC: int tk_refresh __P((SCR *, int));
+ */
+int
+tk_refresh(sp, repaint)
+ SCR *sp;
+ int repaint;
+{
+ TK_PRIVATE *tkp;
+
+ /*
+ * If repaint is set, the editor is telling us that we don't know
+ * what's on the screen, so we have to repaint from scratch.
+ *
+ * XXX
+ * I have no idea how to do this in Tk. My guess is that we have
+ * to delete all of the text and call the editor with an E_REPAINT
+ * event.
+ */
+ if (repaint) {
+ }
+
+ tkp = TKP(sp);
+ return (Tcl_Eval(tkp->interp, "update idletasks") != TCL_OK);
+}
+
+/*
+ * tk_rename --
+ * Rename the file.
+ *
+ * PUBLIC: int tk_rename __P((SCR *));
+ */
+int
+tk_rename(sp)
+ SCR *sp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+ return (Tcl_VarEval(tkp->interp,
+ "tk_rename ", sp->frp->name, NULL) != TCL_OK);
+}
+
+/*
+ * tk_suspend --
+ * Suspend a screen.
+ *
+ * PUBLIC: int tk_suspend __P((SCR *, int *));
+ */
+int
+tk_suspend(sp, allowedp)
+ SCR *sp;
+ int *allowedp;
+{
+ *allowedp = 0;
+ return (0);
+}
+
+/*
+ * tk_usage --
+ * Print out the Tk/Tcl usage messages.
+ *
+ * PUBLIC: void tk_usage __P((void));
+ */
+void
+tk_usage()
+{
+#define USAGE "\
+usage: tkvi [-eFlRrSv] [-c command] [-bg color] [-fg color]\n\
+ [-geometry widthxheight+x+y] [-i script] [-t tag] [-w size]\n\
+ [file ...]\n"
+ (void)fprintf(stderr, "%s", USAGE);
+#undef USAGE
+}
diff --git a/contrib/nvi/tk/tk_main.c b/contrib/nvi/tk/tk_main.c
new file mode 100644
index 000000000000..c2f34e7b2d9f
--- /dev/null
+++ b/contrib/nvi/tk/tk_main.c
@@ -0,0 +1,423 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_main.c 8.18 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tki.h"
+#include "pathnames.h"
+
+GS *__global_list; /* GLOBAL: List of screens. */
+sigset_t __sigblockset; /* GLOBAL: Blocked signals. */
+
+static GS *gs_init __P((char *));
+static void killsig __P((SCR *));
+static void perr __P((char *, char *));
+static void sig_end __P((GS *));
+static int sig_init __P((GS *));
+static int tcl_init __P((GS *));
+static void tcl_err __P((TK_PRIVATE *));
+
+/*
+ * main --
+ * This is the main loop for the standalone Tcl/Tk editor.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static int reenter;
+ GS *gp;
+ TK_PRIVATE *tkp;
+ size_t rows, cols;
+ int rval;
+ char **p_av, **t_av, *script;
+
+ /* If loaded at 0 and jumping through a NULL pointer, stop. */
+ if (reenter++)
+ abort();
+
+ /* Create and initialize the global structure. */
+ __global_list = gp = gs_init(argv[0]);
+
+ /* Initialize Tk/Tcl. */
+ if (tcl_init(gp))
+ exit (1);
+
+ /*
+ * Strip out any arguments that the common editor doesn't understand
+ * (i.e. the Tk/Tcl arguments). Search for -i first, it's the Tk/Tcl
+ * startup script and needs to be run first.
+ *
+ * XXX
+ * There's no way to portably call getopt twice.
+ */
+ script = "init.tcl";
+ for (p_av = t_av = argv;;) {
+ if (*t_av == NULL) {
+ *p_av = NULL;
+ break;
+ }
+ if (!strcmp(*t_av, "--")) {
+ while ((*p_av++ = *t_av++) != NULL);
+ break;
+ }
+ if (!memcmp(*t_av, "-i", sizeof("-i") - 1)) {
+ if (t_av[0][2] != '\0') {
+ script = t_av[0] + 2;
+ ++t_av;
+ --argc;
+ continue;
+ }
+ if (t_av[1] != NULL) {
+ script = t_av[1];
+ t_av += 2;
+ argc -= 2;
+ continue;
+ }
+ }
+ *p_av++ = *t_av++;
+ }
+ for (p_av = t_av = argv;;) {
+ if (*t_av == NULL) {
+ *p_av = NULL;
+ break;
+ }
+ if (!strcmp(*t_av, "--")) {
+ while ((*p_av++ = *t_av++) != NULL);
+ break;
+ }
+ if (t_av[1] != NULL &&
+ (!memcmp(*t_av, "-background", sizeof("-background") - 1) ||
+ !memcmp(*t_av, "-bg", sizeof("-bg") - 1) ||
+ !memcmp(*t_av, "-borderwidth", sizeof("-borderwidth") - 1)||
+ !memcmp(*t_av, "-bd", sizeof("-bd") - 1) ||
+ !memcmp(*t_av, "-foreground", sizeof("-foreground") - 1) ||
+ !memcmp(*t_av, "-fg", sizeof("-fg") - 1) ||
+ !memcmp(*t_av, "-font", sizeof("-font") - 1))) {
+ if (Tcl_VarEval(tkp->interp, ".t configure ",
+ t_av[0], " ", t_av[1], NULL) == TCL_ERROR)
+ tcl_err(tkp);
+ t_av += 2;
+ argc -= 2;
+ continue;
+ }
+ if (!memcmp(*t_av, "-geometry", sizeof("-geometry") - 1)) {
+ if (Tcl_VarEval(tkp->interp, "wm geometry . ",
+ *t_av + sizeof("-geometry") - 1, NULL) == TCL_ERROR)
+ tcl_err(tkp);
+ ++t_av;
+ --argc;
+ continue;
+ }
+ *p_av++ = *t_av++;
+ }
+
+ /* Load the initial Tcl/Tk script. */
+ tkp = GTKP(gp);
+ if (Tcl_EvalFile(tkp->interp, script) == TCL_ERROR)
+ tcl_err(tkp);
+
+ /* Add the terminal type to the global structure. */
+ if ((OG_D_STR(gp, GO_TERM) =
+ OG_STR(gp, GO_TERM) = strdup("tkterm")) == NULL)
+ perr(gp->progname, NULL);
+
+ /* Figure out how big the screen is. */
+ if (tk_ssize(NULL, 0, &rows, &cols, NULL))
+ exit (1);
+
+ /* Add the rows and columns to the global structure. */
+ OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
+ OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
+
+ /* Start catching signals. */
+ if (sig_init(gp))
+ exit (1);
+
+ /* Run ex/vi. */
+ rval = editor(gp, argc, argv);
+
+ /* Clean up signals. */
+ sig_end(gp);
+
+ /* Clean up the terminal. */
+ (void)tk_quit(gp);
+
+ /* If a killer signal arrived, pretend we just got it. */
+ if (tkp->killersig) {
+ (void)signal(tkp->killersig, SIG_DFL);
+ (void)kill(getpid(), tkp->killersig);
+ /* NOTREACHED */
+ }
+
+ /* Free the global and TK private areas. */
+#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
+ free(tkp);
+ free(gp);
+#endif
+
+ exit (rval);
+}
+
+/*
+ * gs_init --
+ * Create and partially initialize the GS structure.
+ */
+static GS *
+gs_init(name)
+ char *name;
+{
+ TK_PRIVATE *tkp;
+ GS *gp;
+ int fd;
+ char *p;
+
+ /* Figure out what our name is. */
+ if ((p = strrchr(name, '/')) != NULL)
+ name = p + 1;
+
+ /* Allocate the global structure. */
+ CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
+
+ /* Allocate the CL private structure. */
+ if (gp != NULL)
+ CALLOC_NOMSG(NULL, tkp, TK_PRIVATE *, 1, sizeof(TK_PRIVATE));
+ if (gp == NULL || tkp == NULL)
+ perr(name, NULL);
+ gp->tk_private = tkp;
+ TAILQ_INIT(&tkp->evq);
+
+ /* Initialize the list of curses functions. */
+ gp->scr_addstr = tk_addstr;
+ gp->scr_attr = tk_attr;
+ gp->scr_baud = tk_baud;
+ gp->scr_bell = tk_bell;
+ gp->scr_busy = NULL;
+ gp->scr_clrtoeol = tk_clrtoeol;
+ gp->scr_cursor = tk_cursor;
+ gp->scr_deleteln = tk_deleteln;
+ gp->scr_event = tk_event;
+ gp->scr_ex_adjust = tk_ex_adjust;
+ gp->scr_fmap = tk_fmap;
+ gp->scr_insertln = tk_insertln;
+ gp->scr_keyval = tk_keyval;
+ gp->scr_move = tk_move;
+ gp->scr_msg = NULL;
+ gp->scr_optchange = tk_optchange;
+ gp->scr_refresh = tk_refresh;
+ gp->scr_rename = tk_rename;
+ gp->scr_screen = tk_screen;
+ gp->scr_suspend = tk_suspend;
+ gp->scr_usage = tk_usage;
+
+ /*
+ * We expect that if we've lost our controlling terminal that the
+ * open() (but not the tcgetattr()) will fail.
+ */
+ if (isatty(STDIN_FILENO)) {
+ if (tcgetattr(STDIN_FILENO, &tkp->orig) == -1)
+ goto tcfail;
+ } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
+ if (tcgetattr(fd, &tkp->orig) == -1)
+tcfail: perr(name, "tcgetattr");
+ (void)close(fd);
+ }
+
+ gp->progname = name;
+ return (gp);
+}
+
+/*
+ * tcl_init --
+ * Get Tcl/Tk up and running.
+ */
+static int
+tcl_init(gp)
+ GS *gp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = GTKP(gp);
+ if ((tkp->interp = Tcl_CreateInterp()) == NULL)
+ tcl_err(tkp);
+ /* XXX: Tk 4.1 has an incompatible change. */
+#if (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION == 0)
+ if (Tk_CreateMainWindow(tkp->interp, NULL, "vi", "Vi") == NULL)
+ tcl_err(tkp);
+#endif
+ if (Tcl_Init(tkp->interp) == TCL_ERROR)
+ tcl_err(tkp);
+ if (Tk_Init(tkp->interp) == TCL_ERROR)
+ tcl_err(tkp);
+
+ /* Shared variables. */
+ (void)Tcl_LinkVar(tkp->interp,
+ "tk_cursor_row", (char *)&tkp->tk_cursor_row, TCL_LINK_INT);
+ (void)Tcl_LinkVar(tkp->interp,
+ "tk_cursor_col", (char *)&tkp->tk_cursor_col, TCL_LINK_INT);
+ (void)Tcl_LinkVar(tkp->interp,
+ "tk_ssize_row", (char *)&tkp->tk_ssize_row, TCL_LINK_INT);
+ (void)Tcl_LinkVar(tkp->interp,
+ "tk_ssize_col", (char *)&tkp->tk_ssize_col, TCL_LINK_INT);
+
+ /* Functions called by Tcl script. */
+ Tcl_CreateCommand(tkp->interp, "tk_key", tk_key, tkp, NULL);
+ Tcl_CreateCommand(tkp->interp, "tk_op", tk_op, tkp, NULL);
+ Tcl_CreateCommand(tkp->interp, "tk_opt_init", tk_opt_init, tkp, NULL);
+ Tcl_CreateCommand(tkp->interp, "tk_opt_set", tk_opt_set, tkp, NULL);
+ Tcl_CreateCommand(tkp->interp, "tk_version", tk_version, tkp, NULL);
+
+ /* Other initialization. */
+ if (Tcl_Eval(tkp->interp, "wm geometry . =80x28+0+0") == TCL_ERROR)
+ tcl_err(tkp);
+ return (0);
+}
+
+/*
+ * tcl_err --
+ * Tcl/Tk error message during initialization.
+ */
+static void
+tcl_err(tkp)
+ TK_PRIVATE *tkp;
+{
+ (void)fprintf(stderr, "%s\n", tkp->interp->result != NULL ?
+ tkp->interp->result : "Tcl/Tk: initialization error");
+ (void)tk_usage();
+ exit (1);
+}
+
+#define GLOBAL_TKP \
+ TK_PRIVATE *tkp = GTKP(__global_list);
+static void
+h_hup(signo)
+ int signo;
+{
+ GLOBAL_TKP;
+
+ F_SET(tkp, TK_SIGHUP);
+ tkp->killersig = SIGHUP;
+}
+
+static void
+h_int(signo)
+ int signo;
+{
+ GLOBAL_TKP;
+
+ F_SET(tkp, TK_SIGINT);
+}
+
+static void
+h_term(signo)
+ int signo;
+{
+ GLOBAL_TKP;
+
+ F_SET(tkp, TK_SIGTERM);
+ tkp->killersig = SIGTERM;
+}
+
+static void
+h_winch(signo)
+ int signo;
+{
+ GLOBAL_TKP;
+
+ F_SET(tkp, TK_SIGWINCH);
+}
+#undef GLOBAL_TKP
+
+/*
+ * sig_init --
+ * Initialize signals.
+ */
+static int
+sig_init(gp)
+ GS *gp;
+{
+ TK_PRIVATE *tkp;
+ struct sigaction act;
+
+ tkp = GTKP(gp);
+
+ (void)sigemptyset(&__sigblockset);
+
+ /*
+ * Use sigaction(2), not signal(3), since we don't always want to
+ * restart system calls. The example is when waiting for a command
+ * mode keystroke and SIGWINCH arrives. Besides, you can't portably
+ * restart system calls (thanks, POSIX!).
+ */
+#define SETSIG(signal, handler, off) { \
+ if (sigaddset(&__sigblockset, signal)) \
+ goto err; \
+ act.sa_handler = handler; \
+ sigemptyset(&act.sa_mask); \
+ act.sa_flags = 0; \
+ if (sigaction(signal, &act, &tkp->oact[off])) \
+ goto err; \
+}
+#undef SETSIG
+ return (0);
+
+err: perr(gp->progname, NULL);
+ return (1);
+}
+
+/*
+ * sig_end --
+ * End signal setup.
+ */
+static void
+sig_end(gp)
+ GS *gp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = GTKP(gp);
+ (void)sigaction(SIGHUP, NULL, &tkp->oact[INDX_HUP]);
+ (void)sigaction(SIGINT, NULL, &tkp->oact[INDX_INT]);
+ (void)sigaction(SIGTERM, NULL, &tkp->oact[INDX_TERM]);
+ (void)sigaction(SIGWINCH, NULL, &tkp->oact[INDX_WINCH]);
+}
+
+/*
+ * perr --
+ * Print system error.
+ */
+static void
+perr(name, msg)
+ char *name, *msg;
+{
+ (void)fprintf(stderr, "%s:", name);
+ if (msg != NULL)
+ (void)fprintf(stderr, "%s:", msg);
+ (void)fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+}
diff --git a/contrib/nvi/tk/tk_read.c b/contrib/nvi/tk/tk_read.c
new file mode 100644
index 000000000000..b5cfab71c7e2
--- /dev/null
+++ b/contrib/nvi/tk/tk_read.c
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_read.c 8.12 (Berkeley) 9/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "../ex/script.h"
+#include "tki.h"
+
+static input_t tk_read __P((SCR *, int));
+static int tk_resize __P((SCR *, size_t, size_t));
+
+
+/*
+ * tk_event --
+ * Return a single event.
+ *
+ * PUBLIC: int tk_event __P((SCR *, EVENT *, u_int32_t, int));
+ */
+int
+tk_event(sp, evp, flags, timeout)
+ SCR *sp;
+ EVENT *evp;
+ u_int32_t flags;
+ int timeout;
+{
+ EVENT *tevp;
+ TK_PRIVATE *tkp;
+ size_t lines, columns;
+ int changed;
+
+ /*
+ * Queue signal based events. We never clear SIGHUP or SIGTERM events,
+ * so that we just keep returning them until the editor dies.
+ */
+ tkp = TKP(sp);
+sig: if (LF_ISSET(EC_INTERRUPT) || F_ISSET(tkp, TK_SIGINT)) {
+ if (F_ISSET(tkp, TK_SIGINT)) {
+ F_CLR(tkp, TK_SIGINT);
+ evp->e_event = E_INTERRUPT;
+ } else
+ evp->e_event = E_TIMEOUT;
+ return (0);
+ }
+ if (F_ISSET(tkp, TK_SIGHUP | TK_SIGTERM | TK_SIGWINCH)) {
+ if (F_ISSET(tkp, TK_SIGHUP)) {
+ evp->e_event = E_SIGHUP;
+ return (0);
+ }
+ if (F_ISSET(tkp, TK_SIGTERM)) {
+ evp->e_event = E_SIGTERM;
+ return (0);
+ }
+ if (F_ISSET(tkp, TK_SIGWINCH)) {
+ F_CLR(tkp, TK_SIGWINCH);
+ (void)tk_ssize(sp, 1, &lines, &columns, &changed);
+ if (changed) {
+ (void)tk_resize(sp, lines, columns);
+ evp->e_event = E_WRESIZE;
+ return (0);
+ }
+ /* No change, so ignore the signal. */
+ }
+ }
+
+ /* Queue special ops. */
+ops: if ((tevp = tkp->evq.tqh_first) != NULL) {
+ *evp = *tevp;
+ TAILQ_REMOVE(&tkp->evq, tevp, q);
+ free(tevp);
+ return (0);
+ }
+
+ /* Read input characters. */
+ switch (tk_read(sp, timeout)) {
+ case INP_OK:
+ evp->e_csp = tkp->ibuf;
+ evp->e_len = tkp->ibuf_cnt;
+ evp->e_event = E_STRING;
+ tkp->ibuf_cnt = 0;
+ break;
+ case INP_EOF:
+ evp->e_event = E_EOF;
+ break;
+ case INP_ERR:
+ evp->e_event = E_ERR;
+ break;
+ case INP_INTR:
+ goto sig;
+ break;
+ case INP_TIMEOUT:
+ /* May have returned because queued a special op. */
+ if (tkp->evq.tqh_first != NULL)
+ goto ops;
+
+ /* Otherwise, we timed out. */
+ evp->e_event = E_TIMEOUT;
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * tk_read --
+ * Read characters from the input.
+ */
+static input_t
+tk_read(sp, timeout)
+ SCR *sp;
+ int timeout;
+{
+ TK_PRIVATE *tkp;
+ char buf[20];
+
+ /*
+ * Check scripting window file descriptors. It's ugly that we wait
+ * on scripting file descriptors here, but it's the only way to keep
+ * from locking out scripting windows.
+ */
+ if (F_ISSET(sp->gp, G_SCRWIN) && sscr_input(sp))
+ return (INP_ERR);
+
+ /* Read characters. */
+ tkp = TKP(sp);
+ (void)snprintf(buf, sizeof(buf), "%d", timeout);
+ (void)Tcl_VarEval(tkp->interp, "tk_key_wait ", buf, NULL);
+
+ return (tkp->ibuf_cnt == 0 ? INP_TIMEOUT : INP_OK);
+}
+
+/*
+ * tk_key --
+ * Receive an input key.
+ *
+ * PUBLIC: int tk_key __P((ClientData, Tcl_Interp *, int, char *[]));
+ */
+int
+tk_key(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ TK_PRIVATE *tkp;
+ u_int8_t *p, *t;
+
+ tkp = (TK_PRIVATE *)clientData;
+ for (p =
+ tkp->ibuf + tkp->ibuf_cnt, t = argv[1]; (*p++ = *t++) != '\0';
+ ++tkp->ibuf_cnt);
+ return (TCL_OK);
+}
+
+/*
+ * tk_resize --
+ * Reset the options for a resize event.
+ */
+static int
+tk_resize(sp, lines, columns)
+ SCR *sp;
+ size_t lines, columns;
+{
+ ARGS *argv[2], a, b;
+ int rval;
+ char b1[1024];
+
+ a.bp = b1;
+ b.bp = NULL;
+ a.len = b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+ (void)snprintf(b1, sizeof(b1), "lines=%lu", (u_long)lines);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ (void)snprintf(b1, sizeof(b1), "columns=%lu", (u_long)columns);
+ a.len = strlen(b1);
+ if (opts_set(sp, argv, NULL))
+ return (1);
+ return (0);
+}
diff --git a/contrib/nvi/tk/tk_screen.c b/contrib/nvi/tk/tk_screen.c
new file mode 100644
index 000000000000..e1090930ba23
--- /dev/null
+++ b/contrib/nvi/tk/tk_screen.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_screen.c 8.9 (Berkeley) 5/24/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tki.h"
+
+/*
+ * tk_screen --
+ * Initialize/shutdown the Tcl/Tk screen.
+ *
+ * PUBLIC: int tk_screen __P((SCR *, u_int32_t));
+ */
+int
+tk_screen(sp, flags)
+ SCR *sp;
+ u_int32_t flags;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = TKP(sp);
+
+ /* See if we're already in the right mode. */
+ if (LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI))
+ return (0);
+
+ /* Ex isn't possible. */
+ if (LF_ISSET(SC_EX))
+ return (1);
+
+ /* Initialize terminal based information. */
+ if (tk_term_init(sp))
+ return (1);
+
+ /* Put up the first file name. */
+ if (tk_rename(sp))
+ return (1);
+
+ F_SET(tkp, TK_SCR_VI_INIT);
+ return (0);
+}
+
+/*
+ * tk_quit --
+ * Shutdown the screens.
+ *
+ * PUBLIC: int tk_quit __P((GS *));
+ */
+int
+tk_quit(gp)
+ GS *gp;
+{
+ TK_PRIVATE *tkp;
+ int rval;
+
+ /* Clean up the terminal mappings. */
+ rval = tk_term_end(gp);
+
+ tkp = GTKP(gp);
+ F_CLR(tkp, TK_SCR_VI_INIT);
+
+ return (rval);
+}
diff --git a/contrib/nvi/tk/tk_term.c b/contrib/nvi/tk/tk_term.c
new file mode 100644
index 000000000000..18299a28ae77
--- /dev/null
+++ b/contrib/nvi/tk/tk_term.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_term.c 8.12 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tki.h"
+
+/*
+ * tk_term_init --
+ * Initialize the terminal special keys.
+ *
+ * PUBLIC: int tk_term_init __P((SCR *));
+ */
+int
+tk_term_init(sp)
+ SCR *sp;
+{
+ SEQ *qp;
+
+ /*
+ * Rework any function key mappings that were set before the
+ * screen was initialized.
+ */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next)
+ if (F_ISSET(qp, SEQ_FUNCMAP))
+ (void)tk_fmap(sp, qp->stype,
+ qp->input, qp->ilen, qp->output, qp->olen);
+ return (0);
+}
+
+/*
+ * tk_term_end --
+ * End the special keys defined by the termcap/terminfo entry.
+ *
+ * PUBLIC: int tk_term_end __P((GS *));
+ */
+int
+tk_term_end(gp)
+ GS *gp;
+{
+ SEQ *qp, *nqp;
+
+ /* Delete screen specific mappings. */
+ for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) {
+ nqp = qp->q.le_next;
+ if (F_ISSET(qp, SEQ_SCREEN))
+ (void)seq_mdel(qp);
+ }
+ return (0);
+}
+
+/*
+ * tk_fmap --
+ * Map a function key.
+ *
+ * PUBLIC: int tk_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
+ */
+int
+tk_fmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ seq_t stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ VI_INIT_IGNORE(sp);
+
+ /* Bind a Tk/Tcl function key to a string sequence. */
+ return (0);
+}
+
+/*
+ * tk_optchange --
+ * Curses screen specific "option changed" routine.
+ *
+ * PUBLIC: int tk_optchange __P((SCR *, int, char *, u_long *));
+ */
+int
+tk_optchange(sp, opt, str, valp)
+ SCR *sp;
+ int opt;
+ char *str;
+ u_long *valp;
+{
+ switch (opt) {
+ case O_COLUMNS:
+ case O_LINES:
+ /*
+ * Changing the columns or lines require that we restart
+ * the screen.
+ */
+ F_SET(sp->gp, G_SRESTART);
+ F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
+ break;
+ case O_TERM:
+ msgq(sp, M_ERR, "The screen type may not be changed");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * tk_ssize --
+ * Return the window size.
+ *
+ * PUBLIC: int tk_ssize __P((SCR *, int, size_t *, size_t *, int *));
+ */
+int
+tk_ssize(sp, sigwinch, rowp, colp, changedp)
+ SCR *sp;
+ int sigwinch;
+ size_t *rowp, *colp;
+ int *changedp;
+{
+ TK_PRIVATE *tkp;
+
+ tkp = GTKP(__global_list);
+ (void)Tcl_Eval(tkp->interp, "tk_ssize");
+
+ /*
+ * SunOS systems deliver SIGWINCH when windows are uncovered
+ * as well as when they change size. In addition, we call
+ * here when continuing after being suspended since the window
+ * may have changed size. Since we don't want to background
+ * all of the screens just because the window was uncovered,
+ * ignore the signal if there's no change.
+ *
+ * !!!
+ * sp may be NULL.
+ */
+ if (sigwinch && sp != NULL &&
+ tkp->tk_ssize_row == O_VAL(sp, O_LINES) &&
+ tkp->tk_ssize_col == O_VAL(sp, O_COLUMNS)) {
+ if (changedp != NULL)
+ *changedp = 0;
+ return (0);
+ }
+
+ if (rowp != NULL)
+ *rowp = tkp->tk_ssize_row;
+ if (colp != NULL)
+ *colp = tkp->tk_ssize_col;
+ if (changedp != NULL)
+ *changedp = 1;
+ return (0);
+}
diff --git a/contrib/nvi/tk/tk_util.c b/contrib/nvi/tk/tk_util.c
new file mode 100644
index 000000000000..096fa7bb3eba
--- /dev/null
+++ b/contrib/nvi/tk/tk_util.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)tk_util.c 8.14 (Berkeley) 7/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "tki.h"
+
+static int tk_op_push __P((SCR *, TK_PRIVATE *, e_event_t));
+
+/*
+ * tk_op --
+ * Events provided directly from Tcl/Tk.
+ *
+ * PUBLIC: int tk_op __P((ClientData, Tcl_Interp *, int, char *[]));
+ */
+int
+tk_op(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ SCR *sp;
+ TK_PRIVATE *tkp;
+
+ sp = __global_list->dq.cqh_first; /* XXX */
+ tkp = (TK_PRIVATE *)clientData;
+
+ switch (argv[1][0]) {
+ case 'q':
+ if (!strcmp(argv[1], "quit"))
+ return (tk_op_push(sp, tkp, E_QUIT));
+ break;
+ case 'w':
+ if (!strcmp(argv[1], "write"))
+ return (tk_op_push(sp, tkp, E_WRITE));
+ if (!strcmp(argv[1], "writequit")) {
+ if (tk_op_push(sp, tkp, E_WRITE) != TCL_OK)
+ return (TCL_ERROR);
+ if (tk_op_push(sp, tkp, E_QUIT) != TCL_OK)
+ return (TCL_ERROR);
+ return (TCL_OK);
+ }
+ break;
+ }
+ (void)Tcl_VarEval(tkp->interp,
+ "tk_err {", argv[1], ": unknown event", "}", NULL);
+ return (TCL_OK);
+}
+
+/*
+ * tk_op_push --
+ * Push an event.
+ */
+static int
+tk_op_push(sp, tkp, et)
+ SCR *sp;
+ TK_PRIVATE *tkp;
+ e_event_t et;
+{
+ EVENT *evp;
+
+ CALLOC(sp, evp, EVENT *, 1, sizeof(EVENT));
+ if (evp == NULL)
+ return (TCL_ERROR);
+
+ evp->e_event = et;
+ TAILQ_INSERT_TAIL(&tkp->evq, evp, q);
+ return (TCL_OK);
+}
+
+/*
+ * tk_opt_init --
+ * Initialize the values of the current options.
+ *
+ * PUBLIC: int tk_opt_init __P((ClientData, Tcl_Interp *, int, char *[]));
+ */
+int
+tk_opt_init(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ OPTLIST const *op;
+ SCR *sp;
+ TK_PRIVATE *tkp;
+ int off;
+ char buf[20];
+
+ sp = __global_list->dq.cqh_first; /* XXX */
+
+ tkp = (TK_PRIVATE *)clientData;
+ for (op = optlist, off = 0; op->name != NULL; ++op, ++off) {
+ if (F_ISSET(op, OPT_NDISP))
+ continue;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ (void)Tcl_VarEval(tkp->interp, "set tko_",
+ op->name, O_ISSET(sp, off) ? " 1" : " 0", NULL);
+ break;
+ case OPT_NUM:
+ (void)snprintf(buf,
+ sizeof(buf), " %lu", O_VAL(sp, off));
+ (void)Tcl_VarEval(tkp->interp,
+ "set tko_", op->name, buf, NULL);
+ break;
+ case OPT_STR:
+ (void)Tcl_VarEval(tkp->interp,
+ "set tko_", op->name, " {",
+ O_STR(sp, off) == NULL ? "" : O_STR(sp, off),
+ "}", NULL);
+ break;
+ }
+ }
+ return (TCL_OK);
+}
+
+/*
+ * tk_opt_set --
+ * Set an options button.
+ *
+ * PUBLIC: int tk_opt_set __P((ClientData, Tcl_Interp *, int, char *[]));
+ */
+int
+tk_opt_set(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ ARGS *ap[2], a, b;
+ GS *gp;
+ SCR *sp;
+ int rval;
+ void (*msg) __P((SCR *, mtype_t, char *, size_t));
+ char buf[64];
+
+ gp = __global_list;
+ sp = gp->dq.cqh_first; /* XXX */
+
+ switch (argc) {
+ case 2:
+ a.bp = argv[1] + (sizeof("tko_") - 1);
+ a.len = strlen(a.bp);
+ break;
+ case 3:
+ a.bp = buf;
+ a.len = snprintf(buf, sizeof(buf),
+ "%s%s", atoi(argv[2]) ? "no" : "", argv[1]);
+ break;
+ default:
+ abort();
+ }
+ b.bp = NULL;
+ b.len = 0;
+ ap[0] = &a;
+ ap[1] = &b;
+
+ /* Use Tcl/Tk for error messages. */
+ msg = gp->scr_msg;
+ gp->scr_msg = tk_msg;
+
+ rval = opts_set(sp, ap, NULL);
+
+ gp->scr_msg = msg;
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tk_version --
+ * Display the version in Tcl/Tk.
+ *
+ * PUBLIC: int tk_version __P((ClientData, Tcl_Interp *, int, char *[]));
+ */
+int
+tk_version(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ EXCMD cmd;
+ GS *gp;
+ SCR *sp;
+ int rval;
+ void (*msg) __P((SCR *, mtype_t, char *, size_t));
+
+ gp = __global_list;
+ sp = gp->dq.cqh_first; /* XXX */
+
+ msg = gp->scr_msg;
+ gp->scr_msg = tk_msg;
+
+ ex_cinit(&cmd, C_VERSION, 0, OOBLNO, OOBLNO, 0, NULL);
+ rval = cmd.cmd->fn(sp, &cmd);
+ (void)ex_fflush(sp);
+
+ gp->scr_msg = msg;
+ return (rval ? TCL_ERROR : TCL_OK);
+}
+
+/*
+ * tk_msg --
+ * Display an error message in Tcl/Tk.
+ *
+ * PUBLIC: void tk_msg __P((SCR *, mtype_t, char *, size_t));
+ */
+void
+tk_msg(sp, mtype, line, rlen)
+ SCR *sp;
+ mtype_t mtype;
+ char *line;
+ size_t rlen;
+{
+ TK_PRIVATE *tkp;
+ char buf[2048];
+
+ if (line == NULL)
+ return;
+
+ tkp = TKP(sp);
+ (void)snprintf(buf, sizeof(buf), "%.*s", (int)rlen, line);
+ (void)Tcl_VarEval(tkp->interp, "tk_err {", buf, "}", NULL);
+}
diff --git a/contrib/nvi/tk/tki.h b/contrib/nvi/tk/tki.h
new file mode 100644
index 000000000000..206dacf5d73b
--- /dev/null
+++ b/contrib/nvi/tk/tki.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)tki.h 8.6 (Berkeley) 4/27/96
+ */
+
+#include <tcl.h>
+#include <tk.h>
+
+typedef struct _tk_private {
+ Tcl_Interp *interp;/* Tcl interpreter cookie. */
+
+ /* Shared variables. */
+ int tk_cursor_row; /* Current cursor row. */
+ int tk_cursor_col; /* Current cursor col. */
+ int tk_ssize_row; /* Screen rows. */
+ int tk_ssize_col; /* Screen columns. */
+
+ struct termios orig; /* Original terminal values. */
+
+ CHAR_T ibuf[64]; /* Input keys. */
+ int ibuf_cnt; /* Number of input keys. */
+
+ /* Event queue. */
+ TAILQ_HEAD(_eventh, _event) evq;
+
+ int killersig; /* Killer signal. */
+#define INDX_HUP 0
+#define INDX_INT 1
+#define INDX_TERM 2
+#define INDX_WINCH 3
+#define INDX_MAX 4 /* Original signal information. */
+ struct sigaction oact[INDX_MAX];
+
+#define TK_LLINE_IV 0x0001 /* Last line is in inverse video. */
+#define TK_SCR_VI_INIT 0x0002 /* Vi screen initialized. */
+#define TK_SIGHUP 0x0004 /* SIGHUP arrived. */
+#define TK_SIGINT 0x0008 /* SIGINT arrived. */
+#define TK_SIGTERM 0x0010 /* SIGTERM arrived. */
+#define TK_SIGWINCH 0x0020 /* SIGWINCH arrived. */
+ u_int16_t flags;
+} TK_PRIVATE;
+
+extern GS *__global_list;
+#define TKP(sp) ((TK_PRIVATE *)((sp)->gp->tk_private))
+#define GTKP(gp) ((TK_PRIVATE *)gp->tk_private)
+
+/* Return possibilities from the keyboard read routine. */
+typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_INTR, INP_TIMEOUT } input_t;
+
+/* The screen line relative to a specific window. */
+#define RLNO(sp, lno) (sp)->woff + (lno)
+
+/* Some functions can be safely ignored until the screen is running. */
+#define VI_INIT_IGNORE(sp) \
+ if (F_ISSET(sp, SC_VI) && !F_ISSET(TKP(sp), TK_SCR_VI_INIT)) \
+ return (0);
+
+#include "tk_extern.h"
diff --git a/contrib/nvi/vi/getc.c b/contrib/nvi/vi/getc.c
new file mode 100644
index 000000000000..5e60adee96d7
--- /dev/null
+++ b/contrib/nvi/vi/getc.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)getc.c 10.10 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/common.h"
+#include "vi.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.
+ *
+ * PUBLIC: int cs_init __P((SCR *, VCS *));
+ */
+int
+cs_init(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ int isempty;
+
+ if (db_eget(sp, csp->cs_lno, &csp->cs_bp, &csp->cs_len, &isempty)) {
+ if (isempty)
+ msgq(sp, M_BERR, "177|Empty file");
+ 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.
+ *
+ * PUBLIC: int cs_next __P((SCR *, VCS *));
+ */
+int
+cs_next(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ char *p;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get next line. */
+ case CS_EOL: /* EOL; get next line. */
+ if (db_get(sp, ++csp->cs_lno, 0, &p, &csp->cs_len)) {
+ --csp->cs_lno;
+ csp->cs_flags = CS_EOF;
+ } else {
+ csp->cs_bp = p;
+ 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. */
+ break;
+ 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.
+ *
+ * PUBLIC: int cs_fspace __P((SCR *, VCS *));
+ */
+int
+cs_fspace(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ return (0);
+ for (;;) {
+ if (cs_next(sp, 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.
+ *
+ * PUBLIC: int cs_fblank __P((SCR *, VCS *));
+ */
+int
+cs_fblank(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_next(sp, 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.
+ *
+ * PUBLIC: int cs_prev __P((SCR *, VCS *));
+ */
+int
+cs_prev(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ 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;
+ }
+ if (db_get(sp, /* The line should exist. */
+ --csp->cs_lno, DBG_FATAL, &csp->cs_bp, &csp->cs_len)) {
+ ++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_cno = csp->cs_len - 1;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ break;
+ case CS_EOF: /* EOF: get previous char. */
+ case 0:
+ if (csp->cs_cno == 0)
+ if (csp->cs_lno == 1)
+ csp->cs_flags = CS_SOF;
+ else
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[--csp->cs_cno];
+ break;
+ case CS_SOF: /* SOF. */
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_bblank --
+ * Eat backward to the next non-whitespace character.
+ *
+ * PUBLIC: int cs_bblank __P((SCR *, VCS *));
+ */
+int
+cs_bblank(sp, csp)
+ SCR *sp;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_prev(sp, 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/contrib/nvi/vi/v_at.c b/contrib/nvi/vi/v_at.c
new file mode 100644
index 000000000000..d266f275d983
--- /dev/null
+++ b/contrib/nvi/vi/v_at.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_at.c 10.8 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_at -- @
+ * Execute a buffer.
+ *
+ * PUBLIC: int v_at __P((SCR *, VICMD *));
+ */
+int
+v_at(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ CB *cbp;
+ CHAR_T name;
+ TEXT *tp;
+ size_t len;
+ char nbuf[20];
+
+ /*
+ * !!!
+ * Historically, [@*]<carriage-return> and [@*][@*] executed the most
+ * recently executed buffer in ex mode. In vi mode, only @@ repeated
+ * the last buffer. We change historic practice and make @* work from
+ * vi mode as well, it's simpler and more consistent.
+ *
+ * My intent is that *[buffer] will, in the future, pass the buffer to
+ * whatever interpreter is loaded.
+ */
+ name = F_ISSET(vp, VC_BUFFER) ? vp->buffer : '@';
+ if (name == '@' || name == '*') {
+ if (!F_ISSET(sp, SC_AT_SET)) {
+ ex_emsg(sp, NULL, EXM_NOPREVBUF);
+ return (1);
+ }
+ name = sp->at_lbuf;
+ }
+ F_SET(sp, SC_AT_SET);
+
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ ex_emsg(sp, KEY_NAME(sp, name), EXM_EMPTYBUF);
+ return (1);
+ }
+
+ /* Save for reuse. */
+ sp->at_lbuf = name;
+
+ /*
+ * The buffer is executed in vi mode, while in vi mode, so simply
+ * push it onto the terminal queue and continue.
+ *
+ * !!!
+ * Historic practice is that if the buffer was cut in line mode,
+ * <newlines> were appended to each line as it was pushed onto
+ * the stack. If the buffer was cut in character mode, <newlines>
+ * were appended to all lines but the last one.
+ *
+ * XXX
+ * Historic practice is that execution of an @ buffer could be
+ * undone by a single 'u' command, i.e. the changes were grouped
+ * together. We don't get this right; I'm waiting for the new DB
+ * logging code to be available.
+ */
+ for (tp = cbp->textq.cqh_last;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+ if ((F_ISSET(cbp, CB_LMODE) ||
+ tp->q.cqe_next != (void *)&cbp->textq) &&
+ v_event_push(sp, NULL, "\n", 1, 0) ||
+ v_event_push(sp, NULL, tp->lb, tp->len, 0))
+ return (1);
+
+ /*
+ * !!!
+ * If any count was supplied, it applies to the first command in the
+ * at buffer.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ len = snprintf(nbuf, sizeof(nbuf), "%lu", vp->count);
+ if (v_event_push(sp, NULL, nbuf, len, 0))
+ return (1);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_ch.c b/contrib/nvi/vi/v_ch.c
new file mode 100644
index 000000000000..6a1b61182680
--- /dev/null
+++ b/contrib/nvi/vi/v_ch.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_ch.c 10.8 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static void notfound __P((SCR *, ARG_CHAR_T));
+static void noprev __P((SCR *));
+
+/*
+ * v_chrepeat -- [count];
+ * Repeat the last F, f, T or t search.
+ *
+ * PUBLIC: int v_chrepeat __P((SCR *, VICMD *));
+ */
+int
+v_chrepeat(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ vp->character = VIP(sp)->lastckey;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ return (v_chF(sp, vp));
+ case fSEARCH:
+ return (v_chf(sp, vp));
+ case TSEARCH:
+ return (v_chT(sp, vp));
+ case tSEARCH:
+ return (v_cht(sp, vp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * v_chrrepeat -- [count],
+ * Repeat the last F, f, T or t search in the reverse direction.
+ *
+ * PUBLIC: int v_chrrepeat __P((SCR *, VICMD *));
+ */
+int
+v_chrrepeat(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ cdir_t savedir;
+ int rval;
+
+ vp->character = VIP(sp)->lastckey;
+ savedir = VIP(sp)->csearchdir;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ rval = v_chf(sp, vp);
+ break;
+ case fSEARCH:
+ rval = v_chF(sp, vp);
+ break;
+ case TSEARCH:
+ rval = v_cht(sp, vp);
+ break;
+ case tSEARCH:
+ rval = v_chT(sp, vp);
+ break;
+ default:
+ abort();
+ }
+ VIP(sp)->csearchdir = savedir;
+ return (rval);
+}
+
+/*
+ * v_cht -- [count]tc
+ * Search forward in the line for the character before the next
+ * occurrence of the specified character.
+ *
+ * PUBLIC: int v_cht __P((SCR *, VICMD *));
+ */
+int
+v_cht(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ if (v_chf(sp, vp))
+ return (1);
+
+ /*
+ * v_chf places the cursor on the character, where the 't'
+ * command wants it to its left. We know this is safe since
+ * we had to move right for v_chf() to have succeeded.
+ */
+ --vp->m_stop.cno;
+
+ /*
+ * Make any necessary correction to the motion decision made
+ * by the v_chf routine.
+ */
+ if (!ISMOTION(vp))
+ vp->m_final = vp->m_stop;
+
+ VIP(sp)->csearchdir = tSEARCH;
+ return (0);
+}
+
+/*
+ * v_chf -- [count]fc
+ * Search forward in the line for the next occurrence of the
+ * specified character.
+ *
+ * PUBLIC: int v_chf __P((SCR *, VICMD *));
+ */
+int
+v_chf(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_long cnt;
+ int isempty, 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))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = fSEARCH;
+
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (isempty)
+ goto empty;
+ return (1);
+ }
+
+ if (len == 0) {
+empty: notfound(sp, key);
+ return (1);
+ }
+
+ endp = (startp = p) + len;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (++p < endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = p - startp;
+
+ /*
+ * Non-motion commands move to the end of the range.
+ * Delete and yank stay at the start, ignore others.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_chT -- [count]Tc
+ * Search backward in the line for the character after the next
+ * occurrence of the specified character.
+ *
+ * PUBLIC: int v_chT __P((SCR *, VICMD *));
+ */
+int
+v_chT(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ if (v_chF(sp, vp))
+ return (1);
+
+ /*
+ * v_chF places the cursor on the character, where the 'T'
+ * command wants it to its right. We know this is safe since
+ * we had to move left for v_chF() to have succeeded.
+ */
+ ++vp->m_stop.cno;
+ vp->m_final = vp->m_stop;
+
+ VIP(sp)->csearchdir = TSEARCH;
+ return (0);
+}
+
+/*
+ * v_chF -- [count]Fc
+ * Search backward in the line for the next occurrence of the
+ * specified character.
+ *
+ * PUBLIC: int v_chF __P((SCR *, VICMD *));
+ */
+int
+v_chF(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_long cnt;
+ int isempty, key;
+ char *endp, *p;
+
+ /*
+ * !!!
+ * 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))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = FSEARCH;
+
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (isempty)
+ goto empty;
+ return (1);
+ }
+
+ if (len == 0) {
+empty: notfound(sp, key);
+ return (1);
+ }
+
+ endp = p - 1;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (--p > endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = (p - endp) - 1;
+
+ /*
+ * All commands move to the end of the range. Motion commands
+ * adjust the starting point to the character before the current
+ * one.
+ */
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+static void
+noprev(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "178|No previous F, f, T or t search");
+}
+
+static void
+notfound(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ msgq(sp, M_BERR, "179|%s not found", KEY_NAME(sp, ch));
+}
diff --git a/contrib/nvi/vi/v_cmd.c b/contrib/nvi/vi/v_cmd.c
new file mode 100644
index 000000000000..cc9e30f79ae4
--- /dev/null
+++ b/contrib/nvi/vi/v_cmd.c
@@ -0,0 +1,505 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_cmd.c 10.9 (Berkeley) 3/28/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.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|VM_CUTREQ|VM_RCM_SET,
+ "[count]^A",
+ "^A search forward for cursor word"},
+/* 002 ^B */
+ {v_pageup, V_CNT|VM_RCM_SET,
+ "[count]^B",
+ "^B scroll up by screens"},
+/* 003 ^C */
+ {NULL, 0,
+ "^C",
+ "^C interrupt an operation (e.g. read, write, search)"},
+/* 004 ^D */
+ {v_hpagedown, V_CNT|VM_RCM_SET,
+ "[count]^D",
+ "^D scroll down by half screens (setting count)"},
+/* 005 ^E */
+ {v_linedown, V_CNT,
+ "[count]^E",
+ "^E scroll down by lines"},
+/* 006 ^F */
+ {v_pagedown, V_CNT|VM_RCM_SET,
+ "[count]^F",
+ "^F scroll down by screens"},
+/* 007 ^G */
+ {v_status, 0,
+ "^G",
+ "^G file status"},
+/* 010 ^H */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]^H",
+ "^H move left by characters"},
+/* 011 ^I */
+ {NULL},
+/* 012 ^J */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_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_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]^M",
+ "^M move down by lines (to first non-blank)"},
+/* 016 ^N */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^N",
+ "^N move down by lines"},
+/* 017 ^O */
+ {NULL},
+/* 020 ^P */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^P",
+ "^P move up by lines"},
+/* 021 ^Q -- same as ^V if not 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_ABS|VM_RCM_SET,
+ "^T",
+ "^T tag pop"},
+/* 025 ^U */
+ {v_hpageup, V_CNT|VM_RCM_SET,
+ "[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_suspend, V_SECURE,
+ "^Z",
+ "^Z suspend editor"},
+/* 033 ^[ */
+ {NULL, 0,
+ "^[ <escape>",
+ "^[ <escape> exit input mode, cancel partial commands"},
+/* 034 ^\ */
+ {v_exmode, 0,
+ "^\\",
+ " ^\\ switch to ex mode"},
+/* 035 ^] */
+ {v_tagpush, V_ABS|V_KEYW|VM_RCM_SET,
+ "^]",
+ "^] tag push cursor word"},
+/* 036 ^^ */
+ {v_switch, 0,
+ "^^",
+ "^^ switch to previous file"},
+/* 037 ^_ */
+ {NULL},
+/* 040 ' ' */
+ {v_right, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]' '",
+ " <space> move right by columns"},
+/* 041 ! */
+ {v_filter, V_CNT|V_DOT|V_MOTION|V_SECURE|VM_RCM_SET,
+ "[count]![count]motion command(s)",
+ " ! filter through command(s) to motion"},
+/* 042 " */
+ {NULL},
+/* 043 # */
+ {v_increment, V_CHAR|V_CNT|V_DOT|VM_RCM_SET,
+ "[count]# +|-|#",
+ " # number increment/decrement"},
+/* 044 $ */
+ {v_dollar, V_CNT|V_MOVE|VM_RCM_SETLAST,
+ " [count]$",
+ " $ move to last column"},
+/* 045 % */
+ {v_match, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "%",
+ " % move to match"},
+/* 046 & */
+ {v_again, 0,
+ "&",
+ " & repeat substitution"},
+/* 047 ' */
+ {v_fmark, V_ABS_L|V_CHAR|V_MOVE|VM_LMODE|VM_RCM_SET,
+ "'['a-z]",
+ " ' move to mark (to first non-blank)"},
+/* 050 ( */
+ {v_sentenceb, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count](",
+ " ( move back sentence"},
+/* 051 ) */
+ {v_sentencef, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count])",
+ " ) move forward sentence"},
+/* 052 * */
+ {NULL},
+/* 053 + */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]+",
+ " + move down by lines (to first non-blank)"},
+/* 054 , */
+ {v_chrrepeat, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count],",
+ " , reverse last F, f, T or t search"},
+/* 055 - */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]-",
+ " - move up by lines (to first non-blank)"},
+/* 056 . */
+ {NULL, 0,
+ ".",
+ " . repeat the last command"},
+/* 057 / */
+ {v_searchf, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "/RE[/ offset]",
+ " / search forward"},
+/* 060 0 */
+ {v_zero, V_MOVE|VM_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|VM_RCM_SET,
+ "[count];",
+ " ; repeat last F, f, T or t search"},
+/* 074 < */
+ {v_shiftl, V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
+ "[count]<[count]motion",
+ " < shift lines left to motion"},
+/* 075 = */
+ {NULL},
+/* 076 > */
+ {v_shiftr, V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
+ "[count]>[count]motion",
+ " > shift lines right to motion"},
+/* 077 ? */
+ {v_searchb, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "?RE[? offset]",
+ " ? search backward"},
+/* 100 @ */
+ {v_at, V_CNT|V_RBUF|VM_RCM_SET,
+ "@buffer",
+ " @ execute buffer"},
+/* 101 A */
+ {v_iA, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]A",
+ " A append to the line"},
+/* 102 B */
+ {v_wordB, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]B",
+ " B move back bigword"},
+/* 103 C */
+ {NULL, 0,
+ "[buffer][count]C",
+ " C change to end-of-line"},
+/* 104 D */
+ {NULL, 0,
+ "[buffer]D",
+ " D delete to end-of-line"},
+/* 105 E */
+ {v_wordE, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]E",
+ " E move to end of bigword"},
+/* 106 F */
+ {v_chF, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]F character",
+ " F character in line backward search"},
+/* 107 G */
+ {v_lgoto, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]G",
+ " G move to line"},
+/* 110 H */
+ {v_home, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]H",
+ " H move to count lines from screen top"},
+/* 111 I */
+ {v_iI, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]I",
+ " I insert before first nonblank"},
+/* 112 J */
+ {v_join, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]J",
+ " J join lines"},
+/* 113 K */
+ {NULL},
+/* 114 L */
+ {v_bottom, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]L",
+ " L move to screen bottom"},
+/* 115 M */
+ {v_middle, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "M",
+ " M move to screen middle"},
+/* 116 N */
+ {v_searchN, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "n",
+ " N reverse last search"},
+/* 117 O */
+ {v_iO, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]O",
+ " O insert above line"},
+/* 120 P */
+ {v_Put, V_CNT|V_DOT|V_OBUF|VM_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|VM_RCM_SET,
+ "[count]R",
+ " R replace characters"},
+/* 123 S */
+ {NULL, 0,
+ "[buffer][count]S",
+ " S substitute for the line(s)"},
+/* 124 T */
+ {v_chT, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]T character",
+ " T before character in line backward search"},
+/* 125 U */
+ {v_Undo, VM_RCM_SET,
+ "U",
+ " U Restore the current line"},
+/* 126 V */
+ {NULL},
+/* 127 W */
+ {v_wordW, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]W",
+ " W move to next bigword"},
+/* 130 X */
+ {v_Xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]X",
+ " X delete character before cursor"},
+/* 131 Y */
+ {NULL, 0,
+ "[buffer][count]Y",
+ " Y copy line"},
+/* 132 Z */
+ {v_zexit, 0,
+ "ZZ",
+ "ZZ save file and exit"},
+/* 133 [ */
+ {v_sectionb, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[[",
+ "[[ move back section"},
+/* 134 \ */
+ {NULL},
+/* 135 ] */
+ {v_sectionf, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "]]",
+ "]] move forward section"},
+/* 136 ^ */
+ /*
+ * DON'T set the VM_RCM_SETFNB flag, the function has to do the work
+ * anyway, in case it's a motion component. DO set VM_RCM_SET, so
+ * that any motion that's part of a command is preserved.
+ */
+ {v_first, V_CNT|V_MOVE|VM_RCM_SET,
+ "^",
+ " ^ move to first non-blank"},
+/* 137 _ */
+ /*
+ * Needs both to set the VM_RCM_SETFNB flag, and to do the work
+ * in the function, in case it's a delete.
+ */
+ {v_cfirst, V_CNT|V_MOVE|VM_RCM_SETFNB,
+ "_",
+ " _ move to first non-blank"},
+/* 140 ` */
+ {v_bmark, V_ABS_C|V_CHAR|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "`[`a-z]",
+ " ` move to mark"},
+/* 141 a */
+ {v_ia, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]a",
+ " a append after cursor"},
+/* 142 b */
+ {v_wordb, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]b",
+ " b move back word"},
+/* 143 c */
+ {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|VM_RCM_SET,
+ "[buffer][count]c[count]motion",
+ " c change to motion"},
+/* 144 d */
+ {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|VM_RCM_SET,
+ "[buffer][count]d[count]motion",
+ " d delete to motion"},
+/* 145 e */
+ {v_worde, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]e",
+ " e move to end of word"},
+/* 146 f */
+ {v_chf, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]f character",
+ " f character in line forward search"},
+/* 147 g */
+ {NULL},
+/* 150 h */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]h",
+ " h move left by columns"},
+/* 151 i */
+ {v_ii, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]i",
+ " i insert before cursor"},
+/* 152 j */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]j",
+ " j move down by lines"},
+/* 153 k */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]k",
+ " k move up by lines"},
+/* 154 l */
+ {v_right, V_CNT|V_MOVE|VM_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_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "n",
+ " n repeat last search"},
+/* 157 o */
+ {v_io, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]o",
+ " o append after line"},
+/* 160 p */
+ {v_put, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer]p",
+ " p insert after cursor from buffer"},
+/* 161 q */
+ {NULL},
+/* 162 r */
+ {v_replace, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]r character",
+ " r replace character"},
+/* 163 s */
+ {v_subst, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]s",
+ " s substitute character"},
+/* 164 t */
+ {v_cht, V_CHAR|V_CNT|V_MOVE|VM_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, VM_RCM_SET,
+ "u",
+ " u undo last change"},
+/* 166 v */
+ {NULL},
+/* 167 w */
+ {v_wordw, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]w",
+ " w move to next word"},
+/* 170 x */
+ {v_xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]x",
+ " x delete character"},
+/* 171 y */
+ {v_yank, V_CNT|V_DOT|V_MOTION|V_OBUF|VM_RCM_SET,
+ "[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_ABS_L|V_CNT|VM_RCM_SETFNB,
+ "[line]z[window_size][-|.|+|^|<CR>]",
+ " z reposition the screen"},
+/* 173 { */
+ {v_paragraphb, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count]{",
+ " { move back paragraph"},
+/* 174 | */
+ {v_ncol, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]|",
+ " | move to column"},
+/* 175 } */
+ {v_paragraphf, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count]}",
+ " } move forward paragraph"},
+/* 176 ~ */
+ {v_ulcase, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]~",
+ " ~ reverse case"},
+};
diff --git a/contrib/nvi/vi/v_delete.c b/contrib/nvi/vi/v_delete.c
new file mode 100644
index 000000000000..70df78b22e2c
--- /dev/null
+++ b/contrib/nvi/vi/v_delete.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_delete.c 10.9 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_delete -- [buffer][count]d[count]motion
+ * [buffer][count]D
+ * Delete a range of text.
+ *
+ * PUBLIC: int v_delete __P((SCR *, VICMD *));
+ */
+int
+v_delete(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t nlines;
+ size_t len;
+ int lmode;
+
+ lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
+
+ /* Yank the lines. */
+ if (cut(sp, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop,
+ lmode | (F_ISSET(vp, VM_CUTREQ) ? CUT_NUMREQ : CUT_NUMOPT)))
+ return (1);
+
+ /* Delete the lines. */
+ if (del(sp, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /*
+ * Check for deletion of the entire file. Try to check a close
+ * by line so we don't go to the end of the file unnecessarily.
+ */
+ if (!db_exist(sp, vp->m_final.lno + 1)) {
+ if (db_last(sp, &nlines))
+ return (1);
+ if (nlines == 0) {
+ vp->m_final.lno = 1;
+ vp->m_final.cno = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * One special correction, in case we've deleted the current line or
+ * character. We check it here instead of checking in every command
+ * that can be a motion component.
+ */
+ if (db_get(sp, vp->m_final.lno, 0, NULL, &len)) {
+ if (db_get(sp, nlines, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_final.lno = nlines;
+ }
+
+ /*
+ * !!!
+ * Cursor movements, other than those caused by a line mode command
+ * moving to another line, historically reset the relative position.
+ *
+ * This currently matches the check made in v_yank(), I'm hoping that
+ * they should be consistent...
+ */
+ if (!F_ISSET(vp, VM_LMODE)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SET);
+
+ /* Make sure the set cursor position exists. */
+ if (vp->m_final.cno >= len)
+ vp->m_final.cno = len ? len - 1 : 0;
+ }
+
+ /*
+ * !!!
+ * The "dd" command moved to the first non-blank; "d<motion>"
+ * didn't.
+ */
+ if (F_ISSET(vp, VM_LDOUBLE)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_ex.c b/contrib/nvi/vi/v_ex.c
new file mode 100644
index 000000000000..359080c656e3
--- /dev/null
+++ b/contrib/nvi/vi/v_ex.c
@@ -0,0 +1,696 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_ex.c 10.42 (Berkeley) 6/28/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static int v_ecl __P((SCR *));
+static int v_ecl_init __P((SCR *));
+static int v_ecl_log __P((SCR *, TEXT *));
+static int v_ex_done __P((SCR *, VICMD *));
+static int v_exec_ex __P((SCR *, VICMD *, EXCMD *));
+
+/*
+ * v_again -- &
+ * Repeat the previous substitution.
+ *
+ * PUBLIC: int v_again __P((SCR *, VICMD *));
+ */
+int
+v_again(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1, ap);
+ ex_cadd(&cmd, &a, "", 1);
+
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_exmode -- Q
+ * Switch the editor into EX mode.
+ *
+ * PUBLIC: int v_exmode __P((SCR *, VICMD *));
+ */
+int
+v_exmode(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ GS *gp;
+
+ gp = sp->gp;
+
+ /* Try and switch screens -- the screen may not permit it. */
+ if (gp->scr_screen(sp, SC_EX)) {
+ msgq(sp, M_ERR,
+ "207|The Q command requires the ex terminal interface");
+ return (1);
+ }
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
+
+ /* Save the current cursor position. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ /* Switch to ex mode. */
+ F_CLR(sp, SC_VI | SC_SCR_VI);
+ F_SET(sp, SC_EX);
+
+ /* Move out of the vi screen. */
+ (void)ex_puts(sp, "\n");
+
+ return (0);
+}
+
+/*
+ * v_join -- [count]J
+ * Join lines together.
+ *
+ * PUBLIC: int v_join __P((SCR *, VICMD *));
+ */
+int
+v_join(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EXCMD 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 = vp->m_start.lno + 1;
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
+ lno = vp->m_start.lno + (vp->count - 1);
+
+ ex_cinit(&cmd, C_JOIN, 2, vp->m_start.lno, lno, 0, NULL);
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_shiftl -- [count]<motion
+ * Shift lines left.
+ *
+ * PUBLIC: int v_shiftl __P((SCR *, VICMD *));
+ */
+int
+v_shiftl(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap);
+ ex_cadd(&cmd, &a, "<", 1);
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_shiftr -- [count]>motion
+ * Shift lines right.
+ *
+ * PUBLIC: int v_shiftr __P((SCR *, VICMD *));
+ */
+int
+v_shiftr(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0, ap);
+ ex_cadd(&cmd, &a, ">", 1);
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_suspend -- ^Z
+ * Suspend vi.
+ *
+ * PUBLIC: int v_suspend __P((SCR *, VICMD *));
+ */
+int
+v_suspend(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_STOP, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, "suspend", sizeof("suspend") - 1);
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_switch -- ^^
+ * Switch to the previous file.
+ *
+ * PUBLIC: int v_switch __P((SCR *, VICMD *));
+ */
+int
+v_switch(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+ char *name;
+
+ /*
+ * Try the alternate file name, then the previous file
+ * name. Use the real name, not the user's current name.
+ */
+ if ((name = sp->alt_name) == NULL) {
+ msgq(sp, M_ERR, "180|No previous file to edit");
+ return (1);
+ }
+
+ /* If autowrite is set, write out the file. */
+ if (file_m1(sp, 0, FS_ALL))
+ return (1);
+
+ ex_cinit(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, ap);
+ ex_cadd(&cmd, &a, name, strlen(name));
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_tagpush -- ^[
+ * Do a tag search on the cursor keyword.
+ *
+ * PUBLIC: int v_tagpush __P((SCR *, VICMD *));
+ */
+int
+v_tagpush(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ ARGS *ap[2], a;
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_TAG, 0, OOBLNO, 0, 0, ap);
+ ex_cadd(&cmd, &a, VIP(sp)->keyw, strlen(VIP(sp)->keyw));
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_tagpop -- ^T
+ * Pop the tags stack.
+ *
+ * PUBLIC: int v_tagpop __P((SCR *, VICMD *));
+ */
+int
+v_tagpop(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EXCMD cmd;
+
+ ex_cinit(&cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL);
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_filter -- [count]!motion command(s)
+ * Run range through shell commands, replacing text.
+ *
+ * PUBLIC: int v_filter __P((SCR *, VICMD *));
+ */
+int
+v_filter(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EXCMD cmd;
+ TEXT *tp;
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file, and it's 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.
+ *
+ * !!!
+ * Historical vi did not permit the '!' command to be associated with
+ * a non-line oriented motion command, in general, although it did
+ * with search commands. So, !f; and !w would fail, but !/;<CR>
+ * would succeed, even if they all moved to the same location in the
+ * current line. I don't see any reason to disallow '!' using any of
+ * the possible motion commands.
+ *
+ * !!!
+ * Historical vi ran the last bang command if N or n was used as the
+ * search motion.
+ */
+ if (F_ISSET(vp, VC_ISDOT) ||
+ ISCMD(vp->rkp, 'N') || ISCMD(vp->rkp, 'n')) {
+ ex_cinit(&cmd, C_BANG,
+ 2, vp->m_start.lno, vp->m_stop.lno, 0, NULL);
+ EXP(sp)->argsoff = 0; /* XXX */
+
+ if (argv_exp1(sp, &cmd, "!", 1, 1))
+ return (1);
+ cmd.argc = EXP(sp)->argsoff; /* XXX */
+ cmd.argv = EXP(sp)->args; /* XXX */
+ return (v_exec_ex(sp, vp, &cmd));
+ }
+
+ /* Get the command from the user. */
+ if (v_tcmd(sp, vp,
+ '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_FILEC | TXT_PROMPT))
+ return (1);
+
+ /*
+ * Check to see if the user changed their mind.
+ *
+ * !!!
+ * Entering <escape> on an empty line was historically an error,
+ * this implementation doesn't bother.
+ */
+ tp = sp->tiq.cqh_first;
+ if (tp->term != TERM_OK) {
+ vp->m_final.lno = sp->lno;
+ vp->m_final.cno = sp->cno;
+ return (0);
+ }
+
+ /* Home the cursor. */
+ vs_home(sp);
+
+ ex_cinit(&cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0, NULL);
+ EXP(sp)->argsoff = 0; /* XXX */
+
+ if (argv_exp1(sp, &cmd, tp->lb + 1, tp->len - 1, 1))
+ return (1);
+ cmd.argc = EXP(sp)->argsoff; /* XXX */
+ cmd.argv = EXP(sp)->args; /* XXX */
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_event_exec --
+ * Execute some command(s) based on an event.
+ *
+ * PUBLIC: int v_event_exec __P((SCR *, VICMD *));
+ */
+int
+v_event_exec(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EXCMD cmd;
+
+ switch (vp->ev.e_event) {
+ case E_QUIT:
+ ex_cinit(&cmd, C_QUIT, 0, OOBLNO, OOBLNO, 0, NULL);
+ break;
+ case E_WRITE:
+ ex_cinit(&cmd, C_WRITE, 0, OOBLNO, OOBLNO, 0, NULL);
+ break;
+ default:
+ abort();
+ }
+ return (v_exec_ex(sp, vp, &cmd));
+}
+
+/*
+ * v_exec_ex --
+ * Execute an ex command.
+ */
+static int
+v_exec_ex(sp, vp, exp)
+ SCR *sp;
+ VICMD *vp;
+ EXCMD *exp;
+{
+ int rval;
+
+ rval = exp->cmd->fn(sp, exp);
+ return (v_ex_done(sp, vp) || rval);
+}
+
+/*
+ * v_ex -- :
+ * Execute a colon command line.
+ *
+ * PUBLIC: int v_ex __P((SCR *, VICMD *));
+ */
+int
+v_ex(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ GS *gp;
+ TEXT *tp;
+ int do_cedit, do_resolution, ifcontinue;
+
+ gp = sp->gp;
+
+ /*
+ * !!!
+ * If we put out more than a single line of messages, or ex trashes
+ * the screen, the user may continue entering ex commands. We find
+ * this out when we do the screen/message resolution. We can't enter
+ * completely into ex mode however, because the user can elect to
+ * return into vi mode by entering any key, i.e. we have to be in raw
+ * mode.
+ */
+ for (do_cedit = do_resolution = 0;;) {
+ /*
+ * !!!
+ * There may already be an ex command waiting to run. If
+ * so, we continue with it.
+ */
+ if (!EXCMD_RUNNING(gp)) {
+ /* Get a command. */
+ if (v_tcmd(sp, vp, ':',
+ TXT_BS | TXT_CEDIT | TXT_FILEC | TXT_PROMPT))
+ return (1);
+ tp = sp->tiq.cqh_first;
+
+ /*
+ * If the user entered a single <esc>, they want to
+ * edit their colon command history. If they already
+ * entered some text, move it into the edit history.
+ */
+ if (tp->term == TERM_CEDIT) {
+ if (tp->len > 1 && v_ecl_log(sp, tp))
+ return (1);
+ do_cedit = 1;
+ break;
+ }
+
+ /* If the user didn't enter anything, return. */
+ if (tp->term == TERM_BS)
+ break;
+
+ /* Log the command. */
+ if (O_STR(sp, O_CEDIT) != NULL && v_ecl_log(sp, tp))
+ return (1);
+
+ /* Push a command on the command stack. */
+ if (ex_run_str(sp, NULL, tp->lb, tp->len, 0, 1))
+ return (1);
+ }
+
+ /* Home the cursor. */
+ vs_home(sp);
+
+ /*
+ * !!!
+ * If the editor wrote the screen behind curses back, put out
+ * a <newline> so that we don't overwrite the user's command
+ * with its output or the next want-to-continue? message. This
+ * doesn't belong here, but I can't find another place to put
+ * it. See, we resolved the output from the last ex command,
+ * and the user entered another one. This is the only place
+ * where we have control before the ex command writes output.
+ * We could get control in vs_msg(), but we have no way to know
+ * if command didn't put out any output when we try and resolve
+ * this command. This fixes a bug where combinations of ex
+ * commands, e.g. ":set<CR>:!date<CR>:set" didn't look right.
+ */
+ if (F_ISSET(sp, SC_SCR_EXWROTE))
+ (void)putchar('\n');
+
+ /* Call the ex parser. */
+ (void)ex_cmd(sp);
+
+ /* Flush ex messages. */
+ (void)ex_fflush(sp);
+
+ /* Resolve any messages. */
+ if (vs_ex_resolve(sp, &ifcontinue))
+ return (1);
+
+ /*
+ * Continue or return. If continuing, make sure that we
+ * eventually do resolution.
+ */
+ if (!ifcontinue)
+ break;
+ do_resolution = 1;
+
+ /* If we're continuing, it's a new command. */
+ ++sp->ccnt;
+ }
+
+ /*
+ * If the user previously continued an ex command, we have to do
+ * resolution to clean up the screen. Don't wait, we already did
+ * that.
+ */
+ if (do_resolution) {
+ F_SET(sp, SC_EX_WAIT_NO);
+ if (vs_ex_resolve(sp, &ifcontinue))
+ return (1);
+ }
+
+ /* Cleanup from the ex command. */
+ if (v_ex_done(sp, vp))
+ return (1);
+
+ /* The user may want to edit their colon command history. */
+ if (do_cedit)
+ return (v_ecl(sp));
+
+ return (0);
+}
+
+/*
+ * v_ex_done --
+ * Cleanup from an ex command.
+ */
+static int
+v_ex_done(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+
+ /*
+ * The only cursor modifications are real, however, the underlying
+ * line may have changed; don't trust anything. This code has been
+ * a remarkably fertile place for bugs. 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 it cares less about the
+ * column and it may have disappeared.
+ *
+ * Don't trust ANYTHING.
+ *
+ * XXX
+ * Ex will soon have to start handling the column correctly; see
+ * the POSIX 1003.2 standard.
+ */
+ if (db_eget(sp, sp->lno, NULL, &len, NULL)) {
+ sp->lno = 1;
+ sp->cno = 0;
+ } else if (sp->cno >= len)
+ sp->cno = len ? len - 1 : 0;
+
+ vp->m_final.lno = sp->lno;
+ vp->m_final.cno = sp->cno;
+
+ /*
+ * Don't re-adjust the cursor after executing an ex command,
+ * and ex movements are permanent.
+ */
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SET);
+
+ return (0);
+}
+
+/*
+ * v_ecl --
+ * Start an edit window on the colon command-line commands.
+ */
+static int
+v_ecl(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SCR *new;
+
+ /* Initialize the screen, if necessary. */
+ gp = sp->gp;
+ if (gp->ccl_sp == NULL && v_ecl_init(sp))
+ return (1);
+
+ /* Get a new screen. */
+ if (screen_init(gp, sp, &new))
+ return (1);
+ if (vs_split(sp, new, 1)) {
+ (void)screen_end(new);
+ return (1);
+ }
+
+ /* Attach to the screen. */
+ new->ep = gp->ccl_sp->ep;
+ ++new->ep->refcnt;
+
+ new->frp = gp->ccl_sp->frp;
+ new->frp->flags = sp->frp->flags;
+
+ /* Move the cursor to the end. */
+ (void)db_last(new, &new->lno);
+ if (new->lno == 0)
+ new->lno = 1;
+
+ /* Remember the originating window. */
+ sp->ccl_parent = sp;
+
+ /* It's a special window. */
+ F_SET(new, SC_COMEDIT);
+
+ /* Set up the switch. */
+ sp->nextdisp = new;
+ F_SET(sp, SC_SSWITCH);
+ return (0);
+}
+
+/*
+ * v_ecl_exec --
+ * Execute a command from a colon command-line window.
+ *
+ * PUBLIC: int v_ecl_exec __P((SCR *));
+ */
+int
+v_ecl_exec(sp)
+ SCR *sp;
+{
+ size_t len;
+ char *p;
+
+ if (db_get(sp, sp->lno, 0, &p, &len) && sp->lno == 1) {
+ v_emsg(sp, NULL, VIM_EMPTY);
+ return (1);
+ }
+ if (len == 0) {
+ msgq(sp, M_BERR, "307|No ex command to execute");
+ return (1);
+ }
+
+ /* Push the command on the command stack. */
+ if (ex_run_str(sp, NULL, p, len, 0, 0))
+ return (1);
+
+ /* Set up the switch. */
+ sp->nextdisp = sp->ccl_parent;
+ F_SET(sp, SC_EXIT);
+ return (0);
+}
+
+/*
+ * v_ecl_log --
+ * Log a command into the colon command-line log file.
+ */
+static int
+v_ecl_log(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ EXF *save_ep;
+ recno_t lno;
+ int rval;
+
+ /* Initialize the screen, if necessary. */
+ if (sp->gp->ccl_sp == NULL && v_ecl_init(sp))
+ return (1);
+
+ /*
+ * Don't log colon command window commands into the colon command
+ * window...
+ */
+ if (sp->ep == sp->gp->ccl_sp->ep)
+ return (0);
+
+ /*
+ * XXX
+ * Swap the current EXF with the colon command file EXF. This
+ * isn't pretty, but too many routines "know" that sp->ep points
+ * to the current EXF.
+ */
+ save_ep = sp->ep;
+ sp->ep = sp->gp->ccl_sp->ep;
+ if (db_last(sp, &lno)) {
+ sp->ep = save_ep;
+ return (1);
+ }
+ rval = db_append(sp, 0, lno, tp->lb, tp->len);
+ sp->ep = save_ep;
+ return (rval);
+}
+
+/*
+ * v_ecl_init --
+ * Initialize the colon command-line log file.
+ */
+static int
+v_ecl_init(sp)
+ SCR *sp;
+{
+ FREF *frp;
+ GS *gp;
+
+ gp = sp->gp;
+
+ /* Get a temporary file. */
+ if ((frp = file_add(sp, NULL)) == NULL)
+ return (1);
+
+ /*
+ * XXX
+ * Create a screen -- the file initialization code wants one.
+ */
+ if (screen_init(gp, sp, &gp->ccl_sp))
+ return (1);
+ if (file_init(gp->ccl_sp, frp, NULL, 0)) {
+ (void)screen_end(gp->ccl_sp);
+ return (1);
+ }
+
+ /* The underlying file isn't recoverable. */
+ F_CLR(gp->ccl_sp->ep, F_RCV_ON);
+
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_increment.c b/contrib/nvi/vi/v_increment.c
new file mode 100644
index 000000000000..45e763fa5bd5
--- /dev/null
+++ b/contrib/nvi/vi/v_increment.c
@@ -0,0 +1,267 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_increment.c 10.12 (Berkeley) 3/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static char * const fmt[] = {
+#define DEC 0
+ "%ld",
+#define SDEC 1
+ "%+ld",
+#define HEXC 2
+ "0X%0*lX",
+#define HEXL 3
+ "0x%0*lx",
+#define OCTAL 4
+ "%#0*lo",
+};
+
+static void inc_err __P((SCR *, enum nresult));
+
+/*
+ * v_increment -- [count]#[#+-]
+ * Increment/decrement a keyword number.
+ *
+ * PUBLIC: int v_increment __P((SCR *, VICMD *));
+ */
+int
+v_increment(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ enum nresult nret;
+ u_long ulval;
+ long change, ltmp, lval;
+ size_t beg, blen, end, len, nlen, wlen;
+ int base, isempty, rval;
+ char *bp, *ntype, *p, *t, nbuf[100];
+
+ /* Validate the operator. */
+ if (vp->character == '#')
+ vp->character = '+';
+ if (vp->character != '+' && vp->character != '-') {
+ v_emsg(sp, vp->kp->usage, VIM_USAGE);
+ return (1);
+ }
+
+ /* If new value set, save it off, but it has to fit in a long. */
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (vp->count > LONG_MAX) {
+ inc_err(sp, NUM_OVER);
+ return (1);
+ }
+ change = vp->count;
+ } else
+ change = 1;
+
+ /* Get the line. */
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (isempty)
+ goto nonum;
+ return (1);
+ }
+
+ /*
+ * Skip any leading space before the number. Getting a cursor word
+ * implies moving the cursor to its beginning, if we moved, refresh
+ * now.
+ */
+ for (beg = vp->m_start.cno; beg < len && isspace(p[beg]); ++beg);
+ if (beg >= len)
+ goto nonum;
+ if (beg != vp->m_start.cno) {
+ sp->cno = beg;
+ (void)vs_refresh(sp, 0);
+ }
+
+#undef ishex
+#define ishex(c) (isdigit(c) || strchr("abcdefABCDEF", c))
+#undef isoctal
+#define isoctal(c) (isdigit(c) && (c) != '8' && (c) != '9')
+
+ /*
+ * Look for 0[Xx], or leading + or - signs, guess at the base.
+ * The character after that must be a number. Wlen is set to
+ * the remaining characters in the line that could be part of
+ * the number.
+ */
+ wlen = len - beg;
+ if (p[beg] == '0' && wlen > 2 &&
+ (p[beg + 1] == 'X' || p[beg + 1] == 'x')) {
+ base = 16;
+ end = beg + 2;
+ if (!ishex(p[end]))
+ goto decimal;
+ ntype = p[beg + 1] == 'X' ? fmt[HEXC] : fmt[HEXL];
+ } else if (p[beg] == '0' && wlen > 1) {
+ base = 8;
+ end = beg + 1;
+ if (!isoctal(p[end]))
+ goto decimal;
+ ntype = fmt[OCTAL];
+ } else if (wlen >= 1 && (p[beg] == '+' || p[beg] == '-')) {
+ base = 10;
+ end = beg + 1;
+ ntype = fmt[SDEC];
+ if (!isdigit(p[end]))
+ goto nonum;
+ } else {
+decimal: base = 10;
+ end = beg;
+ ntype = fmt[DEC];
+ if (!isdigit(p[end])) {
+nonum: msgq(sp, M_ERR, "181|Cursor not in a number");
+ return (1);
+ }
+ }
+
+ /* Find the end of the word, possibly correcting the base. */
+ while (++end < len) {
+ switch (base) {
+ case 8:
+ if (isoctal(p[end]))
+ continue;
+ if (p[end] == '8' || p[end] == '9') {
+ base = 10;
+ ntype = fmt[DEC];
+ continue;
+ }
+ break;
+ case 10:
+ if (isdigit(p[end]))
+ continue;
+ break;
+ case 16:
+ if (ishex(p[end]))
+ continue;
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ break;
+ }
+ wlen = (end - beg);
+
+ /*
+ * XXX
+ * If the line was at the end of the buffer, we have to copy it
+ * so we can guarantee that it's NULL-terminated. We make the
+ * buffer big enough to fit the line changes as well, and only
+ * allocate once.
+ */
+ GET_SPACE_RET(sp, bp, blen, len + 50);
+ if (end == len) {
+ memmove(bp, &p[beg], wlen);
+ bp[wlen] = '\0';
+ t = bp;
+ } else
+ t = &p[beg];
+
+ /*
+ * Octal or hex deal in unsigned longs, everything else is done
+ * in signed longs.
+ */
+ if (base == 10) {
+ if ((nret = nget_slong(&lval, t, NULL, 10)) != NUM_OK)
+ goto err;
+ ltmp = vp->character == '-' ? -change : change;
+ if (lval > 0 && ltmp > 0 && !NPFITS(LONG_MAX, lval, ltmp)) {
+ nret = NUM_OVER;
+ goto err;
+ }
+ if (lval < 0 && ltmp < 0 && !NNFITS(LONG_MIN, lval, ltmp)) {
+ nret = NUM_UNDER;
+ goto err;
+ }
+ lval += ltmp;
+ /* If we cross 0, signed numbers lose their sign. */
+ if (lval == 0 && ntype == fmt[SDEC])
+ ntype = fmt[DEC];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval);
+ } else {
+ if ((nret = nget_uslong(&ulval, t, NULL, base)) != NUM_OK)
+ goto err;
+ if (vp->character == '+') {
+ if (!NPFITS(ULONG_MAX, ulval, change)) {
+ nret = NUM_OVER;
+ goto err;
+ }
+ ulval += change;
+ } else {
+ if (ulval < change) {
+ nret = NUM_UNDER;
+ goto err;
+ }
+ ulval -= change;
+ }
+
+ /* Correct for literal "0[Xx]" in format. */
+ if (base == 16)
+ wlen -= 2;
+
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, wlen, ulval);
+ }
+
+ /* Build the new line. */
+ memmove(bp, p, beg);
+ memmove(bp + beg, nbuf, nlen);
+ memmove(bp + beg + nlen, p + end, len - beg - (end - beg));
+ len = beg + nlen + (len - beg - (end - beg));
+
+ nret = NUM_OK;
+ rval = db_set(sp, vp->m_start.lno, bp, len);
+
+ if (0) {
+err: rval = 1;
+ inc_err(sp, nret);
+ }
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+static void
+inc_err(sp, nret)
+ SCR *sp;
+ enum nresult nret;
+{
+ switch (nret) {
+ case NUM_ERR:
+ break;
+ case NUM_OK:
+ abort();
+ /* NOREACHED */
+ case NUM_OVER:
+ msgq(sp, M_ERR, "182|Resulting number too large");
+ break;
+ case NUM_UNDER:
+ msgq(sp, M_ERR, "183|Resulting number too small");
+ break;
+ }
+}
diff --git a/contrib/nvi/vi/v_init.c b/contrib/nvi/vi/v_init.c
new file mode 100644
index 000000000000..ee58de377ada
--- /dev/null
+++ b/contrib/nvi/vi/v_init.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_init.c 10.8 (Berkeley) 3/30/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_screen_copy --
+ * Copy vi screen.
+ *
+ * PUBLIC: int v_screen_copy __P((SCR *, SCR *));
+ */
+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;
+
+ /* Invalidate the line size cache. */
+ VI_SCR_CFLUSH(nvip);
+
+ if (orig == NULL) {
+ nvip->csearchdir = CNOTSET;
+ } else {
+ ovip = VIP(orig);
+
+ /* User can replay the last input, but nothing else. */
+ if (ovip->rep_len != 0) {
+ MALLOC_RET(orig, nvip->rep, EVENT *, ovip->rep_len);
+ memmove(nvip->rep, ovip->rep, ovip->rep_len);
+ nvip->rep_len = ovip->rep_len;
+ }
+
+ /* Copy the paragraph/section information. */
+ if (ovip->ps != NULL && (nvip->ps =
+ v_strdup(sp, ovip->ps, strlen(ovip->ps))) == NULL)
+ return (1);
+
+ nvip->lastckey = ovip->lastckey;
+ nvip->csearchdir = ovip->csearchdir;
+
+ nvip->srows = ovip->srows;
+ }
+ return (0);
+}
+
+/*
+ * v_screen_end --
+ * End a vi screen.
+ *
+ * PUBLIC: int v_screen_end __P((SCR *));
+ */
+int
+v_screen_end(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+
+ if ((vip = VIP(sp)) == NULL)
+ return (0);
+ if (vip->keyw != NULL)
+ free(vip->keyw);
+ if (vip->rep != NULL)
+ free(vip->rep);
+ if (vip->ps != NULL)
+ free(vip->ps);
+
+ if (HMAP != NULL)
+ free(HMAP);
+
+ free(vip);
+ sp->vi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * v_optchange --
+ * Handle change of options for vi.
+ *
+ * PUBLIC: int v_optchange __P((SCR *, int, char *, u_long *));
+ */
+int
+v_optchange(sp, offset, str, valp)
+ SCR *sp;
+ int offset;
+ char *str;
+ u_long *valp;
+{
+ switch (offset) {
+ case O_PARAGRAPHS:
+ return (v_buildps(sp, str, O_STR(sp, O_SECTIONS)));
+ case O_SECTIONS:
+ return (v_buildps(sp, O_STR(sp, O_PARAGRAPHS), str));
+ case O_WINDOW:
+ return (vs_crel(sp, *valp));
+ }
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_itxt.c b/contrib/nvi/vi/v_itxt.c
new file mode 100644
index 000000000000..6cf93773ac3d
--- /dev/null
+++ b/contrib/nvi/vi/v_itxt.c
@@ -0,0 +1,537 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_itxt.c 10.16 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.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.
+ *
+ * !!!
+ * 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.) We also check for mapped keys waiting, i.e. if we're in the
+ * middle of a map, don't bother logging the cursor.
+ */
+#define LOG_CORRECT { \
+ if (!MAPPED_KEYS_WAITING(sp)) \
+ (void)log_cursor(sp); \
+}
+
+static u_int32_t set_txt_std __P((SCR *, VICMD *, u_int32_t));
+
+/*
+ * v_iA -- [count]A
+ * Append text to the end of the line.
+ *
+ * PUBLIC: int v_iA __P((SCR *, VICMD *));
+ */
+int
+v_iA(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+
+ if (!db_get(sp, vp->m_start.lno, 0, NULL, &len))
+ sp->cno = len == 0 ? 0 : len - 1;
+
+ LOG_CORRECT;
+
+ return (v_ia(sp, vp));
+}
+
+/*
+ * v_ia -- [count]a
+ * [count]A
+ * Append text to the cursor position.
+ *
+ * PUBLIC: int v_ia __P((SCR *, VICMD *));
+ */
+int
+v_ia(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_int32_t flags;
+ int isempty;
+ char *p;
+
+ flags = set_txt_std(sp, vp, 0);
+ sp->showmode = SM_APPEND;
+ sp->lno = vp->m_start.lno;
+
+ /* Move the cursor one column to the right and repaint the screen. */
+ if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (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);
+
+ return (v_txt(sp, vp, NULL, p, len,
+ 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
+}
+
+/*
+ * v_iI -- [count]I
+ * Insert text at the first nonblank.
+ *
+ * PUBLIC: int v_iI __P((SCR *, VICMD *));
+ */
+int
+v_iI(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ sp->cno = 0;
+ if (nonblank(sp, vp->m_start.lno, &sp->cno))
+ return (1);
+
+ LOG_CORRECT;
+
+ return (v_ii(sp, vp));
+}
+
+/*
+ * v_ii -- [count]i
+ * [count]I
+ * Insert text at the cursor position.
+ *
+ * PUBLIC: int v_ii __P((SCR *, VICMD *));
+ */
+int
+v_ii(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_int32_t flags;
+ int isempty;
+ char *p;
+
+ flags = set_txt_std(sp, vp, 0);
+ sp->showmode = SM_INSERT;
+ sp->lno = vp->m_start.lno;
+
+ if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ len = 0;
+ }
+
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ return (v_txt(sp, vp, NULL, p, len,
+ 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
+}
+
+enum which { o_cmd, O_cmd };
+static int io __P((SCR *, VICMD *, enum which));
+
+/*
+ * v_iO -- [count]O
+ * Insert text above this line.
+ *
+ * PUBLIC: int v_iO __P((SCR *, VICMD *));
+ */
+int
+v_iO(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (io(sp, vp, O_cmd));
+}
+
+/*
+ * v_io -- [count]o
+ * Insert text after this line.
+ *
+ * PUBLIC: int v_io __P((SCR *, VICMD *));
+ */
+int
+v_io(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (io(sp, vp, o_cmd));
+}
+
+static int
+io(sp, vp, cmd)
+ SCR *sp;
+ VICMD *vp;
+ enum which cmd;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_int32_t flags;
+ char *p;
+
+ flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL);
+ sp->showmode = SM_INSERT;
+
+ if (sp->lno == 1) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+ LOG_CORRECT;
+
+ if (cmd == O_cmd) {
+ if (db_insert(sp, sp->lno, p, 0))
+ return (1);
+ if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
+ return (1);
+ ai_line = sp->lno + 1;
+ } else {
+ if (db_append(sp, 1, sp->lno, p, 0))
+ return (1);
+ if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len))
+ return (1);
+ ai_line = sp->lno - 1;
+ }
+ }
+ return (v_txt(sp, vp, NULL, p, len,
+ 0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
+}
+
+/*
+ * v_change -- [buffer][count]c[count]motion
+ * [buffer][count]C
+ * [buffer][count]S
+ * Change command.
+ *
+ * PUBLIC: int v_change __P((SCR *, VICMD *));
+ */
+int
+v_change(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t blen, len;
+ u_int32_t flags;
+ int isempty, lmode, rval;
+ char *bp, *p;
+
+ /*
+ * 'c' can be combined with motion commands that set the resulting
+ * cursor position, i.e. "cG". Clear the VM_RCM flags and make the
+ * resulting cursor position stick, inserting text has its own rules
+ * for cursor positioning.
+ */
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SET);
+
+ /*
+ * Find out if the file is empty, it's easier to handle it as a
+ * special case.
+ */
+ if (vp->m_start.lno == vp->m_stop.lno &&
+ db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ return (v_ia(sp, vp));
+ }
+
+ flags = set_txt_std(sp, vp, 0);
+ sp->showmode = SM_CHANGE;
+
+ /*
+ * 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, VM_LMODE) ? CUT_LINEMODE : 0;
+ if (lmode) {
+ vp->m_start.cno = 0;
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ }
+ }
+ sp->lno = vp->m_start.lno;
+ sp->cno = vp->m_start.cno;
+
+ LOG_CORRECT;
+
+ /*
+ * If not in line mode and changing within a single line, copy the
+ * text and overwrite it.
+ */
+ if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
+ /*
+ * !!!
+ * Historic practice, c did not cut into the numeric buffers,
+ * only the unnamed one.
+ */
+ if (cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ return (v_txt(sp, vp, &vp->m_stop, p, len,
+ 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
+ }
+
+ /*
+ * It's trickier if in line mode or changing over multiple lines. If
+ * we're in line mode 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 delete the text and start inserting.
+ *
+ * !!!
+ * Copy the text. Historic practice, c did not cut into the numeric
+ * buffers, only the unnamed one.
+ */
+ if (cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /* If replacing entire lines and there's leading text. */
+ if (lmode && vp->m_start.cno) {
+ /*
+ * Get a copy of the first line changed, and copy out the
+ * leading text.
+ */
+ if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
+ return (1);
+ GET_SPACE_RET(sp, bp, blen, vp->m_start.cno);
+ memmove(bp, p, vp->m_start.cno);
+ } else
+ bp = NULL;
+
+ /* Delete the text. */
+ if (del(sp, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /* If replacing entire lines, insert a replacement line. */
+ if (lmode) {
+ if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno))
+ return (1);
+ sp->lno = vp->m_start.lno;
+ len = sp->cno = vp->m_start.cno;
+ }
+
+ /* Get the line we're editing. */
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ len = 0;
+ }
+
+ /* Check to see if we're appending to the line. */
+ if (vp->m_start.cno >= len)
+ LF_SET(TXT_APPENDEOL);
+
+ rval = v_txt(sp, vp, NULL, p, len,
+ 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags);
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * v_Replace -- [count]R
+ * Overwrite multiple characters.
+ *
+ * PUBLIC: int v_Replace __P((SCR *, VICMD *));
+ */
+int
+v_Replace(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_int32_t flags;
+ int isempty;
+ char *p;
+
+ flags = set_txt_std(sp, vp, 0);
+ sp->showmode = SM_REPLACE;
+
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+
+ return (v_txt(sp, vp, &vp->m_stop, p, len,
+ 0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
+}
+
+/*
+ * v_subst -- [buffer][count]s
+ * Substitute characters.
+ *
+ * PUBLIC: int v_subst __P((SCR *, VICMD *));
+ */
+int
+v_subst(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ u_int32_t flags;
+ int isempty;
+ char *p;
+
+ flags = set_txt_std(sp, vp, 0);
+ sp->showmode = SM_CHANGE;
+
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno =
+ vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (vp->m_stop.cno > len - 1)
+ vp->m_stop.cno = len - 1;
+
+ if (p != NULL && cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+
+ return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags));
+}
+
+/*
+ * set_txt_std --
+ * Initialize text processing flags.
+ */
+static u_int32_t
+set_txt_std(sp, vp, flags)
+ SCR *sp;
+ VICMD *vp;
+ u_int32_t flags;
+{
+ LF_SET(TXT_CNTRLT |
+ TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE);
+
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+
+ 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_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+ if (O_ISSET(sp, O_SHOWMATCH))
+ LF_SET(TXT_SHOWMATCH);
+ if (F_ISSET(sp, SC_SCRIPT))
+ LF_SET(TXT_CR);
+ if (O_ISSET(sp, O_TTYWERASE))
+ LF_SET(TXT_TTYWERASE);
+
+ /*
+ * !!!
+ * Mapped keys were sometimes unaffected by the wrapmargin option
+ * in the historic 4BSD vi. Consider the following commands, where
+ * each is executed on an empty line, in an 80 column screen, with
+ * the wrapmargin value set to 60.
+ *
+ * aABC DEF <ESC>....
+ * :map K aABC DEF ^V<ESC><CR>KKKKK
+ * :map K 5aABC DEF ^V<ESC><CR>K
+ *
+ * The first and second commands are affected by wrapmargin. The
+ * third is not. (If the inserted text is itself longer than the
+ * wrapmargin value, i.e. if the "ABC DEF " string is replaced by
+ * something that's longer than 60 columns from the beginning of
+ * the line, the first two commands behave as before, but the third
+ * command gets fairly strange.) The problem is that people wrote
+ * macros that depended on the third command NOT being affected by
+ * wrapmargin, as in this gem which centers lines:
+ *
+ * map #c $mq81a ^V^[81^V^V|D`qld0:s/ / /g^V^M$p
+ *
+ * For compatibility reasons, we try and make it all work here. I
+ * offer no hope that this is right, but it's probably pretty close.
+ *
+ * XXX
+ * Once I work my courage up, this is all gonna go away. It's too
+ * evil to survive.
+ */
+ if ((O_ISSET(sp, O_WRAPLEN) || O_ISSET(sp, O_WRAPMARGIN)) &&
+ (!MAPPED_KEYS_WAITING(sp) || !F_ISSET(vp, VC_C1SET)))
+ LF_SET(TXT_WRAPMARGIN);
+ return (flags);
+}
diff --git a/contrib/nvi/vi/v_left.c b/contrib/nvi/vi/v_left.c
new file mode 100644
index 000000000000..cfed80c74bf4
--- /dev/null
+++ b/contrib/nvi/vi/v_left.c
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_left.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_left -- [count]^H, [count]h
+ * Move left by columns.
+ *
+ * PUBLIC: int v_left __P((SCR *, VICMD *));
+ */
+int
+v_left(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t cnt;
+
+ /*
+ * !!!
+ * The ^H and h commands always failed in the first column.
+ */
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /* Find the end of the range. */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.cno > cnt)
+ vp->m_stop.cno = vp->m_start.cno - cnt;
+ else
+ vp->m_stop.cno = 0;
+
+ /*
+ * All commands move to the end of the range. Motion commands
+ * adjust the starting point to the character before the current
+ * one.
+ */
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_cfirst -- [count]_
+ * Move to the first non-blank character in a line.
+ *
+ * PUBLIC: int v_cfirst __P((SCR *, VICMD *));
+ */
+int
+v_cfirst(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t cnt, lno;
+
+ /*
+ * !!!
+ * If the _ is a motion component, it makes the command a line motion
+ * e.g. "d_" deletes the line. It also means that the cursor doesn't
+ * move.
+ *
+ * The _ command never failed in the first column.
+ */
+ if (ISMOTION(vp))
+ F_SET(vp, VM_LMODE);
+ /*
+ * !!!
+ * Historically a specified count makes _ move down count - 1
+ * rows, so, "3_" is the same as "2j_".
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt != 1) {
+ --vp->count;
+ return (v_down(sp, vp));
+ }
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case _ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * !!!
+ * The _ command has to fail if the file is empty and we're doing
+ * a delete. If deleting line 1, and 0 is the first nonblank,
+ * make the check.
+ */
+ if (vp->m_stop.lno == 1 &&
+ vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) {
+ if (db_last(sp, &lno))
+ return (1);
+ if (lno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+ }
+
+ /*
+ * Delete and non-motion commands move to the end of the range,
+ * yank stays at the start. Ignore others.
+ */
+ vp->m_final =
+ ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_first -- ^
+ * Move to the first non-blank character in this line.
+ *
+ * PUBLIC: int v_first __P((SCR *, VICMD *));
+ */
+int
+v_first(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * !!!
+ * 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.
+ */
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case ^ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * !!!
+ * The ^ command succeeded if used as a command when the cursor was
+ * on the first non-blank in the line, but failed if used as a motion
+ * component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == vp->m_stop.cno) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * Delete and yank stay at the start. Motion commands adjust the
+ * ending point to the character before the current ending charcter.
+ *
+ * If moving left, all commands move to the end of the range. Motion
+ * commands adjust the starting point to the character before the
+ * current starting character.
+ */
+ if (vp->m_start.cno < vp->m_stop.cno)
+ if (ISMOTION(vp)) {
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ else {
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ }
+ 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.
+ *
+ * PUBLIC: int v_ncol __P((SCR *, VICMD *));
+ */
+int
+v_ncol(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
+ --vp->count;
+ vp->m_stop.cno =
+ vs_colpos(sp, vp->m_start.lno, (size_t)vp->count);
+ /*
+ * !!!
+ * The | command succeeded if used as a command and the cursor
+ * didn't move, but failed if used as a motion component in the
+ * same situation.
+ */
+ if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+ } else {
+ /*
+ * !!!
+ * The | command succeeded if used as a command in column 0
+ * without a count, but failed if used as a motion component
+ * in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ }
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * Delete and yank stay at the start. Motion commands adjust the
+ * ending point to the character before the current ending charcter.
+ *
+ * If moving left, all commands move to the end of the range. Motion
+ * commands adjust the starting point to the character before the
+ * current starting character.
+ */
+ if (vp->m_start.cno < vp->m_stop.cno)
+ if (ISMOTION(vp)) {
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ else {
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ }
+ return (0);
+}
+
+/*
+ * v_zero -- 0
+ * Move to the first column on this line.
+ *
+ * PUBLIC: int v_zero __P((SCR *, VICMD *));
+ */
+int
+v_zero(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * !!!
+ * The 0 command succeeded if used as a command in the first column
+ * but failed if used as a motion component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * All commands move to the end of the range. Motion commands
+ * adjust the starting point to the character before the current
+ * one.
+ */
+ vp->m_stop.cno = 0;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_mark.c b/contrib/nvi/vi/v_mark.c
new file mode 100644
index 000000000000..447430efabe8
--- /dev/null
+++ b/contrib/nvi/vi/v_mark.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_mark.c 10.8 (Berkeley) 9/20/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_mark -- m[a-z]
+ * Set a mark.
+ *
+ * PUBLIC: int v_mark __P((SCR *, VICMD *));
+ */
+int
+v_mark(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (mark_set(sp, vp->character, &vp->m_start, 1));
+}
+
+enum which {BQMARK, FQMARK};
+static int mark __P((SCR *, VICMD *, enum which));
+
+
+/*
+ * v_bmark -- `['`a-z]
+ * Move to a mark.
+ *
+ * Moves to a mark, setting both row and column.
+ *
+ * !!!
+ * 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.
+ *
+ * PUBLIC: int v_bmark __P((SCR *, VICMD *));
+ */
+int
+v_bmark(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (mark(sp, vp, BQMARK));
+}
+
+/*
+ * v_fmark -- '['`a-z]
+ * Move to a mark.
+ *
+ * Move to the first nonblank character of the line containing the mark.
+ *
+ * PUBLIC: int v_fmark __P((SCR *, VICMD *));
+ */
+int
+v_fmark(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (mark(sp, vp, FQMARK));
+}
+
+/*
+ * mark --
+ * Mark commands.
+ */
+static int
+mark(sp, vp, cmd)
+ SCR *sp;
+ VICMD *vp;
+ enum which cmd;
+{
+ dir_t dir;
+ MARK m;
+ size_t len;
+
+ if (mark_get(sp, vp->character, &vp->m_stop, M_BERR))
+ return (1);
+
+ /*
+ * !!!
+ * Historically, BQMARKS for character positions that no longer
+ * existed acted as FQMARKS.
+ *
+ * FQMARKS move to the first non-blank.
+ */
+ switch (cmd) {
+ case BQMARK:
+ if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
+ return (1);
+ if (vp->m_stop.cno < len ||
+ vp->m_stop.cno == len && len == 0)
+ break;
+
+ if (ISMOTION(vp))
+ F_SET(vp, VM_LMODE);
+ cmd = FQMARK;
+ /* FALLTHROUGH */
+ case FQMARK:
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ /* Non-motion commands move to the end of the range. */
+ if (!ISMOTION(vp)) {
+ vp->m_final = vp->m_stop;
+ return (0);
+ }
+
+ /*
+ * !!!
+ * If a motion component to a BQMARK, the cursor has to move.
+ */
+ if (cmd == BQMARK &&
+ vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+
+ /*
+ * If the motion is in the reverse direction, switch the start and
+ * stop MARK's so that it's in a forward direction. (There's no
+ * reason for this other than to make the tests below easier. The
+ * code in vi.c:vi() would have done the switch.) Both forward
+ * and backward motions can happen for any kind of search command.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno) {
+ m = vp->m_start;
+ vp->m_start = vp->m_stop;
+ vp->m_stop = m;
+ dir = BACKWARD;
+ } else
+ dir = FORWARD;
+
+ /*
+ * Yank cursor motion, when associated with marks as motion commands,
+ * historically behaved as follows:
+ *
+ * ` motion ' motion
+ * Line change? Line change?
+ * Y N Y N
+ * -------------- ---------------
+ * FORWARD: | NM NM | NM NM
+ * | |
+ * BACKWARD: | M M | M NM(1)
+ *
+ * where NM means the cursor didn't move, and M means the cursor
+ * moved to the mark.
+ *
+ * As the cursor was usually moved for yank commands associated
+ * with backward motions, this implementation regularizes it by
+ * changing the NM at position (1) to be an M. This makes mark
+ * motions match search motions, which is probably A Good Thing.
+ *
+ * Delete cursor motion was always to the start of the text region,
+ * regardless. Ignore other motion commands.
+ */
+#ifdef HISTORICAL_PRACTICE
+ if (ISCMD(vp->rkp, 'y')) {
+ if ((cmd == BQMARK ||
+ cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno) &&
+ (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno))
+ vp->m_final = vp->m_stop;
+ } else if (ISCMD(vp->rkp, 'd'))
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno)
+ vp->m_final = vp->m_stop;
+#else
+ vp->m_final = vp->m_start;
+#endif
+
+ /*
+ * Forward marks are always line oriented, and it's set in the
+ * vcmd.c table.
+ */
+ if (cmd == FQMARK)
+ return (0);
+
+ /*
+ * BQMARK'S moving backward and starting at column 0, and ones moving
+ * forward and ending at column 0 are corrected to the last column of
+ * the previous line. Otherwise, adjust the starting/ending point to
+ * the character before the current one (this is safe because we know
+ * the search had to move to succeed).
+ *
+ * Mark motions become line mode opertions if they start at the first
+ * nonblank and end at column 0 of another line.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
+ if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_stop.cno = len ? len - 1 : 0;
+ len = 0;
+ if (nonblank(sp, vp->m_start.lno, &len))
+ return (1);
+ if (vp->m_start.cno <= len)
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_stop.cno;
+
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_match.c b/contrib/nvi/vi/v_match.c
new file mode 100644
index 000000000000..3996560aaa73
--- /dev/null
+++ b/contrib/nvi/vi/v_match.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_match.c 10.8 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_match -- %
+ * Search to matching character.
+ *
+ * PUBLIC: int v_match __P((SCR *, VICMD *));
+ */
+int
+v_match(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ VCS cs;
+ MARK *mp;
+ size_t cno, len, off;
+ int cnt, isempty, matchc, startc, (*gc)__P((SCR *, VCS *));
+ char *p;
+
+ /*
+ * !!!
+ * Historic practice; ignore the count.
+ *
+ * !!!
+ * Historical practice was to search for the initial character in the
+ * forward direction only.
+ */
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (isempty)
+ goto nomatch;
+ return (1);
+ }
+ for (off = vp->m_start.cno;; ++off) {
+ if (off >= len) {
+nomatch: msgq(sp, M_BERR, "184|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;
+ case '<':
+ matchc = '>';
+ gc = cs_next;
+ break;
+ case '>':
+ matchc = '<';
+ gc = cs_prev;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = off;
+ if (cs_init(sp, &cs))
+ return (1);
+ for (cnt = 1;;) {
+ if (gc(sp, &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, "185|Matching character not found");
+ return (1);
+ }
+
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * Delete and yank stay at the start.
+ *
+ * If moving left, all commands move to the end of the range.
+ *
+ * !!!
+ * Don't correct for leftward movement -- historic vi deleted the
+ * starting cursor position when deleting to a match.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno < vp->m_stop.cno)
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ else
+ vp->m_final = vp->m_stop;
+
+ /*
+ * !!!
+ * If the motion is across lines, and the earliest cursor position
+ * is at or before any non-blank characters in the line, i.e. the
+ * movement is cutting all of the line's text, and the later cursor
+ * position has nothing other than whitespace characters between it
+ * and the end of its line, the buffer is in line mode.
+ */
+ if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
+ return (0);
+ mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
+ if (mp->cno != 0) {
+ cno = 0;
+ if (nonblank(sp, mp->lno, &cno))
+ return (1);
+ if (cno < mp->cno)
+ return (0);
+ }
+ mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
+ if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
+ return (1);
+ for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
+ if (!isblank(*p))
+ return (0);
+ F_SET(vp, VM_LMODE);
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_paragraph.c b/contrib/nvi/vi/v_paragraph.c
new file mode 100644
index 000000000000..762e38e01671
--- /dev/null
+++ b/contrib/nvi/vi/v_paragraph.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_paragraph.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+#define INTEXT_CHECK { \
+ if (len == 0 || v_isempty(p, len)) { \
+ if (!--cnt) \
+ goto found; \
+ pstate = P_INBLANK; \
+ } \
+ /* \
+ * !!! \
+ * Historic documentation (USD:15-11, 4.2) said that formfeed \
+ * characters (^L) in the first column delimited paragraphs. \
+ * The historic vi code mentions formfeed characters, but never \
+ * implements them. It seems reasonable, do it. \
+ */ \
+ if (p[0] == '\014') { \
+ if (!--cnt) \
+ goto found; \
+ continue; \
+ } \
+ if (p[0] != '.' || len < 2) \
+ continue; \
+ for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2) \
+ if (lp[0] == p[1] && \
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) && \
+ !--cnt) \
+ goto found; \
+}
+
+/*
+ * v_paragraphf -- [count]}
+ * Move forward count paragraphs.
+ *
+ * Paragraphs are empty lines after text, formfeed characters, or values
+ * from the paragraph or section options.
+ *
+ * PUBLIC: int v_paragraphf __P((SCR *, VICMD *));
+ */
+int
+v_paragraphf(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t lastlen, len;
+ recno_t cnt, lastlno, lno;
+ int isempty;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * If the starting cursor position is at or before any non-blank
+ * characters in the line, i.e. the movement is cutting all of the
+ * line's text, the buffer is in line mode. It's a lot easier to
+ * check here, because we know that the end is going to be the start
+ * or end of a line.
+ *
+ * This was historical practice in vi, with a single exception. If
+ * the paragraph movement was from the start of the last line to EOF,
+ * then all the characters were deleted from the last line, but the
+ * line itself remained. If somebody complains, don't pause, don't
+ * hesitate, just hit them.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ else {
+ vp->m_stop = vp->m_start;
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ if (vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+ }
+
+ /* Figure out what state we're currently in. */
+ lno = vp->m_start.lno;
+ if (db_get(sp, lno, 0, &p, &len))
+ 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 (db_get(sp, ++lno, 0, &p, &len))
+ goto eof;
+ switch (pstate) {
+ case P_INTEXT:
+ INTEXT_CHECK;
+ break;
+ case P_INBLANK:
+ if (len == 0 || v_isempty(p, len))
+ break;
+ if (--cnt) {
+ pstate = P_INTEXT;
+ break;
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range,
+ * delete and yank stay at the start. Ignore others.
+ * Adjust the end of the range for motion commands;
+ * historically, a motion component was to the end of
+ * the previous line, whereas the movement command was
+ * to the start of the new "paragraph".
+ */
+found: if (ISMOTION(vp)) {
+ vp->m_stop.lno = lastlno;
+ vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
+ vp->m_final = vp->m_start;
+ } else {
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ vp->m_final = vp->m_stop;
+ }
+ return (0);
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * !!!
+ * Adjust end of the range for motion commands; EOF is a movement
+ * sink. The } command historically moved to the end of the last
+ * line, not the beginning, from any position before the end of the
+ * last line. It also historically worked on empty files, so we
+ * have to make it okay.
+ */
+eof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) {
+ if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ vp->m_start.cno = 0;
+ return (0);
+ }
+ if (vp->m_start.cno == (len ? len - 1 : 0)) {
+ v_eof(sp, NULL);
+ return (1);
+ }
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range, delete
+ * and yank stay at the start. Ignore others.
+ *
+ * If deleting the line (which happens if deleting to EOF), then
+ * cursor movement is to the first nonblank.
+ */
+ if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ vp->m_stop.lno = lno - 1;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_paragraphb -- [count]{
+ * Move backward count paragraphs.
+ *
+ * PUBLIC: int v_paragraphb __P((SCR *, VICMD *));
+ */
+int
+v_paragraphb(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * Check for SOF. The historic vi didn't complain if users hit SOF
+ * repeatedly, unless it was part of a motion command. There is no
+ * question but that Emerson's editor of choice was vi.
+ *
+ * The { command historically moved to the beginning of the first
+ * line if invoked on the first line.
+ *
+ * !!!
+ * If the starting cursor position is in the first column (backward
+ * paragraph movements did NOT historically pay attention to non-blank
+ * characters) i.e. the movement is cutting the entire line, the buffer
+ * is in line mode. Cuts from the beginning of the line also did not
+ * cut the current line, but started at the previous EOL.
+ *
+ * Correct for a left motion component while we're thinking about it.
+ */
+ lno = vp->m_start.lno;
+
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0) {
+ if (vp->m_start.lno == 1) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ } else
+ --vp->m_start.lno;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+
+ if (vp->m_start.lno <= 1)
+ goto sof;
+
+ /* Figure out what state we're currently in. */
+ if (db_get(sp, lno, 0, &p, &len))
+ 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;
+
+ /*
+ * !!!
+ * If the starting cursor is past the first column,
+ * the current line is checked for a paragraph.
+ */
+ if (vp->m_start.cno > 0)
+ ++lno;
+ }
+
+ for (;;) {
+ if (db_get(sp, --lno, 0, &p, &len))
+ goto sof;
+ switch (pstate) {
+ case P_INTEXT:
+ INTEXT_CHECK;
+ break;
+ case P_INBLANK:
+ if (len != 0 && !v_isempty(p, len)) {
+ if (!--cnt)
+ goto found;
+ pstate = P_INTEXT;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /* SOF is a movement sink. */
+sof: lno = 1;
+
+found: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+
+ /*
+ * All commands move to the end of the range. (We already
+ * adjusted the start of the range for motion commands).
+ */
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_buildps --
+ * Build the paragraph command search pattern.
+ *
+ * PUBLIC: int v_buildps __P((SCR *, char *, char *));
+ */
+int
+v_buildps(sp, p_p, s_p)
+ SCR *sp;
+ char *p_p, *s_p;
+{
+ VI_PRIVATE *vip;
+ size_t p_len, s_len;
+ char *p;
+
+ /*
+ * The vi paragraph command searches for either a paragraph or
+ * section option macro.
+ */
+ p_len = p_p == NULL ? 0 : strlen(p_p);
+ s_len = s_p == 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->ps != NULL)
+ free(vip->ps);
+
+ 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->ps = p;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_put.c b/contrib/nvi/vi/v_put.c
new file mode 100644
index 000000000000..77220ea14ff9
--- /dev/null
+++ b/contrib/nvi/vi/v_put.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_put.c 10.5 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static void inc_buf __P((SCR *, VICMD *));
+
+/*
+ * v_Put -- [buffer]P
+ * Insert the contents of the buffer before the cursor.
+ *
+ * PUBLIC: int v_Put __P((SCR *, VICMD *));
+ */
+int
+v_Put(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ u_long cnt;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ /*
+ * !!!
+ * Historic vi did not support a count with the 'p' and 'P'
+ * commands. It's useful, so we do.
+ */
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (put(sp, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 0))
+ return (1);
+ vp->m_start = vp->m_final;
+ if (INTERRUPTED(sp))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * v_put -- [buffer]p
+ * Insert the contents of the buffer after the cursor.
+ *
+ * PUBLIC: int v_put __P((SCR *, VICMD *));
+ */
+int
+v_put(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ u_long cnt;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ /*
+ * !!!
+ * Historic vi did not support a count with the 'p' and 'P'
+ * commands. It's useful, so we do.
+ */
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (put(sp, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 1))
+ return (1);
+ vp->m_start = vp->m_final;
+ if (INTERRUPTED(sp))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * !!!
+ * 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;
+ VICMD *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/contrib/nvi/vi/v_redraw.c b/contrib/nvi/vi/v_redraw.c
new file mode 100644
index 000000000000..4c965c713913
--- /dev/null
+++ b/contrib/nvi/vi/v_redraw.c
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_redraw.c 10.6 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_redraw -- ^L, ^R
+ * Redraw the screen.
+ *
+ * PUBLIC: int v_redraw __P((SCR *, VICMD *));
+ */
+int
+v_redraw(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (sp->gp->scr_refresh(sp, 1));
+}
diff --git a/contrib/nvi/vi/v_replace.c b/contrib/nvi/vi/v_replace.c
new file mode 100644
index 000000000000..a4712b683a91
--- /dev/null
+++ b/contrib/nvi/vi/v_replace.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_replace.c 10.17 (Berkeley) 6/30/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_replace -- [count]r<char>
+ *
+ * !!!
+ * 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. "r<escape>" did cancel the command,
+ * but "r<literal><escape>" erased a single character. To enter a literal
+ * <literal> character, it required three <literal> characters after the
+ * command. This may not be right, but at least it's not insane.
+ *
+ * PUBLIC: int v_replace __P((SCR *, VICMD *));
+ */
+int
+v_replace(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EVENT ev;
+ VI_PRIVATE *vip;
+ TEXT *tp;
+ size_t blen, len;
+ u_long cnt;
+ int quote, rval;
+ char *bp, *p;
+
+ vip = VIP(sp);
+
+ /*
+ * 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 (vi beeped before the replacement
+ * character was even entered).
+ * 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, which would be a bit odd.
+ * 3: Replacing with a <newline> has somewhat odd semantics.
+ */
+ if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
+ return (1);
+ if (len == 0) {
+ msgq(sp, M_BERR, "186|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 append replacement characters
+ * to the line, but I see no compelling reason to do so. Check this
+ * before we get the character to match historic practice, where Nr
+ * failed immediately if there were less than N characters from the
+ * cursor to the end of the line.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = vp->m_start.cno + cnt - 1;
+ if (vp->m_stop.cno > len - 1) {
+ v_eol(sp, &vp->m_start);
+ return (1);
+ }
+
+ /*
+ * If it's not a repeat, reset the current mode and get a replacement
+ * character.
+ */
+ quote = 0;
+ if (!F_ISSET(vp, VC_ISDOT)) {
+ sp->showmode = SM_REPLACE;
+ if (vs_refresh(sp, 0))
+ return (1);
+next: if (v_event_get(sp, &ev, 0, 0))
+ return (1);
+
+ switch (ev.e_event) {
+ case E_CHARACTER:
+ /*
+ * <literal_next> means escape the next character.
+ * <escape> means they changed their minds.
+ */
+ if (!quote) {
+ if (ev.e_value == K_VLNEXT) {
+ quote = 1;
+ goto next;
+ }
+ if (ev.e_value == K_ESCAPE)
+ return (0);
+ }
+ vip->rlast = ev.e_c;
+ vip->rvalue = ev.e_value;
+ break;
+ case E_ERR:
+ case E_EOF:
+ F_SET(sp, SC_EXIT_FORCE);
+ return (1);
+ case E_INTERRUPT:
+ /* <interrupt> means they changed their minds. */
+ return (0);
+ case E_WRESIZE:
+ /* <resize> interrupts the input mode. */
+ v_emsg(sp, NULL, VIM_WRESIZE);
+ return (0);
+ case E_REPAINT:
+ if (vs_repaint(sp, &ev))
+ return (1);
+ goto next;
+ default:
+ v_event_err(sp, &ev);
+ return (0);
+ }
+ }
+
+ /* Copy the line. */
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ p = bp;
+
+ /*
+ * Versions of nvi before 1.57 created N new lines when they replaced
+ * N characters with <carriage-return> or <newline> characters. This
+ * is different from the historic vi, which replaced N characters with
+ * a single new line. Users complained, so we match historic practice.
+ */
+ if (!quote && vip->rvalue == K_CR || vip->rvalue == K_NL) {
+ /* Set return line. */
+ vp->m_stop.lno = vp->m_start.lno + 1;
+ vp->m_stop.cno = 0;
+
+ /* The first part of the current line. */
+ if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno))
+ goto err_ret;
+
+ /*
+ * The rest of the current line. And, of course, now it gets
+ * tricky. If there are characters left in the line and if
+ * the autoindent edit option is set, white space after the
+ * replaced character is discarded, autoindent is applied, and
+ * the cursor moves to the last indent character.
+ */
+ p += vp->m_start.cno + cnt;
+ len -= vp->m_start.cno + cnt;
+ if (len != 0 && O_ISSET(sp, O_AUTOINDENT))
+ for (; len && isblank(*p); --len, ++p);
+
+ if ((tp = text_init(sp, p, len, len)) == NULL)
+ goto err_ret;
+
+ if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) {
+ if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp))
+ goto err_ret;
+ vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
+ } else
+ vp->m_stop.cno = 0;
+
+ vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
+ if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len))
+err_ret: rval = 1;
+ else {
+ text_free(tp);
+ rval = 0;
+ }
+ } else {
+ memset(bp + vp->m_start.cno, vip->rlast, cnt);
+ rval = db_set(sp, vp->m_start.lno, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ vp->m_final = vp->m_stop;
+ return (rval);
+}
diff --git a/contrib/nvi/vi/v_right.c b/contrib/nvi/vi/v_right.c
new file mode 100644
index 000000000000..c2f349eb2328
--- /dev/null
+++ b/contrib/nvi/vi/v_right.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_right.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_right -- [count]' ', [count]l
+ * Move right by columns.
+ *
+ * PUBLIC: int v_right __P((SCR *, VICMD *));
+ */
+int
+v_right(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ int isempty;
+
+ if (db_eget(sp, vp->m_start.lno, NULL, &len, &isempty)) {
+ if (isempty)
+ goto eol;
+ return (1);
+ }
+
+ /* It's always illegal to move right on empty lines. */
+ if (len == 0) {
+eol: v_eol(sp, NULL);
+ return (1);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range. Delete and
+ * yank stay at the start. Ignore others. Adjust the end of the
+ * range for motion commands.
+ *
+ * !!!
+ * Historically, "[cdsy]l" worked at the end of a line. Also,
+ * EOL is a count sink.
+ */
+ vp->m_stop.cno = vp->m_start.cno +
+ (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (vp->m_start.cno == len - 1 && !ISMOTION(vp)) {
+ v_eol(sp, NULL);
+ return (1);
+ }
+ if (vp->m_stop.cno >= len) {
+ vp->m_stop.cno = len - 1;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ } else if (ISMOTION(vp)) {
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_dollar -- [count]$
+ * Move to the last column.
+ *
+ * PUBLIC: int v_dollar __P((SCR *, VICMD *));
+ */
+int
+v_dollar(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ int isempty;
+
+ /*
+ * !!!
+ * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
+ */
+ if ((F_ISSET(vp, VC_C1SET) ? vp->count : 1) != 1) {
+ /*
+ * !!!
+ * 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.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_start.lno, &vp->m_stop.cno))
+ return (1);
+ if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+
+ --vp->count;
+ if (v_down(sp, vp))
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically, it was illegal to use $ as a motion command on
+ * an empty line. Unfortunately, even though C was historically
+ * aliased to c$, it (and not c$) was special cased to work on
+ * empty lines. Since we alias C to c$ too, we have a problem.
+ * To fix it, we let c$ go through, on the assumption that it's
+ * not a problem for it to work.
+ */
+ if (db_eget(sp, vp->m_stop.lno, NULL, &len, &isempty)) {
+ if (!isempty)
+ return (1);
+ len = 0;
+ }
+
+ if (len == 0) {
+ if (ISMOTION(vp) && !ISCMD(vp->rkp, 'c')) {
+ v_eol(sp, NULL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range. Delete
+ * and yank stay at the start. Ignore others.
+ */
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_screen.c b/contrib/nvi/vi/v_screen.c
new file mode 100644
index 000000000000..85cd1e32bca0
--- /dev/null
+++ b/contrib/nvi/vi/v_screen.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_screen.c 10.10 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_screen -- ^W
+ * Switch screens.
+ *
+ * PUBLIC: int v_screen __P((SCR *, VICMD *));
+ */
+int
+v_screen(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * You can't leave a colon command-line edit window -- it's not that
+ * it won't work, but it gets real weird, real fast when you execute
+ * a colon command out of a window that was forked from a window that's
+ * now backgrounded... You get the idea.
+ */
+ if (F_ISSET(sp, SC_COMEDIT)) {
+ msgq(sp, M_ERR,
+ "308|Enter <CR> to execute a command, :q to exit");
+ return (1);
+ }
+
+ /*
+ * 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, "187|No other screen to switch to");
+ return (1);
+ } else
+ sp->nextdisp = sp->gp->dq.cqh_first;
+
+ F_SET(sp->nextdisp, SC_STATUS);
+ F_SET(sp, SC_SSWITCH | SC_STATUS);
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_scroll.c b/contrib/nvi/vi/v_scroll.c
new file mode 100644
index 000000000000..92def4b3e48d
--- /dev/null
+++ b/contrib/nvi/vi/v_scroll.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_scroll.c 10.9 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static void goto_adjust __P((VICMD *));
+
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * v_lgoto -- [count]G
+ * Go to first non-blank character of the line count, the last line
+ * of the file by default.
+ *
+ * PUBLIC: int v_lgoto __P((SCR *, VICMD *));
+ */
+int
+v_lgoto(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t nlines;
+
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (!db_exist(sp, vp->count)) {
+ /*
+ * !!!
+ * Historically, 1G was legal in an empty file.
+ */
+ if (vp->count == 1) {
+ if (db_last(sp, &nlines))
+ return (1);
+ if (nlines == 0)
+ return (0);
+ }
+ v_eof(sp, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->count;
+ } else {
+ if (db_last(sp, &nlines))
+ return (1);
+ vp->m_stop.lno = nlines ? nlines : 1;
+ }
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_home -- [count]H
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the top of the screen, 0 by default.
+ *
+ * PUBLIC: int v_home __P((SCR *, VICMD *));
+ */
+int
+v_home(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ if (vs_sm_position(sp, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_middle -- M
+ * Move to the first non-blank character of the logical line
+ * in the middle of the screen.
+ *
+ * PUBLIC: int v_middle __P((SCR *, VICMD *));
+ */
+int
+v_middle(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * 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.
+ */
+ if (vs_sm_position(sp, &vp->m_stop, 0, P_MIDDLE))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_bottom -- [count]L
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the bottom of the screen, 0 by default.
+ *
+ * PUBLIC: int v_bottom __P((SCR *, VICMD *));
+ */
+int
+v_bottom(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ if (vs_sm_position(sp, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+static void
+goto_adjust(vp)
+ VICMD *vp;
+{
+ /* Guess that it's the end of the range. */
+ vp->m_final = vp->m_stop;
+
+ /*
+ * Non-motion commands move the cursor to the end of the range, and
+ * then to the NEXT nonblank of the line. Historic vi always moved
+ * to the first nonblank in the line; since the H, M, and L commands
+ * are logical motions in this implementation, we do the next nonblank
+ * so that it looks approximately the same to the user. To make this
+ * happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table.
+ *
+ * If it's a motion, it's more complicated. The best possible solution
+ * is probably to display the first nonblank of the line the cursor
+ * will eventually rest on. This is tricky, particularly given that if
+ * the associated command is a delete, we don't yet know what line that
+ * will be. So, we clear the VM_RCM_SETNNB flag, and set the first
+ * nonblank flag (VM_RCM_SETFNB). Note, if the lines are sufficiently
+ * long, this can cause the cursor to warp out of the screen. It's too
+ * hard to fix.
+ *
+ * XXX
+ * The G command is always first nonblank, so it's okay to reset it.
+ */
+ if (ISMOTION(vp)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ } else
+ return;
+
+ /*
+ * If moving backward in the file, delete and yank move to the end
+ * of the range, unless the line didn't change, in which case yank
+ * doesn't move. If moving forward in the file, delete and yank
+ * stay at the start of the range. Ignore others.
+ */
+ if (vp->m_stop.lno < vp->m_start.lno ||
+ vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno < vp->m_start.cno) {
+ if (ISCMD(vp->rkp, 'y') && vp->m_stop.lno == vp->m_start.lno)
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_start;
+}
+
+/*
+ * v_up -- [count]^P, [count]k, [count]-
+ * Move up by lines.
+ *
+ * PUBLIC: int v_up __P((SCR *, VICMD *));
+ */
+int
+v_up(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t lno;
+
+ lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.lno <= lno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->m_start.lno - lno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_cr -- [count]^M
+ * In a script window, send the line to the shell.
+ * In a regular window, move down by lines.
+ *
+ * PUBLIC: int v_cr __P((SCR *, VICMD *));
+ */
+int
+v_cr(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /* If it's a colon command-line edit window, it's an ex command. */
+ if (F_ISSET(sp, SC_COMEDIT))
+ return (v_ecl_exec(sp));
+
+ /* If it's a script window, exec the line. */
+ if (F_ISSET(sp, SC_SCRIPT))
+ return (sscr_exec(sp, vp->m_start.lno));
+
+ /* Otherwise, it's the same as v_down(). */
+ return (v_down(sp, vp));
+}
+
+/*
+ * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
+ * Move down by lines.
+ *
+ * PUBLIC: int v_down __P((SCR *, VICMD *));
+ */
+int
+v_down(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t lno;
+
+ lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (!db_exist(sp, lno)) {
+ v_eof(sp, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = lno;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpageup -- [count]^U
+ * Page up half screens.
+ *
+ * PUBLIC: int v_hpageup __P((SCR *, VICMD *));
+ */
+int
+v_hpageup(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * Half screens always succeed unless already at SOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command
+ * ultimately failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ sp->defscroll = vp->count;
+ if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_U))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpagedown -- [count]^D
+ * Page down half screens.
+ *
+ * PUBLIC: int v_hpagedown __P((SCR *, VICMD *));
+ */
+int
+v_hpagedown(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * Half screens always succeed unless already at EOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command
+ * ultimately failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ sp->defscroll = vp->count;
+ if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_D))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * 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.
+ *
+ * PUBLIC: int v_pagedown __P((SCR *, VICMD *));
+ */
+int
+v_pagedown(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t offset;
+
+ /*
+ * !!!
+ * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
+ *
+ * top_line = top_line + count * (window - 2);
+ *
+ * which was historically wrong. The correct one is:
+ *
+ * top_line = top_line + count * window - 2;
+ *
+ * i.e. the two line "overlap" was only subtracted once. Which
+ * makes no sense, but then again, an overlap makes no sense for
+ * any screen but the "next" one anyway. We do it the historical
+ * way as there's no good reason to change it.
+ *
+ * If the screen has been split, use the smaller of the current
+ * window size and the window option value.
+ *
+ * It possible for this calculation to be less than 1; move at
+ * least one line.
+ */
+ offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
+ MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
+ offset = offset <= 2 ? 1 : offset - 2;
+ if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_F))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * 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.
+ *
+ * PUBLIC: int v_pageup __P((SCR *, VICMD *));
+ */
+int
+v_pageup(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t offset;
+
+ /*
+ * !!!
+ * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
+ *
+ * top_line = top_line - count * (window - 2);
+ *
+ * which was historically wrong. The correct one is:
+ *
+ * top_line = (top_line - count * window) + 2;
+ *
+ * A simpler expression is that, as with ^F, we scroll exactly:
+ *
+ * count * window - 2
+ *
+ * lines.
+ *
+ * Bizarre. As with ^F, an overlap makes no sense for anything
+ * but the first screen. We do it the historical way as there's
+ * no good reason to change it.
+ *
+ * If the screen has been split, use the smaller of the current
+ * window size and the window option value.
+ *
+ * It possible for this calculation to be less than 1; move at
+ * least one line.
+ */
+ offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
+ MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
+ offset = offset <= 2 ? 1 : offset - 2;
+ if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_B))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_lineup -- [count]^Y
+ * Page up by lines.
+ *
+ * PUBLIC: int v_lineup __P((SCR *, VICMD *));
+ */
+int
+v_lineup(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * The cursor moves down, staying with its original line, unless it
+ * reaches the bottom of the screen.
+ */
+ if (vs_sm_scroll(sp,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_Y))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_linedown -- [count]^E
+ * Page down by lines.
+ *
+ * PUBLIC: int v_linedown __P((SCR *, VICMD *));
+ */
+int
+v_linedown(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * The cursor moves up, staying with its original line, unless it
+ * reaches the top of the screen.
+ */
+ if (vs_sm_scroll(sp,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_E))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_search.c b/contrib/nvi/vi/v_search.c
new file mode 100644
index 000000000000..4f7a2671ab8e
--- /dev/null
+++ b/contrib/nvi/vi/v_search.c
@@ -0,0 +1,515 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_search.c 10.18 (Berkeley) 9/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static int v_exaddr __P((SCR *, VICMD *, dir_t));
+static int v_search __P((SCR *, VICMD *, char *, size_t, u_int, dir_t));
+
+/*
+ * v_srch -- [count]?RE[? offset]
+ * Ex address search backward.
+ *
+ * PUBLIC: int v_searchb __P((SCR *, VICMD *));
+ */
+int
+v_searchb(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (v_exaddr(sp, vp, BACKWARD));
+}
+
+/*
+ * v_searchf -- [count]/RE[/ offset]
+ * Ex address search forward.
+ *
+ * PUBLIC: int v_searchf __P((SCR *, VICMD *));
+ */
+int
+v_searchf(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (v_exaddr(sp, vp, FORWARD));
+}
+
+/*
+ * v_exaddr --
+ * Do a vi search (which is really an ex address).
+ */
+static int
+v_exaddr(sp, vp, dir)
+ SCR *sp;
+ VICMD *vp;
+ dir_t dir;
+{
+ static EXCMDLIST fake = { "search" };
+ EXCMD *cmdp;
+ GS *gp;
+ TEXT *tp;
+ recno_t s_lno;
+ size_t len, s_cno, tlen;
+ int err, nb, type;
+ char *cmd, *t, buf[20];
+
+ /*
+ * !!!
+ * If using the search command as a motion, any addressing components
+ * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
+ */
+ if (F_ISSET(vp, VC_ISDOT))
+ return (v_search(sp, vp,
+ NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));
+
+ /* Get the search pattern. */
+ if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
+ TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
+ (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
+ return (1);
+
+ tp = sp->tiq.cqh_first;
+
+ /* If the user backspaced over the prompt, do nothing. */
+ if (tp->term == TERM_BS)
+ return (1);
+
+ /*
+ * If the user was doing an incremental search, then we've already
+ * updated the cursor and moved to the right location. Return the
+ * correct values, we're done.
+ */
+ if (tp->term == TERM_SEARCH) {
+ vp->m_stop.lno = sp->lno;
+ vp->m_stop.cno = sp->cno;
+ if (ISMOTION(vp))
+ return (v_correct(sp, vp, 0));
+ vp->m_final = vp->m_stop;
+ return (0);
+ }
+
+ /*
+ * If the user entered <escape> or <carriage-return>, the length is
+ * 1 and the right thing will happen, i.e. the prompt will be used
+ * as a command character.
+ *
+ * Build a fake ex command structure.
+ */
+ gp = sp->gp;
+ gp->excmd.cp = tp->lb;
+ gp->excmd.clen = tp->len;
+ F_INIT(&gp->excmd, E_VISEARCH);
+
+ /*
+ * XXX
+ * Warn if the search wraps. This is a pretty special case, but it's
+ * nice feature that wasn't in the original implementations of ex/vi.
+ * (It was added at some point to System V's version.) This message
+ * is only displayed if there are no keys in the queue. The problem is
+ * the command is going to succeed, and the message is informational,
+ * not an error. If a macro displays it repeatedly, e.g., the pattern
+ * only occurs once in the file and wrapscan is set, you lose big. For
+ * example, if the macro does something like:
+ *
+ * :map K /pattern/^MjK
+ *
+ * Each search will display the message, but the following "/pattern/"
+ * will immediately overwrite it, with strange results. The System V
+ * vi displays the "wrapped" message multiple times, but because it's
+ * overwritten each time, it's not as noticeable. As we don't discard
+ * messages, it's a real problem for us.
+ */
+ if (!KEYS_WAITING(sp))
+ F_SET(&gp->excmd, E_SEARCH_WMSG);
+
+ /* Save the current line/column. */
+ s_lno = sp->lno;
+ s_cno = sp->cno;
+
+ /*
+ * !!!
+ * Historically, vi / and ? commands were full-blown ex addresses,
+ * including ';' delimiters, trailing <blank>'s, multiple search
+ * strings (separated by semi-colons) and, finally, full-blown z
+ * commands after the / and ? search strings. (If the search was
+ * being used as a motion, the trailing z command was ignored.
+ * Also, we do some argument checking on the z command, to be sure
+ * that it's not some other random command.) For multiple search
+ * strings, leading <blank>'s at the second and subsequent strings
+ * were eaten as well. This has some (unintended?) side-effects:
+ * the command /ptrn/;3 is legal and results in moving to line 3.
+ * I suppose you could use it to optionally move to line 3...
+ *
+ * !!!
+ * Historically, if any part of the search command failed, the cursor
+ * remained unmodified (even if ; was used). We have to play games
+ * because the underlying ex parser thinks we're modifying the cursor
+ * as we go, but I think we're compatible with historic practice.
+ *
+ * !!!
+ * Historically, the command "/STRING/; " failed, apparently it
+ * confused the parser. We're not that compatible.
+ */
+ cmdp = &gp->excmd;
+ if (ex_range(sp, cmdp, &err))
+ return (1);
+
+ /*
+ * Remember where any remaining command information is, and clean
+ * up the fake ex command.
+ */
+ cmd = cmdp->cp;
+ len = cmdp->clen;
+ gp->excmd.clen = 0;
+
+ if (err)
+ goto err2;
+
+ /* Copy out the new cursor position and make sure it's okay. */
+ switch (cmdp->addrcnt) {
+ case 1:
+ vp->m_stop = cmdp->addr1;
+ break;
+ case 2:
+ vp->m_stop = cmdp->addr2;
+ break;
+ }
+ if (!db_exist(sp, vp->m_stop.lno)) {
+ ex_badaddr(sp, &fake,
+ vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
+ goto err2;
+ }
+
+ /*
+ * !!!
+ * Historic practice is that a trailing 'z' was ignored if it was a
+ * motion command. Should probably be an error, but not worth the
+ * effort.
+ */
+ if (ISMOTION(vp))
+ return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
+
+ /*
+ * !!!
+ * Historically, if it wasn't a motion command, a delta in the search
+ * pattern turns it into a first nonblank movement.
+ */
+ nb = F_ISSET(cmdp, E_DELTA);
+
+ /* Check for the 'z' command. */
+ if (len != 0) {
+ if (*cmd != 'z')
+ goto err1;
+
+ /* No blanks, just like the z command. */
+ for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
+ if (!isdigit(*t))
+ break;
+ if (tlen &&
+ (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
+ ++t;
+ --tlen;
+ type = 1;
+ } else
+ type = 0;
+ if (tlen)
+ goto err1;
+
+ /* The z command will do the nonblank for us. */
+ nb = 0;
+
+ /* Default to z+. */
+ if (!type &&
+ v_event_push(sp, NULL, "+", 1, CH_NOMAP | CH_QUOTED))
+ return (1);
+
+ /* Push the user's command. */
+ if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
+ return (1);
+
+ /* Push line number so get correct z display. */
+ tlen = snprintf(buf,
+ sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
+ if (v_event_push(sp, NULL, buf, tlen, CH_NOMAP | CH_QUOTED))
+ return (1);
+
+ /* Don't refresh until after 'z' happens. */
+ F_SET(VIP(sp), VIP_S_REFRESH);
+ }
+
+ /* Non-motion commands move to the end of the range. */
+ vp->m_final = vp->m_stop;
+ if (nb) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ return (0);
+
+err1: msgq(sp, M_ERR,
+ "188|Characters after search string, line offset and/or z command");
+err2: vp->m_final.lno = s_lno;
+ vp->m_final.cno = s_cno;
+ return (1);
+}
+
+/*
+ * v_searchN -- N
+ * Reverse last search.
+ *
+ * PUBLIC: int v_searchN __P((SCR *, VICMD *));
+ */
+int
+v_searchN(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ dir_t dir;
+
+ switch (sp->searchdir) {
+ case BACKWARD:
+ dir = FORWARD;
+ break;
+ case FORWARD:
+ dir = BACKWARD;
+ break;
+ default:
+ dir = sp->searchdir;
+ break;
+ }
+ return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, dir));
+}
+
+/*
+ * v_searchn -- n
+ * Repeat last search.
+ *
+ * PUBLIC: int v_searchn __P((SCR *, VICMD *));
+ */
+int
+v_searchn(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, sp->searchdir));
+}
+
+/*
+ * v_searchw -- [count]^A
+ * Search for the word under the cursor.
+ *
+ * PUBLIC: int v_searchw __P((SCR *, VICMD *));
+ */
+int
+v_searchw(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t blen, len;
+ int rval;
+ char *bp;
+
+ len = VIP(sp)->klen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
+ GET_SPACE_RET(sp, bp, blen, len);
+ len = snprintf(bp, blen, "%s%s%s", RE_WSTART, VIP(sp)->keyw, RE_WSTOP);
+
+ rval = v_search(sp, vp, bp, len, SEARCH_SET, FORWARD);
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * v_search --
+ * The search commands.
+ */
+static int
+v_search(sp, vp, ptrn, plen, flags, dir)
+ SCR *sp;
+ VICMD *vp;
+ u_int flags;
+ char *ptrn;
+ size_t plen;
+ dir_t dir;
+{
+ /* Display messages. */
+ LF_SET(SEARCH_MSG);
+
+ /* If it's a motion search, offset past end-of-line is okay. */
+ if (ISMOTION(vp))
+ LF_SET(SEARCH_EOL);
+
+ /*
+ * XXX
+ * Warn if the search wraps. See the comment above, in v_exaddr().
+ */
+ if (!KEYS_WAITING(sp))
+ LF_SET(SEARCH_WMSG);
+
+ switch (dir) {
+ case BACKWARD:
+ if (b_search(sp,
+ &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
+ return (1);
+ break;
+ case FORWARD:
+ if (f_search(sp,
+ &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
+ return (1);
+ break;
+ case NOTSET:
+ msgq(sp, M_ERR, "189|No previous search pattern");
+ return (1);
+ default:
+ abort();
+ }
+
+ /* Correct motion commands, otherwise, simply move to the location. */
+ if (ISMOTION(vp)) {
+ if (v_correct(sp, vp, 0))
+ return(1);
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_correct --
+ * Handle command with a search as the motion.
+ *
+ * !!!
+ * Historically, commands didn't affect the line searched to/from if the
+ * motion command was a search and the final position was the start/end
+ * of the line. There were some special cases and vi was not consistent;
+ * 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 going to exactly match historic practice.
+ *
+ * PUBLIC: int v_correct __P((SCR *, VICMD *, int));
+ */
+int
+v_correct(sp, vp, isdelta)
+ SCR *sp;
+ VICMD *vp;
+ int isdelta;
+{
+ dir_t dir;
+ MARK m;
+ size_t len;
+
+ /*
+ * !!!
+ * We may have wrapped if wrapscan was set, and we may have returned
+ * to the position where the cursor started. Historic vi didn't cope
+ * with this well. Yank wouldn't beep, but the first put after the
+ * yank would move the cursor right one column (without adding any
+ * text) and the second would put a copy of the current line. The
+ * change and delete commands would beep, but would leave the cursor
+ * on the colon command line. I believe that there are macros that
+ * depend on delete, at least, failing. For now, commands that use
+ * search as a motion component fail when the search returns to the
+ * original cursor position.
+ */
+ if (vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno == vp->m_stop.cno) {
+ msgq(sp, M_BERR, "190|Search wrapped to original position");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Searches become line mode operations if there was a delta specified
+ * to the search pattern.
+ */
+ if (isdelta)
+ F_SET(vp, VM_LMODE);
+
+ /*
+ * If the motion is in the reverse direction, switch the start and
+ * stop MARK's so that it's in a forward direction. (There's no
+ * reason for this other than to make the tests below easier. The
+ * code in vi.c:vi() would have done the switch.) Both forward
+ * and backward motions can happen for any kind of search command
+ * because of the wrapscan option.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno) {
+ m = vp->m_start;
+ vp->m_start = vp->m_stop;
+ vp->m_stop = m;
+ dir = BACKWARD;
+ } else
+ dir = FORWARD;
+
+ /*
+ * BACKWARD:
+ * Delete and yank commands move to the end of the range.
+ * Ignore others.
+ *
+ * FORWARD:
+ * Delete and yank commands don't move. Ignore others.
+ */
+ vp->m_final = vp->m_start;
+
+ /*
+ * !!!
+ * Delta'd searches don't correct based on column positions.
+ */
+ if (isdelta)
+ return (0);
+
+ /*
+ * !!!
+ * Backward searches starting at column 0, and forward searches ending
+ * at column 0 are corrected to the last column of the previous line.
+ * Otherwise, adjust the starting/ending point to the character before
+ * the current one (this is safe because we know the search had to move
+ * to succeed).
+ *
+ * Searches become line mode operations if they start at the first
+ * nonblank and end at column 0 of another line.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
+ if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_stop.cno = len ? len - 1 : 0;
+ len = 0;
+ if (nonblank(sp, vp->m_start.lno, &len))
+ return (1);
+ if (vp->m_start.cno <= len)
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_stop.cno;
+
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_section.c b/contrib/nvi/vi/v_section.c
new file mode 100644
index 000000000000..20e8ff2808fd
--- /dev/null
+++ b/contrib/nvi/vi/v_section.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_section.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.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 behavior to match the paragraph commands.
+ *
+ * In historic vi, a section was defined as the first character(s) of the
+ * line matching, which could be followed by anything. This implementation
+ * follows that historic practice.
+ *
+ * !!!
+ * The historic vi documentation (USD:15-10) claimed:
+ * The section commands interpret a preceding count as a different
+ * window size in which to redraw the screen at the new location,
+ * and this window size is the base size for newly drawn windows
+ * until another size is specified. This is very useful if you are
+ * on a slow terminal ...
+ *
+ * I can't get the 4BSD vi to do this, it just beeps at me. For now, a
+ * count to the section commands simply repeats the command.
+ */
+
+/*
+ * v_sectionf -- [count]]]
+ * Move forward count sections/functions.
+ *
+ * !!!
+ * Using ]] as a motion command was a bit special, historically. It could
+ * match } as well as the usual { and section values. If it matched a { or
+ * a section, it did NOT include the matched line. If it matched a }, it
+ * did include the line. No clue why.
+ *
+ * PUBLIC: int v_sectionf __P((SCR *, VICMD *));
+ */
+int
+v_sectionf(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t cnt, lno;
+ size_t len;
+ char *p, *list, *lp;
+
+ /* Get the macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ /*
+ * !!!
+ * If the starting cursor position is at or before any non-blank
+ * characters in the line, i.e. the movement is cutting all of the
+ * line's text, the buffer is in line mode. It's a lot easier to
+ * check here, because we know that the end is going to be the start
+ * or end of a line.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ else {
+ vp->m_stop = vp->m_start;
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ if (vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) {
+ if (len == 0)
+ continue;
+ if (p[0] == '{' || ISMOTION(vp) && p[0] == '}') {
+ if (!--cnt) {
+ if (p[0] == '{')
+ goto adjust1;
+ goto adjust2;
+ }
+ continue;
+ }
+ /*
+ * !!!
+ * Historic documentation (USD:15-11, 4.2) said that formfeed
+ * characters (^L) in the first column delimited sections.
+ * The historic code mentions formfeed characters, but never
+ * implements them. Seems reasonable, do it.
+ */
+ if (p[0] == '\014') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ if (p[0] != '.' || len < 2)
+ continue;
+ for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
+ !--cnt) {
+ /*
+ * !!!
+ * If not cutting this line, adjust to the end
+ * of the previous one. Otherwise, position to
+ * column 0.
+ */
+adjust1: if (ISMOTION(vp))
+ goto ret1;
+
+adjust2: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ goto ret2;
+ }
+ }
+
+ /* If moving forward, reached EOF, check to see if we started there. */
+ if (vp->m_start.lno == lno - 1) {
+ v_eof(sp, NULL);
+ return (1);
+ }
+
+ret1: if (db_get(sp, --lno, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+
+ /*
+ * Non-motion commands go to the end of the range. Delete and
+ * yank stay at the start of the range. Ignore others.
+ */
+ret2: if (ISMOTION(vp)) {
+ vp->m_final = vp->m_start;
+ if (F_ISSET(vp, VM_LMODE))
+ vp->m_final.cno = 0;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_sectionb -- [count][[
+ * Move backward count sections/functions.
+ *
+ * PUBLIC: int v_sectionb __P((SCR *, VICMD *));
+ */
+int
+v_sectionb(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *list, *lp;
+
+ /* An empty file or starting from line 1 is always illegal. */
+ if (vp->m_start.lno <= 1) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /* Get the macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) {
+ if (len == 0)
+ continue;
+ if (p[0] == '{') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ /*
+ * !!!
+ * Historic documentation (USD:15-11, 4.2) said that formfeed
+ * characters (^L) in the first column delimited sections.
+ * The historic code mentions formfeed characters, but never
+ * implements them. Seems reasonable, do it.
+ */
+ if (p[0] == '\014') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ if (p[0] != '.' || len < 2)
+ continue;
+ for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
+ !--cnt) {
+adjust1: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ goto ret1;
+ }
+ }
+
+ /*
+ * If moving backward, reached SOF, which is a movement sink.
+ * We already checked for starting there.
+ */
+ vp->m_stop.lno = 1;
+ vp->m_stop.cno = 0;
+
+ /*
+ * All commands move to the end of the range.
+ *
+ * !!!
+ * Historic practice is the section cut was in line mode if it started
+ * from column 0 and was in the backward direction. Otherwise, left
+ * motion commands adjust the starting point to the character before
+ * the current one. What makes this worse is that if it cut to line
+ * mode it also went to the first non-<blank>.
+ */
+ret1: if (vp->m_start.cno == 0) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+
+ --vp->m_start.lno;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_sentence.c b/contrib/nvi/vi/v_sentence.c
new file mode 100644
index 000000000000..a3d9376be403
--- /dev/null
+++ b/contrib/nvi/vi/v_sentence.c
@@ -0,0 +1,359 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_sentence.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.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.
+ * Finally, tabs are eaten (along with spaces) when skipping to the start
+ * of the text following a "sentence".
+ */
+
+/*
+ * v_sentencef -- [count])
+ * Move forward count sentences.
+ *
+ * PUBLIC: int v_sentencef __P((SCR *, VICMD *));
+ */
+int
+v_sentencef(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ enum { BLANK, NONE, PERIOD } state;
+ VCS cs;
+ size_t len;
+ u_long cnt;
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, &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, &cs))
+ return (1);
+ if (--cnt == 0) {
+ if (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.cno != cs.cs_cno)
+ goto okret;
+ return (1);
+ }
+ }
+
+ for (state = NONE;;) {
+ if (cs_next(sp, &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, &cs))
+ return (1);
+ if (cs.cs_flags == 0 &&
+ isblank(cs.cs_ch) && cs_fblank(sp, &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, &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, &cs))
+ return (1);
+ goto okret;
+ }
+ /* FALLTHROUGH */
+ default:
+ state = NONE;
+ break;
+ }
+ }
+
+ /* EOF is a movement sink, but it's an error not to have moved. */
+ if (vp->m_start.lno == cs.cs_lno && vp->m_start.cno == cs.cs_cno) {
+ v_eof(sp, NULL);
+ return (1);
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * Historic, uh, features, yeah, that's right, call 'em features.
+ * If the starting and ending cursor positions are at the first
+ * column in their lines, i.e. the movement is cutting entire lines,
+ * the buffer is in line mode, and the ending position is the last
+ * character of the previous line. Note check to make sure that
+ * it's not within a single line.
+ *
+ * Non-motion commands move to the end of the range. Delete and
+ * yank stay at the start. Ignore others. Adjust the end of the
+ * range for motion commands.
+ */
+ if (ISMOTION(vp)) {
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (vp->m_start.lno < vp->m_stop.lno) {
+ if (db_get(sp,
+ --vp->m_stop.lno, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_stop.cno = len ? len - 1 : 0;
+ }
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_sentenceb -- [count](
+ * Move backward count sentences.
+ *
+ * PUBLIC: int v_sentenceb __P((SCR *, VICMD *));
+ */
+int
+v_sentenceb(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ VCS cs;
+ recno_t slno;
+ size_t len, scno;
+ u_long cnt;
+ int last;
+
+ /*
+ * !!!
+ * Historic vi permitted the user to hit SOF repeatedly.
+ */
+ if (vp->m_start.lno == 1 && vp->m_start.cno == 0)
+ return (0);
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * !!!
+ * In empty lines, skip to the previous non-white-space character.
+ * If in text, skip to the prevous white-space character. Believe
+ * it or not, in the paragraph:
+ * ab cd.
+ * AB CD.
+ * if the cursor is on the 'A' or 'B', ( moves to the 'a'. If it
+ * is on the ' ', 'C' or 'D', it moves to the 'A'. Yes, Virginia,
+ * Berkeley was once a major center of drug activity.
+ */
+ if (cs.cs_flags == CS_EMP) {
+ if (cs_bblank(sp, &cs))
+ return (1);
+ for (;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags != CS_EOL)
+ break;
+ }
+ } else if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ for (;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+
+ for (last = 0;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ last = 1;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) {
+ if (--cnt == 0)
+ goto ret;
+ if (cs_bblank(sp, &cs))
+ return (1);
+ last = 0;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ if (!last || --cnt != 0) {
+ last = 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, &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, &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 (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.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, &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;
+ last = 0;
+ break;
+ case '\t':
+ last = 1;
+ break;
+ default:
+ last =
+ cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
+ cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
+ }
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * If the starting and stopping cursor positions are at the first
+ * columns in the line, i.e. the movement is cutting an entire line,
+ * the buffer is in line mode, and the starting position is the last
+ * character of the previous line.
+ *
+ * All commands move to the end of the range. Adjust the start of
+ * the range for motion commands.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (db_get(sp,
+ --vp->m_start.lno, DBG_FATAL, NULL, &len))
+ return (1);
+ vp->m_start.cno = len ? len - 1 : 0;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_status.c b/contrib/nvi/vi/v_status.c
new file mode 100644
index 000000000000..7095d782c60a
--- /dev/null
+++ b/contrib/nvi/vi/v_status.c
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_status.c 10.9 (Berkeley) 5/15/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_status -- ^G
+ * Show the file status.
+ *
+ * PUBLIC: int v_status __P((SCR *, VICMD *));
+ */
+int
+v_status(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ (void)msgq_status(sp, vp->m_start.lno, MSTAT_SHOWLAST);
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_txt.c b/contrib/nvi/vi/v_txt.c
new file mode 100644
index 000000000000..c47a485686fb
--- /dev/null
+++ b/contrib/nvi/vi/v_txt.c
@@ -0,0 +1,2950 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_txt.c 10.87 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static int txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *));
+static void txt_ai_resolve __P((SCR *, TEXT *, int *));
+static TEXT *txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *));
+static int txt_dent __P((SCR *, TEXT *, int));
+static int txt_emark __P((SCR *, TEXT *, size_t));
+static void txt_err __P((SCR *, TEXTH *));
+static int txt_fc __P((SCR *, TEXT *, int *));
+static int txt_fc_col __P((SCR *, int, ARGS **));
+static int txt_hex __P((SCR *, TEXT *));
+static int txt_insch __P((SCR *, TEXT *, CHAR_T *, u_int));
+static int txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *));
+static int txt_map_end __P((SCR *));
+static int txt_map_init __P((SCR *));
+static int txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t));
+static void txt_nomorech __P((SCR *));
+static void txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t));
+static int txt_resolve __P((SCR *, TEXTH *, u_int32_t));
+static int txt_showmatch __P((SCR *, TEXT *));
+static void txt_unmap __P((SCR *, TEXT *, u_int32_t *));
+
+/* Cursor character (space is hard to track on the screen). */
+#if defined(DEBUG) && 0
+#undef CH_CURSOR
+#define CH_CURSOR '+'
+#endif
+
+/*
+ * v_tcmd --
+ * Fill a buffer from the terminal for vi.
+ *
+ * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int));
+ */
+int
+v_tcmd(sp, vp, prompt, flags)
+ SCR *sp;
+ VICMD *vp;
+ ARG_CHAR_T prompt;
+ u_int flags;
+{
+ /* Normally, we end up where we started. */
+ vp->m_final.lno = sp->lno;
+ vp->m_final.cno = sp->cno;
+
+ /* Initialize the map. */
+ if (txt_map_init(sp))
+ return (1);
+
+ /* Move to the last line. */
+ sp->lno = TMAP[0].lno;
+ sp->cno = 0;
+
+ /* Don't update the modeline for now. */
+ F_SET(sp, SC_TINPUT_INFO);
+
+ /* Set the input flags. */
+ LF_SET(TXT_APPENDEOL |
+ TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
+ if (O_ISSET(sp, O_ALTWERASE))
+ LF_SET(TXT_ALTWERASE);
+ if (O_ISSET(sp, O_TTYWERASE))
+ LF_SET(TXT_TTYWERASE);
+
+ /* Do the input thing. */
+ if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags))
+ return (1);
+
+ /* Reenable the modeline updates. */
+ F_CLR(sp, SC_TINPUT_INFO);
+
+ /* Clean up the map. */
+ if (txt_map_end(sp))
+ return (1);
+
+ if (IS_ONELINE(sp))
+ F_SET(sp, SC_SCR_REDRAW); /* XXX */
+
+ /* Set the cursor to the resulting position. */
+ sp->lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+
+ return (0);
+}
+
+/*
+ * txt_map_init
+ * Initialize the screen map for colon command-line input.
+ */
+static int
+txt_map_init(sp)
+ SCR *sp;
+{
+ SMAP *esmp;
+ VI_PRIVATE *vip;
+
+ vip = VIP(sp);
+ if (!IS_ONELINE(sp)) {
+ /*
+ * 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.
+ */
+ vip->sv_tm_lno = TMAP->lno;
+ vip->sv_tm_soff = TMAP->soff;
+ vip->sv_tm_coff = TMAP->coff;
+ vip->sv_t_maxrows = sp->t_maxrows;
+ vip->sv_t_minrows = sp->t_minrows;
+ vip->sv_t_rows = sp->t_rows;
+
+ /*
+ * If it's a small screen, TMAP may be small for the screen.
+ * Fix it, filling in fake lines as we go.
+ */
+ if (IS_SMALL(sp))
+ for (esmp =
+ HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].coff = HMAP->coff;
+ TMAP[1].soff = 1;
+ }
+
+ /* Build the fake entry. */
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].soff = 1;
+ TMAP[1].coff = 0;
+ SMAP_FLUSH(&TMAP[1]);
+ ++TMAP;
+
+ /* Reset the screen information. */
+ sp->t_rows = sp->t_minrows = ++sp->t_maxrows;
+ }
+ return (0);
+}
+
+/*
+ * txt_map_end
+ * Reset the screen map for colon command-line input.
+ */
+static int
+txt_map_end(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+ size_t cnt;
+
+ vip = VIP(sp);
+ if (!IS_ONELINE(sp)) {
+ /* Restore the screen information. */
+ sp->t_rows = vip->sv_t_rows;
+ sp->t_minrows = vip->sv_t_minrows;
+ sp->t_maxrows = vip->sv_t_maxrows;
+
+ /*
+ * If it's a small screen, TMAP may be wrong. Clear any
+ * lines that might have been overwritten.
+ */
+ if (IS_SMALL(sp)) {
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ (void)sp->gp->scr_move(sp, cnt, 0);
+ (void)sp->gp->scr_clrtoeol(sp);
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+ } else
+ --TMAP;
+
+ /*
+ * 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 we probably don't care.
+ */
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ while (vip->sv_tm_lno != TMAP->lno ||
+ vip->sv_tm_soff != TMAP->soff)
+ if (vs_sm_1down(sp))
+ return (1);
+ }
+
+ /*
+ * Invalidate the cursor and the line size cache, the line never
+ * really existed. This fixes bugs where the user searches for
+ * the last line on the screen + 1 and the refresh routine thinks
+ * that's where we just were.
+ */
+ VI_SCR_CFLUSH(vip);
+ F_SET(vip, VIP_CUR_INVALID);
+
+ return (0);
+}
+
+/*
+ * If doing input mapping on the colon command line, may need to unmap
+ * based on the command.
+ */
+#define UNMAP_TST \
+ FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE)
+
+/*
+ * Internally, we maintain tp->lno and tp->cno, externally, everyone uses
+ * sp->lno and sp->cno. Make them consistent as necessary.
+ */
+#define UPDATE_POSITION(sp, tp) { \
+ (sp)->lno = (tp)->lno; \
+ (sp)->cno = (tp)->cno; \
+}
+
+/*
+ * v_txt --
+ * Vi text input.
+ *
+ * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *,
+ * PUBLIC: const char *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t));
+ */
+int
+v_txt(sp, vp, tm, lp, len, prompt, ai_line, rcount, flags)
+ SCR *sp;
+ VICMD *vp;
+ MARK *tm; /* To MARK. */
+ const char *lp; /* Input line. */
+ size_t len; /* Input line length. */
+ ARG_CHAR_T prompt; /* Prompt to display. */
+ recno_t ai_line; /* Line number to use for autoindent count. */
+ u_long rcount; /* Replay count. */
+ u_int32_t flags; /* TXT_* flags. */
+{
+ EVENT ev, *evp; /* Current event. */
+ EVENT fc; /* File name completion event. */
+ GS *gp;
+ TEXT *ntp, *tp; /* Input text structures. */
+ TEXT ait; /* Autoindent text structure. */
+ TEXT wmt; /* Wrapmargin text structure. */
+ TEXTH *tiqh;
+ VI_PRIVATE *vip;
+ abb_t abb; /* State of abbreviation checks. */
+ carat_t carat; /* State of the "[^0]^D" sequences. */
+ quote_t quote; /* State of quotation. */
+ size_t owrite, insert; /* Temporary copies of TEXT fields. */
+ size_t margin; /* Wrapmargin value. */
+ size_t rcol; /* 0-N: insert offset in the replay buffer. */
+ size_t tcol; /* Temporary column. */
+ u_int32_t ec_flags; /* Input mapping flags. */
+#define IS_RESTART 0x01 /* Reset the incremental search. */
+#define IS_RUNNING 0x02 /* Incremental search turned on. */
+ u_int8_t is_flags;
+ int abcnt, ab_turnoff; /* Abbreviation character count, switch. */
+ int filec_redraw; /* Redraw after the file completion routine. */
+ int hexcnt; /* Hex character count. */
+ int showmatch; /* Showmatch set on this character. */
+ int wm_set, wm_skip; /* Wrapmargin happened, blank skip flags. */
+ int max, tmp;
+ char *p;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /*
+ * Set the input flag, so tabs get displayed correctly
+ * and everyone knows that the text buffer is in use.
+ */
+ F_SET(sp, SC_TINPUT);
+
+ /*
+ * 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.
+ */
+ tiqh = &sp->tiq;
+ 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 default termination condition. */
+ tp->term = TERM_OK;
+
+ /* Set the starting line, column. */
+ tp->lno = sp->lno;
+ tp->cno = sp->cno;
+
+ /*
+ * 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
+ * CH_ENDMARK character.
+ */
+ if (len) {
+ if (LF_ISSET(TXT_OVERWRITE)) {
+ tp->owrite = (tm->cno - tp->cno) + 1;
+ tp->insert = (len - tm->cno) - 1;
+ } else
+ tp->insert = len - tp->cno;
+
+ if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))
+ return (1);
+ }
+
+ /*
+ * Many of the special cases in text input 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 beggars the
+ * imagination. Note also, "^^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 (v_txt_auto(sp, ai_line, NULL, 0, tp))
+ return (1);
+ tp->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 = tp->cno;
+ } else
+ tp->offset = tp->cno;
+ }
+
+ /* If getting a command buffer from the user, there may be a prompt. */
+ if (LF_ISSET(TXT_PROMPT)) {
+ tp->lb[tp->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[tp->cno] = CH_CURSOR;
+ ++tp->len;
+ ++tp->insert;
+ (void)vs_change(sp, tp->lno, LINE_RESET);
+ }
+
+ /*
+ * Historic practice is that the wrapmargin value was a distance
+ * from the RIGHT-HAND margin, not the left. It's more useful to
+ * us as a distance from the left-hand margin, i.e. the same as
+ * the wraplen value. The wrapmargin option is historic practice.
+ * Nvi added the wraplen option so that it would be possible to
+ * edit files with consistent margins without knowing the number of
+ * columns in the window.
+ *
+ * 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.
+ *
+ * !!!
+ * Abbreviated keys were affected by the wrapmargin option in the
+ * historic 4BSD vi. Mapped keys were usually, but sometimes not.
+ * See the comment in vi/v_text():set_txt_std for more information.
+ *
+ * !!!
+ * One more special case. If an inserted <blank> character causes
+ * wrapmargin to split the line, the next user entered character is
+ * discarded if it's a <space> character.
+ */
+ wm_set = wm_skip = 0;
+ if (LF_ISSET(TXT_WRAPMARGIN))
+ if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
+ margin = sp->cols - margin;
+ else
+ margin = O_VAL(sp, O_WRAPLEN);
+ else
+ margin = 0;
+
+ /* Initialize abbreviation checks. */
+ abcnt = ab_turnoff = 0;
+ abb = F_ISSET(gp, G_ABBREV) &&
+ LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;
+
+ /*
+ * Set up the dot command. Dot commands are done by saving the actual
+ * characters and then reevaluating them so that things like wrapmargin
+ * can change between the insert and the replay.
+ *
+ * !!!
+ * Historically, vi did not remap or reabbreviate replayed input. (It
+ * did beep at you if you changed an abbreviation and then replayed the
+ * input. We're not that compatible.) We don't have to do anything to
+ * avoid remapping, as we're not getting characters from the terminal
+ * routines. Turn the abbreviation check off.
+ *
+ * XXX
+ * It would be nice if we could swallow backspaces and such, but it's
+ * not all that easy to do. What we can do is turn off the common
+ * error messages during the replay. Otherwise, when the user enters
+ * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",
+ * and then does a '.', they get a list of error messages after command
+ * completion.
+ */
+ rcol = 0;
+ if (LF_ISSET(TXT_REPLAY)) {
+ abb = AB_NOTSET;
+ LF_CLR(TXT_RECORD);
+ }
+
+ /* Other text input mode setup. */
+ quote = Q_NOTSET;
+ carat = C_NOTSET;
+ FL_INIT(is_flags,
+ LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);
+ filec_redraw = hexcnt = showmatch = 0;
+
+ /* Initialize input flags. */
+ ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;
+
+ /* Refresh the screen. */
+ UPDATE_POSITION(sp, tp);
+ if (vs_refresh(sp, 1))
+ return (1);
+
+ /* If it's dot, just do it now. */
+ if (F_ISSET(vp, VC_ISDOT))
+ goto replay;
+
+ /* Get an event. */
+ evp = &ev;
+next: if (v_event_get(sp, evp, 0, ec_flags))
+ return (1);
+
+ /*
+ * If file completion overwrote part of the screen and nothing else has
+ * been displayed, clean up. We don't do this as part of the normal
+ * message resolution because we know the user is on the colon command
+ * line and there's no reason to enter explicit characters to continue.
+ */
+ if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {
+ filec_redraw = 0;
+
+ fc.e_event = E_REPAINT;
+ fc.e_flno = vip->totalcount >=
+ sp->rows ? 1 : sp->rows - vip->totalcount;
+ fc.e_tlno = sp->rows;
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+ (void)vs_repaint(sp, &fc);
+ (void)vs_refresh(sp, 1);
+ }
+
+ /* Deal with all non-character events. */
+ switch (evp->e_event) {
+ case E_CHARACTER:
+ break;
+ case E_ERR:
+ case E_EOF:
+ F_SET(sp, SC_EXIT_FORCE);
+ return (1);
+ case E_INTERRUPT:
+ /*
+ * !!!
+ * Historically, <interrupt> exited the user from text input
+ * mode or cancelled a colon command, and returned to command
+ * mode. It also beeped the terminal, but that seems a bit
+ * excessive.
+ */
+ goto k_escape;
+ case E_REPAINT:
+ if (vs_repaint(sp, &ev))
+ return (1);
+ goto next;
+ case E_WRESIZE:
+ /* <resize> interrupts the input mode. */
+ v_emsg(sp, NULL, VIM_WRESIZE);
+ goto k_escape;
+ default:
+ v_event_err(sp, evp);
+ goto k_escape;
+ }
+
+ /*
+ * !!!
+ * If the first character of the input is a nul, replay the previous
+ * input. (Historically, it's okay to replay non-existent input.)
+ * This was not documented as far as I know, and is a great test of vi
+ * clones.
+ */
+ if (rcol == 0 && !LF_ISSET(TXT_REPLAY) && evp->e_c == '\0') {
+ if (vip->rep == NULL)
+ goto done;
+
+ abb = AB_NOTSET;
+ LF_CLR(TXT_RECORD);
+ LF_SET(TXT_REPLAY);
+ goto replay;
+ }
+
+ /*
+ * File name completion and colon command-line editing. We don't
+ * have enough meta characters, so we expect people to overload
+ * them. If the two characters are the same, then we do file name
+ * completion if the cursor is past the first column, and do colon
+ * command-line editing if it's not.
+ */
+ if (quote == Q_NOTSET) {
+ int L__cedit, L__filec;
+
+ L__cedit = L__filec = 0;
+ if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&
+ O_STR(sp, O_CEDIT)[0] == evp->e_c)
+ L__cedit = 1;
+ if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&
+ O_STR(sp, O_FILEC)[0] == evp->e_c)
+ L__filec = 1;
+ if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {
+ tp->term = TERM_CEDIT;
+ goto k_escape;
+ }
+ if (L__filec == 1) {
+ if (txt_fc(sp, tp, &filec_redraw))
+ goto err;
+ goto resolve;
+ }
+ }
+
+ /* Abbreviation overflow check. See comment in txt_abbrev(). */
+#define MAX_ABBREVIATION_EXPANSION 256
+ if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) {
+ if (++abcnt > MAX_ABBREVIATION_EXPANSION) {
+ if (v_event_flush(sp, CH_ABBREVIATED))
+ msgq(sp, M_ERR,
+"191|Abbreviation exceeded expansion limit: characters discarded");
+ abcnt = 0;
+ if (LF_ISSET(TXT_REPLAY))
+ goto done;
+ goto resolve;
+ }
+ } else
+ abcnt = 0;
+
+ /* Check to see if the character fits into the replay buffers. */
+ if (LF_ISSET(TXT_RECORD)) {
+ BINC_GOTO(sp, vip->rep,
+ vip->rep_len, (rcol + 1) * sizeof(EVENT));
+ vip->rep[rcol++] = *evp;
+ }
+
+replay: if (LF_ISSET(TXT_REPLAY))
+ evp = vip->rep + rcol++;
+
+ /* Wrapmargin check for leading space. */
+ if (wm_skip) {
+ wm_skip = 0;
+ if (evp->e_c == ' ')
+ goto resolve;
+ }
+
+ /* If quoted by someone else, simply insert the character. */
+ if (F_ISSET(&evp->e_ch, CH_QUOTED))
+ goto insq_ch;
+
+ /*
+ * !!!
+ * If this character was quoted by a K_VLNEXT or a backslash, replace
+ * the placeholder (a carat or a backslash) with the new character.
+ * If it was quoted by a K_VLNEXT, we've already adjusted the cursor
+ * because it has to appear on top of the placeholder character. If
+ * it was quoted by a backslash, adjust the cursor now, the cursor
+ * doesn't appear on top of it. Historic practice in both cases.
+ *
+ * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"
+ * doesn't perform an abbreviation. Special case, ^V^J (not ^V^M) is
+ * the same as ^J, historically.
+ */
+ if (quote == Q_BTHIS || quote == Q_VTHIS) {
+ FL_CLR(ec_flags, EC_QUOTED);
+ if (LF_ISSET(TXT_MAPINPUT))
+ FL_SET(ec_flags, EC_MAPINPUT);
+
+ if (quote == Q_BTHIS &&
+ (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) {
+ quote = Q_NOTSET;
+ --tp->cno;
+ ++tp->owrite;
+ goto insl_ch;
+ }
+ if (quote == Q_VTHIS && evp->e_value != K_NL) {
+ quote = Q_NOTSET;
+ goto insl_ch;
+ }
+ quote = Q_NOTSET;
+ }
+
+ /*
+ * !!!
+ * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:
+ * this test delimits the value by any non-hex character. Offset by
+ * one, we use 0 to mean that we've found <CH_HEX>.
+ */
+ if (hexcnt > 1 && !isxdigit(evp->e_c)) {
+ hexcnt = 0;
+ if (txt_hex(sp, tp))
+ goto err;
+ }
+
+ switch (evp->e_value) {
+ case K_CR: /* Carriage return. */
+ case K_NL: /* New line. */
+ /* Return in script windows and the command line. */
+k_cr: if (LF_ISSET(TXT_CR)) {
+ /*
+ * If this was a map, we may have not displayed
+ * the line. Display it, just in case.
+ *
+ * If a script window and not the colon line,
+ * push a <cr> so it gets executed.
+ */
+ if (LF_ISSET(TXT_INFOLINE)) {
+ if (vs_change(sp, tp->lno, LINE_RESET))
+ goto err;
+ } else if (F_ISSET(sp, SC_SCRIPT))
+ (void)v_event_push(sp, NULL, "\r", 1, CH_NOMAP);
+
+ /* Set term condition: if empty. */
+ if (tp->cno <= tp->offset)
+ tp->term = TERM_CR;
+ /*
+ * Set term condition: if searching incrementally and
+ * the user entered a pattern, return a completed
+ * search, regardless if the entire pattern was found.
+ */
+ if (FL_ISSET(is_flags, IS_RUNNING) &&
+ tp->cno >= tp->offset + 1)
+ tp->term = TERM_SEARCH;
+
+ goto k_escape;
+ }
+
+#define LINE_RESOLVE { \
+ /* \
+ * Handle abbreviations. If there was one, discard the \
+ * replay characters. \
+ */ \
+ if (abb == AB_INWORD && \
+ !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { \
+ if (txt_abbrev(sp, tp, &evp->e_c, \
+ LF_ISSET(TXT_INFOLINE), &tmp, \
+ &ab_turnoff)) \
+ goto err; \
+ if (tmp) { \
+ if (LF_ISSET(TXT_RECORD)) \
+ rcol -= tmp + 1; \
+ goto resolve; \
+ } \
+ } \
+ if (abb != AB_NOTSET) \
+ abb = AB_NOTWORD; \
+ if (UNMAP_TST) \
+ txt_unmap(sp, tp, &ec_flags); \
+ /* \
+ * Delete any appended cursor. It's possible to get in \
+ * situations where TXT_APPENDEOL is set but tp->insert \
+ * is 0 when using the R command and all the characters \
+ * are tp->owrite characters. \
+ */ \
+ if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) { \
+ --tp->len; \
+ --tp->insert; \
+ } \
+}
+ LINE_RESOLVE;
+
+ /*
+ * Save the current line information for restoration in
+ * txt_backup(), and set the line final length.
+ */
+ tp->sv_len = tp->len;
+ tp->sv_cno = tp->cno;
+ tp->len = tp->cno;
+
+ /* Update the old line. */
+ if (vs_change(sp, tp->lno, LINE_RESET))
+ goto err;
+
+ /*
+ * Historic practice, when the autoindent edit option was set,
+ * was to delete <blank> characters following the inserted
+ * newline. This affected the 'R', 'c', and 's' commands; 'c'
+ * and 's' retained the insert characters only, 'R' moved the
+ * overwrite and insert characters into the next TEXT structure.
+ * We keep track of the number of characters erased for the 'R'
+ * command so that the final resolution of the line is correct.
+ */
+ tp->R_erase = 0;
+ owrite = tp->owrite;
+ insert = tp->insert;
+ if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
+ for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p);
+ ++p, --owrite, ++tp->R_erase);
+ if (owrite == 0)
+ for (; insert > 0 && isblank(*p);
+ ++p, ++tp->R_erase, --insert);
+ } else {
+ p = tp->lb + tp->cno + owrite;
+ if (O_ISSET(sp, O_AUTOINDENT))
+ for (; insert > 0 &&
+ isblank(*p); ++p, --insert);
+ owrite = 0;
+ }
+
+ /*
+ * !!!
+ * Create a new line 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:db_get() will be wrong.
+ */
+ if ((ntp = text_init(sp, p,
+ insert + owrite, insert + owrite + 32)) == NULL)
+ goto err;
+ CIRCLEQ_INSERT_TAIL(&sp->tiq, ntp, q);
+
+ /* Set up bookkeeping for the new line. */
+ ntp->insert = insert;
+ ntp->owrite = owrite;
+ ntp->lno = tp->lno + 1;
+
+ /*
+ * Reset the autoindent line value. 0^D keeps the autoindent
+ * line from changing, ^D changes the level, even if there were
+ * no characters in the old line. Note, if using the current
+ * tp structure, use the cursor as the length, the autoindent
+ * characters may have been erased.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ if (carat == C_NOCHANGE) {
+ if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))
+ goto err;
+ FREE_SPACE(sp, ait.lb, ait.lb_len);
+ } else
+ if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))
+ goto err;
+ carat = C_NOTSET;
+ }
+
+ /* Reset the cursor. */
+ ntp->cno = ntp->ai;
+
+ /*
+ * If we're here because wrapmargin was set and we've broken a
+ * line, there may be additional information (i.e. the start of
+ * a line) in the wmt structure.
+ */
+ if (wm_set) {
+ if (wmt.offset != 0 ||
+ wmt.owrite != 0 || wmt.insert != 0) {
+#define WMTSPACE wmt.offset + wmt.owrite + wmt.insert
+ BINC_GOTO(sp, ntp->lb,
+ ntp->lb_len, ntp->len + WMTSPACE + 32);
+ memmove(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);
+ ntp->len += WMTSPACE;
+ ntp->cno += wmt.offset;
+ ntp->owrite = wmt.owrite;
+ ntp->insert = wmt.insert;
+ }
+ wm_set = 0;
+ }
+
+ /* New lines are TXT_APPENDEOL. */
+ if (ntp->owrite == 0 && ntp->insert == 0) {
+ BINC_GOTO(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ ntp->lb[ntp->cno] = CH_CURSOR;
+ ++ntp->insert;
+ ++ntp->len;
+ }
+
+ /* Swap old and new TEXT's, and update the new line. */
+ tp = ntp;
+ if (vs_change(sp, tp->lno, LINE_INSERT))
+ goto err;
+
+ goto resolve;
+ case K_ESCAPE: /* Escape. */
+ if (!LF_ISSET(TXT_ESCAPE))
+ goto ins_ch;
+
+ /* If we have a count, start replaying the input. */
+ if (rcount > 1) {
+ --rcount;
+
+ rcol = 0;
+ abb = AB_NOTSET;
+ LF_CLR(TXT_RECORD);
+ LF_SET(TXT_REPLAY);
+
+ /*
+ * Some commands (e.g. 'o') need a <newline> for each
+ * repetition.
+ */
+ if (LF_ISSET(TXT_ADDNEWLINE))
+ goto k_cr;
+
+ /*
+ * The R command turns into the 'a' command after the
+ * first repetition.
+ */
+ if (LF_ISSET(TXT_REPLACE)) {
+ tp->insert = tp->owrite;
+ tp->owrite = 0;
+ LF_CLR(TXT_REPLACE);
+ }
+ goto replay;
+ }
+
+ /* Set term condition: if empty. */
+ if (tp->cno <= tp->offset)
+ tp->term = TERM_ESC;
+ /*
+ * Set term condition: if searching incrementally and the user
+ * entered a pattern, return a completed search, regardless if
+ * the entire pattern was found.
+ */
+ if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)
+ tp->term = TERM_SEARCH;
+
+k_escape: LINE_RESOLVE;
+
+ /*
+ * Clean up for the 'R' command, restoring overwrite
+ * characters, and making them into insert characters.
+ */
+ if (LF_ISSET(TXT_REPLACE))
+ txt_Rresolve(sp, &sp->tiq, tp, len);
+
+ /*
+ * If there are any overwrite characters, copy down
+ * any insert characters, and decrement the length.
+ */
+ if (tp->owrite) {
+ if (tp->insert)
+ memmove(tp->lb + tp->cno,
+ tp->lb + tp->cno + tp->owrite, tp->insert);
+ tp->len -= tp->owrite;
+ }
+
+ /*
+ * Optionally resolve the lines into the file. If not
+ * resolving the lines into the file, end the line with
+ * a nul. If the line is empty, then set the length to
+ * 0, the termination condition has already been set.
+ *
+ * XXX
+ * This is wrong, should pass back a length.
+ */
+ if (LF_ISSET(TXT_RESOLVE)) {
+ if (txt_resolve(sp, &sp->tiq, flags))
+ goto err;
+ } else {
+ BINC_GOTO(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 (tp->cno != 0)
+ --tp->cno;
+
+ /* Update the last line. */
+ if (vs_change(sp, tp->lno, LINE_RESET))
+ return (1);
+ goto done;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
+ carat = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
+ carat = C_ZEROSET;
+ goto ins_ch;
+ case K_CNTRLD: /* 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 (tp->cno == 0)
+ goto resolve;
+
+ switch (carat) {
+ case C_CARATSET: /* ^^D */
+ if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);
+ memmove(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+
+ carat = C_NOTSET;
+leftmargin: tp->lb[tp->cno - 1] = ' ';
+ tp->owrite += tp->cno - tp->offset;
+ tp->ai = 0;
+ tp->cno = tp->offset;
+ break;
+ case C_NOTSET: /* ^D */
+ if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)
+ goto ins_ch;
+
+ (void)txt_dent(sp, tp, 0);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case K_VERASE: /* Erase the last character. */
+ /* If can erase over the prompt, return. */
+ if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {
+ tp->term = TERM_BS;
+ goto done;
+ }
+
+ /*
+ * If at the beginning of the line, try and drop back to a
+ * previously inserted line.
+ */
+ if (tp->cno == 0) {
+ if ((ntp =
+ txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ break;
+ }
+
+ /* If nothing to erase, bell the user. */
+ if (tp->cno <= tp->offset) {
+ if (!LF_ISSET(TXT_REPLAY))
+ txt_nomorech(sp);
+ break;
+ }
+
+ /* Drop back one character. */
+ --tp->cno;
+
+ /*
+ * Historically, vi didn't replace the erased characters with
+ * <blank>s, presumably because it's easier to fix a minor
+ * typing mistake and continue on if the previous letters are
+ * already there. This is a problem for incremental searching,
+ * because the user can no longer tell where they are in the
+ * colon command line because the cursor is at the last search
+ * point in the screen. So, if incrementally searching, erase
+ * the erased characters from the screen.
+ */
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ tp->lb[tp->cno] = ' ';
+
+ /*
+ * Increment overwrite, decrement ai if deleted.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase characters
+ * to delete autoindent characters. We do. Eat hot death,
+ * POSIX.
+ */
+ ++tp->owrite;
+ if (tp->cno < tp->ai)
+ --tp->ai;
+
+ /* Reset if we deleted an incremental search character. */
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ FL_SET(is_flags, IS_RESTART);
+ break;
+ case K_VWERASE: /* Skip back one word. */
+ /*
+ * If at the beginning of the line, try and drop back to a
+ * previously inserted line.
+ */
+ if (tp->cno == 0) {
+ if ((ntp =
+ txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /*
+ * If at offset, nothing to erase so bell the user.
+ */
+ if (tp->cno <= tp->offset) {
+ if (!LF_ISSET(TXT_REPLAY))
+ txt_nomorech(sp);
+ break;
+ }
+
+ /*
+ * The first werase goes back to any autoindent column and the
+ * second werase goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase characters to
+ * delete autoindent characters.
+ */
+ if (tp->ai && tp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+
+ /* Skip over trailing space characters. */
+ while (tp->cno > max && isblank(tp->lb[tp->cno - 1])) {
+ --tp->cno;
+ ++tp->owrite;
+ }
+ if (tp->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. The edit 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.
+ *
+ * Overwrite erased characters if doing incremental search;
+ * see comment above.
+ */
+ if (LF_ISSET(TXT_TTYWERASE))
+ while (tp->cno > max) {
+ --tp->cno;
+ ++tp->owrite;
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ tp->lb[tp->cno] = ' ';
+ if (isblank(tp->lb[tp->cno - 1]))
+ break;
+ }
+ else {
+ if (LF_ISSET(TXT_ALTWERASE)) {
+ --tp->cno;
+ ++tp->owrite;
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ tp->lb[tp->cno] = ' ';
+ if (isblank(tp->lb[tp->cno - 1]))
+ break;
+ }
+ if (tp->cno > max)
+ tmp = inword(tp->lb[tp->cno - 1]);
+ while (tp->cno > max) {
+ --tp->cno;
+ ++tp->owrite;
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ tp->lb[tp->cno] = ' ';
+ if (tmp != inword(tp->lb[tp->cno - 1])
+ || isblank(tp->lb[tp->cno - 1]))
+ break;
+ }
+ }
+
+ /* Reset if we deleted an incremental search character. */
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ FL_SET(is_flags, IS_RESTART);
+ break;
+ case K_VKILL: /* Restart this line. */
+ /*
+ * !!!
+ * If at the beginning of the line, try and drop back to a
+ * previously inserted line. Historic vi did not permit
+ * users to go back to previous lines.
+ */
+ if (tp->cno == 0) {
+ if ((ntp =
+ txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /* If at offset, nothing to erase so bell the user. */
+ if (tp->cno <= tp->offset) {
+ if (!LF_ISSET(TXT_REPLAY))
+ txt_nomorech(sp);
+ 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 && tp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+ tp->owrite += tp->cno - max;
+
+ /*
+ * Overwrite erased characters if doing incremental search;
+ * see comment above.
+ */
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ do {
+ tp->lb[--tp->cno] = ' ';
+ } while (tp->cno > max);
+ else
+ tp->cno = max;
+
+ /* Reset if we deleted an incremental search character. */
+ if (FL_ISSET(is_flags, IS_RUNNING))
+ FL_SET(is_flags, IS_RESTART);
+ break;
+ case K_CNTRLT: /* Add autoindent characters. */
+ if (!LF_ISSET(TXT_CNTRLT))
+ goto ins_ch;
+ if (txt_dent(sp, tp, 1))
+ goto err;
+ goto ebuf_chk;
+ case K_RIGHTBRACE:
+ case K_RIGHTPAREN:
+ if (LF_ISSET(TXT_SHOWMATCH))
+ showmatch = 1;
+ goto ins_ch;
+ case K_BACKSLASH: /* Quote next erase/kill. */
+ /*
+ * !!!
+ * Historic vi tried to make abbreviations after a backslash
+ * escape work. If you did ":ab x y", and inserted "x\^H",
+ * (assuming the erase character was ^H) you got "x^H", and
+ * no abbreviation was done. If you inserted "x\z", however,
+ * it tried to back up and do the abbreviation, i.e. replace
+ * 'x' with 'y'. The problem was it got it wrong, and you
+ * ended up with "zy\".
+ *
+ * This is really hard to do (you have to remember the
+ * word/non-word state, for example), and doesn't make any
+ * sense to me. Both backslash and the characters it
+ * (usually) escapes will individually trigger the
+ * abbreviation, so I don't see why the combination of them
+ * wouldn't. I don't expect to get caught on this one,
+ * particularly since it never worked right, but I've been
+ * wrong before.
+ *
+ * Do the tests for abbreviations, so ":ab xa XA",
+ * "ixa\<K_VERASE>" performs the abbreviation.
+ */
+ quote = Q_BNEXT;
+ goto insq_ch;
+ case K_VLNEXT: /* Quote next character. */
+ evp->e_c = '^';
+ quote = Q_VNEXT;
+ /*
+ * Turn on the quote flag so that the underlying routines
+ * quote the next character where it's possible. Turn off
+ * the input mapbiting flag so that we don't remap the next
+ * character.
+ */
+ FL_SET(ec_flags, EC_QUOTED);
+ FL_CLR(ec_flags, EC_MAPINPUT);
+
+ /*
+ * !!!
+ * Skip the tests for abbreviations, so ":ab xa XA",
+ * "ixa^V<space>" doesn't perform the abbreviation.
+ */
+ goto insl_ch;
+ case K_HEXCHAR:
+ hexcnt = 1;
+ goto insq_ch;
+ default: /* Insert the character. */
+ins_ch: /*
+ * Historically, vi eliminated nul's out of hand. If the
+ * beautify option was set, it also deleted any unknown
+ * ASCII value less than space (040) and the del character
+ * (0177), except for tabs. Unknown is a key word here.
+ * Most vi documentation claims that it deleted everything
+ * but <tab>, <nl> and <ff>, as that's what the original
+ * 4BSD documentation said. This is obviously wrong,
+ * however, as <esc> would be included in that list. What
+ * we do is eliminate any unquoted, iscntrl() character that
+ * wasn't a replay and wasn't handled specially, except
+ * <tab> or <ff>.
+ */
+ if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(evp->e_c) &&
+ evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {
+ msgq(sp, M_BERR,
+ "192|Illegal character; quote to enter");
+ if (LF_ISSET(TXT_REPLAY))
+ goto done;
+ break;
+ }
+
+insq_ch: /*
+ * If entering a non-word character after a word, check for
+ * abbreviations. If there was one, discard replay characters.
+ * If entering a blank character, check for unmap commands,
+ * as well.
+ */
+ if (!inword(evp->e_c)) {
+ if (abb == AB_INWORD &&
+ !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {
+ if (txt_abbrev(sp, tp, &evp->e_c,
+ LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
+ goto err;
+ if (tmp) {
+ if (LF_ISSET(TXT_RECORD))
+ rcol -= tmp + 1;
+ goto resolve;
+ }
+ }
+ if (isblank(evp->e_c) && UNMAP_TST)
+ txt_unmap(sp, tp, &ec_flags);
+ }
+ if (abb != AB_NOTSET)
+ abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD;
+
+insl_ch: if (txt_insch(sp, tp, &evp->e_c, flags))
+ goto err;
+
+ /*
+ * If we're using K_VLNEXT to quote the next character, then
+ * we want the cursor to position itself on the ^ placeholder
+ * we're displaying, to match historic practice.
+ */
+ if (quote == Q_VNEXT) {
+ --tp->cno;
+ ++tp->owrite;
+ }
+
+ /*
+ * !!!
+ * Translate "<CH_HEX>[isxdigit()]*" to a character with
+ * a hex value: this test delimits the value by the max
+ * number of hex bytes. Offset by one, we use 0 to mean
+ * that we've found <CH_HEX>.
+ */
+ if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) {
+ hexcnt = 0;
+ if (txt_hex(sp, tp))
+ goto err;
+ }
+
+ /*
+ * Check to see if we've crossed the margin.
+ *
+ * !!!
+ * In the historic vi, the wrapmargin value was figured out
+ * using the display widths of the characters, i.e. <tab>
+ * characters were counted as two characters if the list edit
+ * option is set, but as the tabstop edit option number of
+ * characters otherwise. That's what the vs_column() function
+ * gives us, so we use it.
+ */
+ if (margin != 0) {
+ if (vs_column(sp, &tcol))
+ goto err;
+ if (tcol >= margin) {
+ if (txt_margin(sp, tp, &wmt, &tmp, flags))
+ goto err;
+ if (tmp) {
+ if (isblank(evp->e_c))
+ wm_skip = 1;
+ wm_set = 1;
+ goto k_cr;
+ }
+ }
+ }
+
+ /*
+ * 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 (tp->cno >= tp->len) {
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+
+ tp->lb[tp->cno] = CH_CURSOR;
+ ++tp->insert;
+ ++tp->len;
+ }
+
+ /* Step the quote state forward. */
+ if (quote != Q_NOTSET) {
+ if (quote == Q_BNEXT)
+ quote = Q_BTHIS;
+ if (quote == Q_VNEXT)
+ quote = Q_VTHIS;
+ }
+ break;
+ }
+
+#ifdef DEBUG
+ if (tp->cno + tp->insert + tp->owrite != tp->len) {
+ msgq(sp, M_ERR,
+ "len %u != cno: %u ai: %u insert %u overwrite %u",
+ tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);
+ if (LF_ISSET(TXT_REPLAY))
+ goto done;
+ tp->len = tp->cno + tp->insert + tp->owrite;
+ }
+#endif
+
+resolve:/*
+ * 1: If we don't need to know where the cursor really is and we're
+ * replaying text, keep going.
+ */
+ if (margin == 0 && LF_ISSET(TXT_REPLAY))
+ goto replay;
+
+ /*
+ * 2: Reset the line. Don't bother unless we're about to wait on
+ * a character or we need to know where the cursor really is.
+ * We have to do this before showing matching characters so the
+ * user can see what they're matching.
+ */
+ if ((margin != 0 || !KEYS_WAITING(sp)) &&
+ vs_change(sp, tp->lno, LINE_RESET))
+ return (1);
+
+ /*
+ * 3: If there aren't keys waiting, display the matching character.
+ * We have to do this before resolving any messages, otherwise
+ * the error message from a missing match won't appear correctly.
+ */
+ if (showmatch) {
+ if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))
+ return (1);
+ showmatch = 0;
+ }
+
+ /*
+ * 4: If there have been messages and we're not editing on the colon
+ * command line or doing file name completion, resolve them.
+ */
+ if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&
+ !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw &&
+ vs_resolve(sp, NULL, 0))
+ return (1);
+
+ /*
+ * 5: Refresh the screen if we're about to wait on a character or we
+ * need to know where the cursor really is.
+ */
+ if (margin != 0 || !KEYS_WAITING(sp)) {
+ UPDATE_POSITION(sp, tp);
+ if (vs_refresh(sp, margin != 0))
+ return (1);
+ }
+
+ /* 6: Proceed with the incremental search. */
+ if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))
+ return (1);
+
+ /* 7: Next character... */
+ if (LF_ISSET(TXT_REPLAY))
+ goto replay;
+ goto next;
+
+done: /* Leave input mode. */
+ F_CLR(sp, SC_TINPUT);
+
+ /* If recording for playback, save it. */
+ if (LF_ISSET(TXT_RECORD))
+ vip->rep_cnt = rcol;
+
+ /*
+ * If not working on the colon command line, set the final cursor
+ * position.
+ */
+ if (!F_ISSET(sp, SC_TINPUT_INFO)) {
+ vp->m_final.lno = tp->lno;
+ vp->m_final.cno = tp->cno;
+ }
+ return (0);
+
+err:
+alloc_err:
+ txt_err(sp, &sp->tiq);
+ return (1);
+}
+
+/*
+ * txt_abbrev --
+ * Handle abbreviations.
+ */
+static int
+txt_abbrev(sp, tp, pushcp, isinfoline, didsubp, turnoffp)
+ SCR *sp;
+ TEXT *tp;
+ CHAR_T *pushcp;
+ int isinfoline, *didsubp, *turnoffp;
+{
+ VI_PRIVATE *vip;
+ CHAR_T ch, *p;
+ SEQ *qp;
+ size_t len, off;
+
+ /* Check to make sure we're not at the start of an append. */
+ *didsubp = 0;
+ if (tp->cno == tp->offset)
+ return (0);
+
+ vip = VIP(sp);
+
+ /*
+ * Find the start of the "word".
+ *
+ * !!!
+ * We match historic practice, which, as far as I can tell, had an
+ * off-by-one error. The way this worked was that when the inserted
+ * text switched from a "word" character to a non-word character,
+ * vi would check for possible abbreviations. It would then take the
+ * type (i.e. word/non-word) of the character entered TWO characters
+ * ago, and move backward in the text until reaching a character that
+ * was not that type, or the beginning of the insert, the line, or
+ * the file. For example, in the string "abc<space>", when the <space>
+ * character triggered the abbreviation check, the type of the 'b'
+ * character was used for moving through the string. Maybe there's a
+ * reason for not using the first (i.e. 'c') character, but I can't
+ * think of one.
+ *
+ * Terminate at the beginning of the insert or the character after the
+ * offset character -- both can be tested for using tp->offset.
+ */
+ off = tp->cno - 1; /* Previous character. */
+ p = tp->lb + off;
+ len = 1; /* One character test. */
+ if (off == tp->offset || isblank(p[-1]))
+ goto search;
+ if (inword(p[-1])) /* Move backward to change. */
+ for (;;) {
+ --off; --p; ++len;
+ if (off == tp->offset || !inword(p[-1]))
+ break;
+ }
+ else
+ for (;;) {
+ --off; --p; ++len;
+ if (off == tp->offset ||
+ inword(p[-1]) || isblank(p[-1]))
+ 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
+ * unabbreviate "foo1", and the commands:
+ * :ab foo bar
+ * :ab bar baz
+ * unabbreviate "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. Note also, minor trickiness, so that if
+ * the user erases the line and starts another command, we turn the
+ * abbreviations back on.
+ *
+ * This makes the layering look like a Nachos Supreme.
+ */
+search: 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, 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 = *pushcp;
+ if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED))
+ return (1);
+ if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED))
+ return (1);
+
+ /*
+ * If the size of the abbreviation is larger than or equal to the size
+ * of the original text, move to the start of the replaced characters,
+ * and add their length to the overwrite count.
+ *
+ * If the abbreviation is smaller than the original text, we have to
+ * delete the additional overwrite characters and copy down any insert
+ * characters.
+ */
+ tp->cno -= len;
+ if (qp->olen >= len)
+ tp->owrite += len;
+ else {
+ if (tp->insert)
+ memmove(tp->lb + tp->cno + qp->olen,
+ tp->lb + tp->cno + tp->owrite + len, tp->insert);
+ tp->owrite += qp->olen;
+ tp->len -= len - qp->olen;
+ }
+
+ /*
+ * 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);
+}
+
+/*
+ * txt_unmap --
+ * Handle the unmap command.
+ */
+static void
+txt_unmap(sp, tp, ec_flagsp)
+ SCR *sp;
+ TEXT *tp;
+ u_int32_t *ec_flagsp;
+{
+ size_t len, off;
+ char *p;
+
+ /* Find the beginning of this "word". */
+ for (off = tp->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 input mappings on the command line. See the
+ * txt_abbrev() routine for an explanation of the problems inherent
+ * in this.
+ *
+ * We make this work as follows. 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 unmap command. If it is, we return that the input
+ * mapping should be turned off. Note also, minor trickiness, so that
+ * if the user erases the line and starts another command, we go ahead
+ * an turn mapping back on.
+ */
+ if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
+ FL_CLR(*ec_flagsp, EC_MAPINPUT);
+ else
+ FL_SET(*ec_flagsp, EC_MAPINPUT);
+}
+
+/*
+ * txt_ai_resolve --
+ * When a line is resolved by <esc>, review autoindent characters.
+ */
+static void
+txt_ai_resolve(sp, tp, changedp)
+ SCR *sp;
+ TEXT *tp;
+ int *changedp;
+{
+ u_long ts;
+ int del;
+ size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
+ char *p;
+
+ *changedp = 0;
+
+ /*
+ * If the line is empty, has an offset, or no autoindent
+ * characters, we're done.
+ */
+ if (!tp->len || tp->offset || !tp->ai)
+ return;
+
+ /*
+ * If the length is less than or equal to the autoindent
+ * characters, delete them.
+ */
+ if (tp->len <= tp->ai) {
+ tp->ai = tp->cno = tp->len = 0;
+ 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 += COL_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 + COL_OFF(cno, ts) <= scno; ++tabs)
+ cno += COL_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);
+ tp->len -= del;
+ tp->cno -= del;
+
+ /* Fill in space/tab characters. */
+ for (p = tp->lb; tabs--;)
+ *p++ = '\t';
+ while (spaces--)
+ *p++ = ' ';
+ *changedp = 1;
+}
+
+/*
+ * v_txt_auto --
+ * Handle autoindent. If aitp isn't NULL, use it, otherwise,
+ * retrieve the line.
+ *
+ * PUBLIC: int v_txt_auto __P((SCR *, recno_t, TEXT *, size_t, TEXT *));
+ */
+int
+v_txt_auto(sp, lno, aitp, len, tp)
+ SCR *sp;
+ recno_t lno;
+ TEXT *aitp, *tp;
+ size_t len;
+{
+ size_t nlen;
+ char *p, *t;
+
+ if (aitp == NULL) {
+ /*
+ * If the ex append command is executed with an address of 0,
+ * it's possible to get here with a line number of 0. Return
+ * an indent of 0.
+ */
+ if (lno == 0) {
+ tp->ai = 0;
+ return (0);
+ }
+ if (db_get(sp, lno, DBG_FATAL, &t, &len))
+ return (1);
+ } else
+ t = aitp->lb;
+
+ /* Count whitespace characters. */
+ for (p = t; len > 0; ++p, --len)
+ if (!isblank(*p))
+ break;
+
+ /* Set count, check for no indentation. */
+ if ((nlen = (p - t)) == 0)
+ return (0);
+
+ /* Make sure the buffer's big enough. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+
+ /* Copy the buffer's current contents up. */
+ if (tp->len != 0)
+ memmove(tp->lb + nlen, tp->lb, tp->len);
+ tp->len += nlen;
+
+ /* Copy the indentation into the new buffer. */
+ memmove(tp->lb, t, nlen);
+
+ /* Set the autoindent count. */
+ tp->ai = nlen;
+ return (0);
+}
+
+/*
+ * txt_backup --
+ * Back up to the previously edited line.
+ */
+static TEXT *
+txt_backup(sp, tiqh, tp, flagsp)
+ SCR *sp;
+ TEXTH *tiqh;
+ TEXT *tp;
+ u_int32_t *flagsp;
+{
+ VI_PRIVATE *vip;
+ TEXT *ntp;
+
+ /* Get a handle on the previous TEXT structure. */
+ if ((ntp = tp->q.cqe_prev) == (void *)tiqh) {
+ if (!FL_ISSET(*flagsp, TXT_REPLAY))
+ msgq(sp, M_BERR,
+ "193|Already at the beginning of the insert");
+ return (tp);
+ }
+
+ /* Bookkeeping. */
+ ntp->len = ntp->sv_len;
+
+ /* Handle appending to the line. */
+ vip = VIP(sp);
+ if (ntp->owrite == 0 && ntp->insert == 0) {
+ ntp->lb[ntp->len] = CH_CURSOR;
+ ++ntp->insert;
+ ++ntp->len;
+ FL_SET(*flagsp, TXT_APPENDEOL);
+ } else
+ FL_CLR(*flagsp, TXT_APPENDEOL);
+
+ /* Release the current TEXT. */
+ CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+
+ /* Update the old line on the screen. */
+ if (vs_change(sp, ntp->lno + 1, LINE_DELETE))
+ return (NULL);
+
+ /* Return the new/current TEXT. */
+ return (ntp);
+}
+
+/*
+ * Text indentation is 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 had special meaning only when they
+ * were the first characters entered after entering text input mode. As normal
+ * erase characters couldn't erase autoindent characters (^T in this case), it
+ * meant that inserting text into previously existing text was strange -- ^T
+ * only worked if it was the first keystroke(s), and then could only be erased
+ * using ^D. This implementation treats ^T specially anywhere it occurs in the
+ * input, and permits the standard erase characters to erase the characters it
+ * inserts.
+ *
+ * !!!
+ * A fun test is to try:
+ * :se sw=4 ai list
+ * i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc>
+ * Historic vi loses some of the '$' marks on the line ends, but otherwise gets
+ * it right.
+ *
+ * XXX
+ * Technically, txt_dent should be part of the screen interface, as it requires
+ * knowledge of character sizes, including <space>s, on the screen. It's here
+ * because it's a complicated little beast, and I didn't want to shove it down
+ * into the screen. It's probable that KEY_LEN will call into the screen once
+ * there are screens with different character representations.
+ *
+ * txt_dent --
+ * Handle ^T indents, ^D outdents.
+ *
+ * If anything changes here, check the ex version to see if it needs similar
+ * changes.
+ */
+static int
+txt_dent(sp, tp, isindent)
+ SCR *sp;
+ TEXT *tp;
+ int isindent;
+{
+ CHAR_T ch;
+ u_long sw, ts;
+ size_t cno, current, spaces, target, tabs, off;
+ int ai_reset;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /*
+ * Since we don't know what precedes the character(s) being inserted
+ * (or deleted), the preceding whitespace characters must be resolved.
+ * An example is a <tab>, which doesn't need a full shiftwidth number
+ * of columns because it's preceded by <space>s. This is easy to get
+ * if the user sets shiftwidth to a value less than tabstop (or worse,
+ * something for which tabstop isn't a multiple) and then uses ^T to
+ * indent, and ^D to outdent.
+ *
+ * Figure out the current and target screen columns. In the historic
+ * vi, the autoindent column was NOT determined using display widths
+ * of characters as was the wrapmargin column. For that reason, we
+ * can't use the vs_column() function, but have to calculate it here.
+ * This is slow, but it's normally only on the first few characters of
+ * a line.
+ */
+ for (current = cno = 0; cno < tp->cno; ++cno)
+ current += tp->lb[cno] == '\t' ?
+ COL_OFF(current, ts) : KEY_LEN(sp, tp->lb[cno]);
+
+ target = current;
+ if (isindent)
+ target += COL_OFF(target, sw);
+ else
+ target -= --target % sw;
+
+ /*
+ * The AI characters will be turned into overwrite characters if the
+ * cursor immediately follows them. We test both the cursor position
+ * and the indent flag because there's no single test. (^T can only
+ * be detected by the cursor position, and while we know that the test
+ * is always true for ^D, the cursor can be in more than one place, as
+ * "0^D" and "^D" are different.)
+ */
+ ai_reset = !isindent || tp->cno == tp->ai + tp->offset;
+
+ /*
+ * Back up over any previous <blank> characters, changing them into
+ * overwrite characters (including any ai characters). Then figure
+ * out the current screen column.
+ */
+ for (; tp->cno > tp->offset &&
+ (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t');
+ --tp->cno, ++tp->owrite);
+ for (current = cno = 0; cno < tp->cno; ++cno)
+ current += tp->lb[cno] == '\t' ?
+ COL_OFF(current, ts) : KEY_LEN(sp, tp->lb[cno]);
+
+ /*
+ * If we didn't move up to or past the target, it's because there
+ * weren't enough characters to delete, e.g. the first character
+ * of the line was a tp->offset character, and the user entered
+ * ^D to move to the beginning of a line. An example of this is:
+ *
+ * :set ai sw=4<cr>i<space>a<esc>i^T^D
+ *
+ * Otherwise, count up the total spaces/tabs needed to get from the
+ * beginning of the line (or the last non-<blank> character) to the
+ * target.
+ */
+ if (current >= target)
+ spaces = tabs = 0;
+ else {
+ for (cno = current,
+ tabs = 0; cno + COL_OFF(cno, ts) <= target; ++tabs)
+ cno += COL_OFF(cno, ts);
+ spaces = target - cno;
+ }
+
+ /* If we overwrote ai characters, reset the ai count. */
+ if (ai_reset)
+ tp->ai = tabs + spaces;
+
+ /*
+ * Call txt_insch() to insert each character, so that we get the
+ * correct effect when we add a <tab> to replace N <spaces>.
+ */
+ for (ch = '\t'; tabs > 0; --tabs)
+ (void)txt_insch(sp, tp, &ch, 0);
+ for (ch = ' '; spaces > 0; --spaces)
+ (void)txt_insch(sp, tp, &ch, 0);
+ return (0);
+}
+
+/*
+ * txt_fc --
+ * File name completion.
+ */
+static int
+txt_fc(sp, tp, redrawp)
+ SCR *sp;
+ TEXT *tp;
+ int *redrawp;
+{
+ struct stat sb;
+ ARGS **argv;
+ CHAR_T s_ch;
+ EXCMD cmd;
+ size_t indx, len, nlen, off;
+ int argc, trydir;
+ char *p, *t;
+
+ trydir = 0;
+ *redrawp = 0;
+
+ /*
+ * Find the beginning of this "word" -- if we're at the beginning
+ * of the line, it's a special case.
+ */
+ if (tp->cno == 1) {
+ len = 0;
+ p = tp->lb;
+ } else
+retry: for (len = 0,
+ off = tp->cno - 1, p = tp->lb + off;; --off, --p) {
+ if (isblank(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * Get enough space for a wildcard character.
+ *
+ * XXX
+ * This won't work for "foo\", since the \ will escape the expansion
+ * character. I'm not sure if that's a bug or not...
+ */
+ off = p - tp->lb;
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+ p = tp->lb + off;
+
+ s_ch = p[len];
+ p[len] = '*';
+
+ /* Build an ex command, and call the ex expansion routines. */
+ ex_cinit(&cmd, 0, 0, OOBLNO, OOBLNO, 0, NULL);
+ if (argv_init(sp, &cmd))
+ return (1);
+ if (argv_exp2(sp, &cmd, p, len + 1)) {
+ p[len] = s_ch;
+ return (0);
+ }
+ argc = cmd.argc;
+ argv = cmd.argv;
+
+ p[len] = s_ch;
+
+ switch (argc) {
+ case 0: /* No matches. */
+ if (!trydir)
+ (void)sp->gp->scr_bell(sp);
+ return (0);
+ case 1: /* One match. */
+ /* If something changed, do the exchange. */
+ nlen = strlen(cmd.argv[0]->bp);
+ if (len != nlen || memcmp(cmd.argv[0]->bp, p, len))
+ break;
+
+ /* If haven't done a directory test, do it now. */
+ if (!trydir &&
+ !stat(cmd.argv[0]->bp, &sb) && S_ISDIR(sb.st_mode)) {
+ p += len;
+ goto isdir;
+ }
+
+ /* If nothing changed, period, ring the bell. */
+ if (!trydir)
+ (void)sp->gp->scr_bell(sp);
+ return (0);
+ default: /* Multiple matches. */
+ *redrawp = 1;
+ if (txt_fc_col(sp, argc, argv))
+ return (1);
+
+ /* Find the length of the shortest match. */
+ for (nlen = cmd.argv[0]->len; --argc > 0;) {
+ if (cmd.argv[argc]->len < nlen)
+ nlen = cmd.argv[argc]->len;
+ for (indx = 0; indx < nlen &&
+ cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx];
+ ++indx);
+ nlen = indx;
+ }
+ break;
+ }
+
+ /* Overwrite the expanded text first. */
+ for (t = cmd.argv[0]->bp; len > 0 && nlen > 0; --len, --nlen)
+ *p++ = *t++;
+
+ /* If lost text, make the remaining old text overwrite characters. */
+ if (len) {
+ tp->cno -= len;
+ tp->owrite += len;
+ }
+
+ /* Overwrite any overwrite characters next. */
+ for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno)
+ *p++ = *t++;
+
+ /* Shift remaining text up, and move the cursor to the end. */
+ if (nlen) {
+ off = p - tp->lb;
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+ p = tp->lb + off;
+
+ tp->cno += nlen;
+ tp->len += nlen;
+
+ if (tp->insert != 0)
+ (void)memmove(p + nlen, p, tp->insert);
+ while (nlen--)
+ *p++ = *t++;
+ }
+
+ /* If a single match and it's a directory, retry it. */
+ if (argc == 1 && !stat(cmd.argv[0]->bp, &sb) && S_ISDIR(sb.st_mode)) {
+isdir: if (tp->owrite == 0) {
+ off = p - tp->lb;
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+ p = tp->lb + off;
+ if (tp->insert != 0)
+ (void)memmove(p + 1, p, tp->insert);
+ ++tp->len;
+ } else
+ --tp->owrite;
+
+ ++tp->cno;
+ *p++ = '/';
+
+ trydir = 1;
+ goto retry;
+ }
+ return (0);
+}
+
+/*
+ * txt_fc_col --
+ * Display file names for file name completion.
+ */
+static int
+txt_fc_col(sp, argc, argv)
+ SCR *sp;
+ int argc;
+ ARGS **argv;
+{
+ ARGS **av;
+ CHAR_T *p;
+ GS *gp;
+ size_t base, cnt, col, colwidth, numrows, numcols, prefix, row;
+ int ac, nf, reset;
+
+ gp = sp->gp;
+
+ /* Trim any directory prefix common to all of the files. */
+ if ((p = strrchr(argv[0]->bp, '/')) == NULL)
+ prefix = 0;
+ else {
+ prefix = (p - argv[0]->bp) + 1;
+ for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av)
+ if (av[0]->len < prefix ||
+ memcmp(av[0]->bp, argv[0]->bp, prefix)) {
+ prefix = 0;
+ break;
+ }
+ }
+
+ /*
+ * Figure out the column width for the longest name. Output is done on
+ * 6 character "tab" boundaries for no particular reason. (Since we
+ * don't output tab characters, we ignore the terminal's tab settings.)
+ * Ignore the user's tab setting because we have no idea how reasonable
+ * it is.
+ */
+ for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) {
+ for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p)
+ col += KEY_LEN(sp, *p);
+ if (col > colwidth)
+ colwidth = col;
+ }
+ colwidth += COL_OFF(colwidth, 6);
+
+ /*
+ * Writing to the bottom line of the screen is always turned off when
+ * SC_TINPUT_INFO is set. Turn it back on, we know what we're doing.
+ */
+ if (F_ISSET(sp, SC_TINPUT_INFO)) {
+ reset = 1;
+ F_CLR(sp, SC_TINPUT_INFO);
+ } else
+ reset = 0;
+
+#define CHK_INTR \
+ if (F_ISSET(gp, G_INTERRUPTED)) \
+ goto intr;
+
+ /* If the largest file name is too large, just print them. */
+ if (colwidth > sp->cols) {
+ p = msg_print(sp, av[0]->bp + prefix, &nf);
+ for (ac = argc, av = argv; ac > 0; --ac, ++av) {
+ (void)ex_printf(sp, "%s\n", p);
+ if (F_ISSET(gp, G_INTERRUPTED))
+ break;
+ }
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ CHK_INTR;
+ } else {
+ /* Figure out the number of columns. */
+ numcols = (sp->cols - 1) / colwidth;
+ if (argc > numcols) {
+ numrows = argc / numcols;
+ if (argc % numcols)
+ ++numrows;
+ } else
+ numrows = 1;
+
+ /* Display the files in sorted order. */
+ for (row = 0; row < numrows; ++row) {
+ for (base = row, col = 0; col < numcols; ++col) {
+ p = msg_print(sp, argv[base]->bp + prefix, &nf);
+ cnt = ex_printf(sp, "%s", p);
+ if (nf)
+ FREE_SPACE(sp, p, 0);
+ CHK_INTR;
+ if ((base += numrows) >= argc)
+ break;
+ (void)ex_printf(sp,
+ "%*s", (int)(colwidth - cnt), "");
+ CHK_INTR;
+ }
+ (void)ex_puts(sp, "\n");
+ CHK_INTR;
+ }
+ (void)ex_puts(sp, "\n");
+ CHK_INTR;
+ }
+ (void)ex_fflush(sp);
+
+ if (0) {
+intr: F_CLR(gp, G_INTERRUPTED);
+ }
+ if (reset)
+ F_SET(sp, SC_TINPUT_INFO);
+
+ return (0);
+}
+
+/*
+ * txt_emark --
+ * Set the end mark on the line.
+ */
+static int
+txt_emark(sp, tp, cno)
+ SCR *sp;
+ TEXT *tp;
+ size_t cno;
+{
+ CHAR_T ch, *kp;
+ size_t chlen, nlen, olen;
+ char *p;
+
+ ch = CH_ENDMARK;
+
+ /*
+ * The end mark may not be the same size as the current character.
+ * Don't let the line shift.
+ */
+ nlen = KEY_LEN(sp, ch);
+ if (tp->lb[cno] == '\t')
+ (void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen);
+ else
+ olen = KEY_LEN(sp, tp->lb[cno]);
+
+ /*
+ * If the line got longer, well, it's weird, but it's easy. If
+ * it's the same length, it's easy. If it got shorter, we have
+ * to fix it up.
+ */
+ if (olen > nlen) {
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + olen);
+ chlen = olen - nlen;
+ if (tp->insert != 0)
+ memmove(tp->lb + cno + 1 + chlen,
+ tp->lb + cno + 1, tp->insert);
+
+ tp->len += chlen;
+ tp->owrite += chlen;
+ p = tp->lb + cno;
+ if (tp->lb[cno] == '\t')
+ for (cno += chlen; chlen--;)
+ *p++ = ' ';
+ else
+ for (kp = KEY_NAME(sp, tp->lb[cno]),
+ cno += chlen; chlen--;)
+ *p++ = *kp++;
+ }
+ tp->lb[cno] = ch;
+ return (vs_change(sp, tp->lno, LINE_RESET));
+}
+
+/*
+ * txt_err --
+ * Handle an error during input processing.
+ */
+static void
+txt_err(sp, tiqh)
+ SCR *sp;
+ TEXTH *tiqh;
+{
+ recno_t lno;
+
+ /*
+ * 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;
+ !db_exist(sp, lno) && lno > 0; --lno);
+
+ sp->lno = lno == 0 ? 1 : lno;
+ sp->cno = 0;
+
+ /* Redraw the screen, just in case. */
+ F_SET(sp, SC_SCR_REDRAW);
+}
+
+/*
+ * txt_hex --
+ * Let the user insert any character value they want.
+ *
+ * !!!
+ * This is an extension. The pattern "^X[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)
+ SCR *sp;
+ TEXT *tp;
+{
+ CHAR_T 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[tp->cno];
+ tp->lb[tp->cno] = 0;
+
+ /* Find the previous CH_HEX character. */
+ for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
+ if (*p == CH_HEX) {
+ wp = p + 1;
+ break;
+ }
+ /* Not on this line? Shouldn't happen. */
+ if (off == tp->ai || off == tp->offset)
+ goto nothex;
+ }
+
+ /* If length of 0, then it wasn't a hex value. */
+ if (len == 0)
+ goto nothex;
+
+ /* Get the value. */
+ errno = 0;
+ value = strtol(wp, NULL, 16);
+ if (errno || value > MAX_CHAR_T) {
+nothex: tp->lb[tp->cno] = savec;
+ return (0);
+ }
+
+ /* Restore the original character. */
+ tp->lb[tp->cno] = savec;
+
+ /* Adjust the bookkeeping. */
+ tp->cno -= len;
+ tp->len -= len;
+ tp->lb[tp->cno - 1] = value;
+
+ /* Copy down any overwrite characters. */
+ if (tp->owrite)
+ memmove(tp->lb + tp->cno, tp->lb + tp->cno + len, tp->owrite);
+
+ /* Copy down any insert characters. */
+ if (tp->insert)
+ memmove(tp->lb + tp->cno + tp->owrite,
+ tp->lb + tp->cno + tp->owrite + len, tp->insert);
+
+ return (0);
+}
+
+/*
+ * txt_insch --
+ *
+ * !!!
+ * Historic vi did a special screen optimization for tab characters. As an
+ * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the
+ * rest of the string when it was displayed.
+ *
+ * Because early versions of this implementation redisplayed the entire line
+ * on each keystroke, the "bcd" was pushed to the right as it ignored that
+ * the user had "promised" to change the rest of the characters. However,
+ * the historic vi implementation had an even worse bug: given the keystrokes
+ * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
+ * on the second <esc> key.
+ *
+ * POSIX 1003.2 requires (will require) that this be fixed, specifying that
+ * vi overwrite characters the user has committed to changing, on the basis
+ * of the screen space they require, but that it not overwrite other characters.
+ */
+static int
+txt_insch(sp, tp, chp, flags)
+ SCR *sp;
+ TEXT *tp;
+ CHAR_T *chp;
+ u_int flags;
+{
+ CHAR_T *kp, savech;
+ size_t chlen, cno, copydown, olen, nlen;
+ char *p;
+
+ /*
+ * The 'R' command does one-for-one replacement, because there's
+ * no way to know how many characters the user intends to replace.
+ */
+ if (LF_ISSET(TXT_REPLACE)) {
+ if (tp->owrite) {
+ --tp->owrite;
+ tp->lb[tp->cno++] = *chp;
+ return (0);
+ }
+ } else if (tp->owrite) { /* Overwrite a character. */
+ cno = tp->cno;
+
+ /*
+ * If the old or new characters are tabs, then the length of the
+ * display depends on the character position in the display. We
+ * don't even try to handle this here, just ask the screen.
+ */
+ if (*chp == '\t') {
+ savech = tp->lb[cno];
+ tp->lb[cno] = '\t';
+ (void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen);
+ tp->lb[cno] = savech;
+ } else
+ nlen = KEY_LEN(sp, *chp);
+
+ /*
+ * Eat overwrite characters until we run out of them or we've
+ * handled the length of the new character. If we only eat
+ * part of an overwrite character, break it into its component
+ * elements and display the remaining components.
+ */
+ for (copydown = 0; nlen != 0 && tp->owrite != 0;) {
+ --tp->owrite;
+
+ if (tp->lb[cno] == '\t')
+ (void)vs_columns(sp,
+ tp->lb, tp->lno, &cno, &olen);
+ else
+ olen = KEY_LEN(sp, tp->lb[cno]);
+
+ if (olen == nlen) {
+ nlen = 0;
+ break;
+ }
+ if (olen < nlen) {
+ ++copydown;
+ nlen -= olen;
+ } else {
+ BINC_RET(sp,
+ tp->lb, tp->lb_len, tp->len + olen);
+ chlen = olen - nlen;
+ memmove(tp->lb + cno + 1 + chlen,
+ tp->lb + cno + 1, tp->owrite + tp->insert);
+
+ tp->len += chlen;
+ tp->owrite += chlen;
+ if (tp->lb[cno] == '\t')
+ for (p = tp->lb + cno + 1; chlen--;)
+ *p++ = ' ';
+ else
+ for (kp =
+ KEY_NAME(sp, tp->lb[cno]) + nlen,
+ p = tp->lb + cno + 1; chlen--;)
+ *p++ = *kp++;
+ nlen = 0;
+ break;
+ }
+ }
+
+ /*
+ * If had to erase several characters, we adjust the total
+ * count, and if there are any characters left, shift them
+ * into position.
+ */
+ if (copydown != 0 && (tp->len -= copydown) != 0)
+ memmove(tp->lb + cno, tp->lb + cno + copydown,
+ tp->owrite + tp->insert + copydown);
+
+ /* If we had enough overwrite characters, we're done. */
+ if (nlen == 0) {
+ tp->lb[tp->cno++] = *chp;
+ return (0);
+ }
+ }
+
+ /* Check to see if the character fits into the input buffer. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1);
+
+ ++tp->len;
+ if (tp->insert) { /* Insert a character. */
+ if (tp->insert == 1)
+ tp->lb[tp->cno + 1] = tp->lb[tp->cno];
+ else
+ memmove(tp->lb + tp->cno + 1,
+ tp->lb + tp->cno, tp->owrite + tp->insert);
+ }
+ tp->lb[tp->cno++] = *chp;
+ return (0);
+}
+
+/*
+ * txt_isrch --
+ * Do an incremental search.
+ */
+static int
+txt_isrch(sp, vp, tp, is_flagsp)
+ SCR *sp;
+ VICMD *vp;
+ TEXT *tp;
+ u_int8_t *is_flagsp;
+{
+ MARK start;
+ recno_t lno;
+ u_int sf;
+
+ /* If it's a one-line screen, we don't do incrementals. */
+ if (IS_ONELINE(sp)) {
+ FL_CLR(*is_flagsp, IS_RUNNING);
+ return (0);
+ }
+
+ /*
+ * If the user erases back to the beginning of the buffer, there's
+ * nothing to search for. Reset the cursor to the starting point.
+ */
+ if (tp->cno <= 1) {
+ vp->m_final = vp->m_start;
+ return (0);
+ }
+
+ /*
+ * If it's an RE quote character, and not quoted, ignore it until
+ * we get another character.
+ */
+ if (tp->lb[tp->cno - 1] == '\\' &&
+ (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
+ return (0);
+
+ /*
+ * If it's a magic shell character, and not quoted, reset the cursor
+ * to the starting point.
+ */
+ if (strchr(O_STR(sp, O_SHELLMETA), tp->lb[tp->cno - 1]) != NULL &&
+ (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
+ vp->m_final = vp->m_start;
+
+ /*
+ * If we see the search pattern termination character, then quit doing
+ * an incremental search. There may be more, e.g., ":/foo/;/bar/",
+ * and we can't handle that incrementally. Also, reset the cursor to
+ * the original location, the ex search routines don't know anything
+ * about incremental searches.
+ */
+ if (tp->lb[0] == tp->lb[tp->cno - 1] &&
+ (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) {
+ vp->m_final = vp->m_start;
+ FL_CLR(*is_flagsp, IS_RUNNING);
+ return (0);
+ }
+
+ /*
+ * Remember the input line and discard the special input map,
+ * but don't overwrite the input line on the screen.
+ */
+ lno = tp->lno;
+ F_SET(VIP(sp), VIP_S_MODELINE);
+ F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO);
+ if (txt_map_end(sp))
+ return (1);
+
+ /*
+ * Specify a starting point and search. If we find a match, move to
+ * it and refresh the screen. If we didn't find the match, then we
+ * beep the screen. When searching from the original cursor position,
+ * we have to move the cursor, otherwise, we don't want to move the
+ * cursor in case the text at the current position continues to match.
+ */
+ if (FL_ISSET(*is_flagsp, IS_RESTART)) {
+ start = vp->m_start;
+ sf = SEARCH_SET;
+ } else {
+ start = vp->m_final;
+ sf = SEARCH_INCR | SEARCH_SET;
+ }
+
+ if (tp->lb[0] == '/' ?
+ !f_search(sp,
+ &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) :
+ !b_search(sp,
+ &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) {
+ sp->lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ FL_CLR(*is_flagsp, IS_RESTART);
+
+ if (!KEYS_WAITING(sp) && vs_refresh(sp, 0))
+ return (1);
+ } else
+ FL_SET(*is_flagsp, IS_RESTART);
+
+ /* Reinstantiate the special input map. */
+ if (txt_map_init(sp))
+ return (1);
+ F_CLR(VIP(sp), VIP_S_MODELINE);
+ F_SET(sp, SC_TINPUT | SC_TINPUT_INFO);
+
+ /* Reset the line number of the input line. */
+ tp->lno = TMAP[0].lno;
+
+ /*
+ * If the colon command-line moved, i.e. the screen scrolled,
+ * refresh the input line.
+ *
+ * XXX
+ * We shouldn't be calling vs_line, here -- we need dirty bits
+ * on entries in the SMAP array.
+ */
+ if (lno != TMAP[0].lno) {
+ if (vs_line(sp, &TMAP[0], NULL, NULL))
+ return (1);
+ (void)sp->gp->scr_refresh(sp, 0);
+ }
+ return (0);
+}
+
+/*
+ * txt_resolve --
+ * Resolve the input text chain into the file.
+ */
+static int
+txt_resolve(sp, tiqh, flags)
+ SCR *sp;
+ TEXTH *tiqh;
+ u_int32_t flags;
+{
+ VI_PRIVATE *vip;
+ TEXT *tp;
+ recno_t lno;
+ int changed;
+
+ /*
+ * The first line replaces a current line, and all subsequent lines
+ * are appended into the file. Resolve autoindented characters for
+ * each line before committing it. If the latter causes the line to
+ * change, we have to redisplay it, otherwise the information cached
+ * about the line will be wrong.
+ */
+ vip = VIP(sp);
+ tp = tiqh->cqh_first;
+
+ if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp, &changed);
+ else
+ changed = 0;
+ if (db_set(sp, tp->lno, tp->lb, tp->len) ||
+ changed && vs_change(sp, tp->lno, LINE_RESET))
+ return (1);
+
+ for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno) {
+ if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp, &changed);
+ else
+ changed = 0;
+ if (db_append(sp, 0, lno, tp->lb, tp->len) ||
+ changed && vs_change(sp, tp->lno, LINE_RESET))
+ return (1);
+ }
+
+ /*
+ * Clear the input flag, the look-aside buffer is no longer valid.
+ * Has to be done as part of text resolution, or upon return we'll
+ * be looking at incorrect data.
+ */
+ F_CLR(sp, SC_TINPUT);
+
+ return (0);
+}
+
+/*
+ * txt_showmatch --
+ * Show a character match.
+ *
+ * !!!
+ * Historic vi tried to display matches even in the :colon command line.
+ * I think not.
+ */
+static int
+txt_showmatch(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ GS *gp;
+ VCS cs;
+ MARK m;
+ int cnt, endc, startc;
+
+ gp = sp->gp;
+
+ /*
+ * Do a refresh first, in case we haven't done one in awhile,
+ * so the user can see what we're complaining about.
+ */
+ UPDATE_POSITION(sp, tp);
+ if (vs_refresh(sp, 1))
+ return (1);
+
+ /*
+ * We don't display the match if it's not on the screen. Find
+ * out what the first character on the screen is.
+ */
+ if (vs_sm_position(sp, &m, 0, P_TOP))
+ return (1);
+
+ /* Initialize the getc() interface. */
+ cs.cs_lno = tp->lno;
+ cs.cs_cno = tp->cno - 1;
+ if (cs_init(sp, &cs))
+ return (1);
+ startc = (endc = cs.cs_ch) == ')' ? '(' : '{';
+
+ /* Search for the match. */
+ for (cnt = 1;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
+ msgq(sp, M_BERR,
+ "Unmatched %s", KEY_NAME(sp, endc));
+ return (0);
+ }
+ continue;
+ }
+ if (cs.cs_ch == endc)
+ ++cnt;
+ else if (cs.cs_ch == startc && --cnt == 0)
+ break;
+ }
+
+ /* If the match is on the screen, move to it. */
+ if (cs.cs_lno < m.lno || cs.cs_lno == m.lno && cs.cs_cno < m.cno)
+ return (0);
+ sp->lno = cs.cs_lno;
+ sp->cno = cs.cs_cno;
+ if (vs_refresh(sp, 1))
+ return (1);
+
+ /* Wait for timeout or character arrival. */
+ return (v_event_get(sp,
+ NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT));
+}
+
+/*
+ * txt_margin --
+ * Handle margin wrap.
+ */
+static int
+txt_margin(sp, tp, wmtp, didbreak, flags)
+ SCR *sp;
+ TEXT *tp, *wmtp;
+ int *didbreak;
+ u_int32_t flags;
+{
+ VI_PRIVATE *vip;
+ size_t len, off;
+ char *p, *wp;
+
+ /* Find the nearest previous blank. */
+ for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
+ if (isblank(*p)) {
+ wp = p + 1;
+ break;
+ }
+
+ /*
+ * If reach the start of the line, there's nowhere to break.
+ *
+ * !!!
+ * Historic vi belled 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 as it tends to wake the
+ * cats.
+ */
+ if (off == tp->ai || off == tp->offset) {
+ *didbreak = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * Store saved information about the rest of the line in the
+ * wrapmargin TEXT structure.
+ *
+ * !!!
+ * The offset field holds the length of the current characters
+ * that the user entered, but which are getting split to the new
+ * line -- it's going to be used to set the cursor value when we
+ * move to the new line.
+ */
+ vip = VIP(sp);
+ wmtp->lb = p + 1;
+ wmtp->offset = len;
+ wmtp->insert = LF_ISSET(TXT_APPENDEOL) ? tp->insert - 1 : tp->insert;
+ wmtp->owrite = tp->owrite;
+
+ /* Correct current bookkeeping information. */
+ tp->cno -= len;
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ tp->len -= len + tp->owrite + (tp->insert - 1);
+ tp->insert = 1;
+ } else {
+ tp->len -= len + tp->owrite + tp->insert;
+ tp->insert = 0;
+ }
+ tp->owrite = 0;
+
+ /*
+ * !!!
+ * Delete any trailing whitespace from the current line.
+ */
+ for (;; --p, --off) {
+ if (!isblank(*p))
+ break;
+ --tp->cno;
+ --tp->len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+ *didbreak = 1;
+ return (0);
+}
+
+/*
+ * txt_Rresolve --
+ * Resolve the input line for the 'R' command.
+ */
+static void
+txt_Rresolve(sp, tiqh, tp, orig_len)
+ SCR *sp;
+ TEXTH *tiqh;
+ TEXT *tp;
+ const size_t orig_len;
+{
+ TEXT *ttp;
+ size_t input_len, retain;
+ char *p;
+
+ /*
+ * Check to make sure that the cursor hasn't moved beyond
+ * the end of the line.
+ */
+ if (tp->owrite == 0)
+ return;
+
+ /*
+ * Calculate how many characters the user has entered,
+ * plus the blanks erased by <carriage-return>/<newline>s.
+ */
+ for (ttp = tiqh->cqh_first, input_len = 0;;) {
+ input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase;
+ if ((ttp = ttp->q.cqe_next) == (void *)&sp->tiq)
+ break;
+ }
+
+ /*
+ * If the user has entered less characters than the original line
+ * was long, restore any overwriteable characters to the original
+ * characters. These characters are entered as "insert characters",
+ * because they're after the cursor and we don't want to lose them.
+ * (This is okay because the R command has no insert characters.)
+ * We set owrite to 0 so that the insert characters don't get copied
+ * to somewhere else, which means that the line and the length have
+ * to be adjusted here as well.
+ *
+ * We have to retrieve the original line because the original pinned
+ * page has long since been discarded. If it doesn't exist, that's
+ * okay, the user just extended the file.
+ */
+ if (input_len < orig_len) {
+ retain = MIN(tp->owrite, orig_len - input_len);
+ if (db_get(sp,
+ tiqh->cqh_first->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL))
+ return;
+ memcpy(tp->lb + tp->cno, p + input_len, retain);
+ tp->len -= tp->owrite - retain;
+ tp->owrite = 0;
+ tp->insert += retain;
+ }
+}
+
+/*
+ * txt_nomorech --
+ * No more characters message.
+ */
+static void
+txt_nomorech(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "194|No more characters to erase");
+}
diff --git a/contrib/nvi/vi/v_ulcase.c b/contrib/nvi/vi/v_ulcase.c
new file mode 100644
index 000000000000..179bebabcc11
--- /dev/null
+++ b/contrib/nvi/vi/v_ulcase.c
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_ulcase.c 10.7 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static int ulcase __P((SCR *, recno_t, CHAR_T *, size_t, size_t, size_t));
+
+/*
+ * v_ulcase -- [count]~
+ * Toggle upper & lower case letters.
+ *
+ * !!!
+ * Historic vi didn't permit ~ to cross newline boundaries. I can
+ * think of no reason why it shouldn't, which at least lets the user
+ * auto-repeat through a paragraph.
+ *
+ * !!!
+ * 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 make
+ * that the default now.
+ *
+ * PUBLIC: int v_ulcase __P((SCR *, VICMD *));
+ */
+int
+v_ulcase(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t lno;
+ size_t cno, lcnt, len;
+ u_long cnt;
+ char *p;
+
+ lno = vp->m_start.lno;
+ cno = vp->m_start.cno;
+
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt > 0; cno = 0) {
+ /* SOF is an error, EOF is an infinite count sink. */
+ if (db_get(sp, lno, 0, &p, &len)) {
+ if (lno == 1) {
+ v_emsg(sp, NULL, VIM_EMPTY);
+ return (1);
+ }
+ --lno;
+ break;
+ }
+
+ /* Empty lines decrement the count by one. */
+ if (len == 0) {
+ --cnt;
+ vp->m_final.cno = 0;
+ continue;
+ }
+
+ if (cno + cnt >= len) {
+ lcnt = len - 1;
+ cnt -= len - cno;
+
+ vp->m_final.cno = len - 1;
+ } else {
+ lcnt = cno + cnt - 1;
+ cnt = 0;
+
+ vp->m_final.cno = lcnt + 1;
+ }
+
+ if (ulcase(sp, lno, p, len, cno, lcnt))
+ return (1);
+
+ if (cnt > 0)
+ ++lno;
+ }
+
+ vp->m_final.lno = lno;
+ return (0);
+}
+
+/*
+ * v_mulcase -- [count]~[count]motion
+ * Toggle upper & lower case letters over a range.
+ *
+ * PUBLIC: int v_mulcase __P((SCR *, VICMD *));
+ */
+int
+v_mulcase(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ CHAR_T *p;
+ size_t len;
+ recno_t lno;
+
+ for (lno = vp->m_start.lno;;) {
+ if (db_get(sp, lno, DBG_FATAL, &p, &len))
+ return (1);
+ if (len != 0 && ulcase(sp, lno, p, len,
+ lno == vp->m_start.lno ? vp->m_start.cno : 0,
+ !F_ISSET(vp, VM_LMODE) &&
+ lno == vp->m_stop.lno ? vp->m_stop.cno : len))
+ return (1);
+
+ if (++lno > vp->m_stop.lno)
+ break;
+ }
+
+ /*
+ * XXX
+ * I didn't create a new motion command when I added motion semantics
+ * for ~. While that's the correct way to do it, that choice would
+ * have required changes all over the vi directory for little gain.
+ * Instead, we pretend it's a yank command. Note, this means that we
+ * follow the cursor motion rules for yank commands, but that seems
+ * reasonable to me.
+ */
+ return (0);
+}
+
+/*
+ * ulcase --
+ * Change part of a line's case.
+ */
+static int
+ulcase(sp, lno, lp, len, scno, ecno)
+ SCR *sp;
+ recno_t lno;
+ CHAR_T *lp;
+ size_t len, scno, ecno;
+{
+ size_t blen;
+ int change, rval;
+ CHAR_T ch, *p, *t;
+ char *bp;
+
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, lp, len);
+
+ change = rval = 0;
+ for (p = bp + scno, t = bp + ecno + 1; p < t; ++p) {
+ ch = *(u_char *)p;
+ if (islower(ch)) {
+ *p = toupper(ch);
+ change = 1;
+ } else if (isupper(ch)) {
+ *p = tolower(ch);
+ change = 1;
+ }
+ }
+
+ if (change && db_set(sp, lno, bp, len))
+ rval = 1;
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/contrib/nvi/vi/v_undo.c b/contrib/nvi/vi/v_undo.c
new file mode 100644
index 000000000000..d04a8a18810b
--- /dev/null
+++ b/contrib/nvi/vi/v_undo.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_undo.c 10.5 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_Undo -- U
+ * Undo changes to this line.
+ *
+ * PUBLIC: int v_Undo __P((SCR *, VICMD *));
+ */
+int
+v_Undo(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /*
+ * 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.
+ */
+ vp->m_final.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(sp->ep, F_UNDO);
+ sp->ep->lundo = BACKWARD;
+
+ return (log_setline(sp));
+}
+
+/*
+ * v_undo -- u
+ * Undo the last change.
+ *
+ * PUBLIC: int v_undo __P((SCR *, VICMD *));
+ */
+int
+v_undo(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ EXF *ep;
+
+ /* 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.
+ *
+ * !!!
+ * There is change to historic practice for the final cursor position
+ * in this implementation. In historic vi, if an undo was isolated to
+ * a single line, the cursor moved to the start of the change, and
+ * then, subsequent 'u' commands would not move it again. (It has been
+ * pointed out that users used multiple undo commands to get the cursor
+ * to the start of the changed text.) Nvi toggles between the cursor
+ * position before and after the change was made. One final issue is
+ * that historic vi only did this if the user had not moved off of the
+ * line before entering the undo command; otherwise, vi would move the
+ * cursor to the most attractive position on the changed line.
+ *
+ * It would be difficult to match historic practice in this area. You
+ * not only have to know that the changes were isolated to one line,
+ * but whether it was the first or second undo command as well. And,
+ * to completely match historic practice, we'd have to track users line
+ * changes, too. This isn't worth the effort.
+ */
+ ep = sp->ep;
+ 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, &vp->m_final));
+ case FORWARD:
+ return (log_forward(sp, &vp->m_final));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
diff --git a/contrib/nvi/vi/v_util.c b/contrib/nvi/vi/v_util.c
new file mode 100644
index 000000000000..c4c0daa901fd
--- /dev/null
+++ b/contrib/nvi/vi/v_util.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_util.c 10.11 (Berkeley) 6/30/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_eof --
+ * Vi end-of-file error.
+ *
+ * PUBLIC: void v_eof __P((SCR *, MARK *));
+ */
+void
+v_eof(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ recno_t lno;
+
+ if (mp == NULL)
+ v_emsg(sp, NULL, VIM_EOF);
+ else {
+ if (db_last(sp, &lno))
+ return;
+ if (mp->lno >= lno)
+ v_emsg(sp, NULL, VIM_EOF);
+ else
+ msgq(sp, M_BERR, "195|Movement past the end-of-file");
+ }
+}
+
+/*
+ * v_eol --
+ * Vi end-of-line error.
+ *
+ * PUBLIC: void v_eol __P((SCR *, MARK *));
+ */
+void
+v_eol(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ size_t len;
+
+ if (mp == NULL)
+ v_emsg(sp, NULL, VIM_EOL);
+ else {
+ if (db_get(sp, mp->lno, DBG_FATAL, NULL, &len))
+ return;
+ if (mp->cno == len - 1)
+ v_emsg(sp, NULL, VIM_EOL);
+ else
+ msgq(sp, M_BERR, "196|Movement past the end-of-line");
+ }
+}
+
+/*
+ * v_nomove --
+ * Vi no cursor movement error.
+ *
+ * PUBLIC: void v_nomove __P((SCR *));
+ */
+void
+v_nomove(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "197|No cursor movement made");
+}
+
+/*
+ * v_sof --
+ * Vi start-of-file error.
+ *
+ * PUBLIC: void v_sof __P((SCR *, MARK *));
+ */
+void
+v_sof(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ if (mp == NULL || mp->lno == 1)
+ msgq(sp, M_BERR, "198|Already at the beginning of the file");
+ else
+ msgq(sp, M_BERR, "199|Movement past the beginning of the file");
+}
+
+/*
+ * v_sol --
+ * Vi start-of-line error.
+ *
+ * PUBLIC: void v_sol __P((SCR *));
+ */
+void
+v_sol(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "200|Already in the first column");
+}
+
+/*
+ * v_isempty --
+ * Return if the line contains nothing but white-space characters.
+ *
+ * PUBLIC: int v_isempty __P((char *, size_t));
+ */
+int
+v_isempty(p, len)
+ char *p;
+ size_t len;
+{
+ for (; len--; ++p)
+ if (!isblank(*p))
+ return (0);
+ return (1);
+}
+
+/*
+ * v_emsg --
+ * Display a few common vi messages.
+ *
+ * PUBLIC: void v_emsg __P((SCR *, char *, vim_t));
+ */
+void
+v_emsg(sp, p, which)
+ SCR *sp;
+ char *p;
+ vim_t which;
+{
+ switch (which) {
+ case VIM_COMBUF:
+ msgq(sp, M_ERR,
+ "201|Buffers should be specified before the command");
+ break;
+ case VIM_EMPTY:
+ msgq(sp, M_BERR, "209|The file is empty");
+ break;
+ case VIM_EOF:
+ msgq(sp, M_BERR, "202|Already at end-of-file");
+ break;
+ case VIM_EOL:
+ msgq(sp, M_BERR, "203|Already at end-of-line");
+ break;
+ case VIM_NOCOM:
+ case VIM_NOCOM_B:
+ msgq(sp,
+ which == VIM_NOCOM_B ? M_BERR : M_ERR,
+ "204|%s isn't a vi command", p);
+ break;
+ case VIM_WRESIZE:
+ msgq(sp, M_ERR, "Window resize interrupted text input mode");
+ break;
+ case VIM_USAGE:
+ msgq(sp, M_ERR, "205|Usage: %s", p);
+ break;
+ }
+}
diff --git a/contrib/nvi/vi/v_word.c b/contrib/nvi/vi/v_word.c
new file mode 100644
index 000000000000..e6e4a63c57e7
--- /dev/null
+++ b/contrib/nvi/vi/v_word.c
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_word.c 10.5 (Berkeley) 3/6/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.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" -- in particular, single character words, and commands that
+ * began in whitespace were almost always handled incorrectly. 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, or, if you did it from the start of the line as part of a motion
+ * command, go absolutely nuts. If the lines contained only white-space
+ * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
+ * 'b', 'E' and 'e' commands would treat the group as a single word, and
+ * the 'B' and 'b' commands will treat the lines as individual words. This
+ * implementation treats all of these cases as a single white-space word.
+ */
+
+enum which {BIGWORD, LITTLEWORD};
+
+static int bword __P((SCR *, VICMD *, enum which));
+static int eword __P((SCR *, VICMD *, enum which));
+static int fword __P((SCR *, VICMD *, enum which));
+
+/*
+ * v_wordW -- [count]W
+ * Move forward a bigword at a time.
+ *
+ * PUBLIC: int v_wordW __P((SCR *, VICMD *));
+ */
+int
+v_wordW(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (fword(sp, vp, BIGWORD));
+}
+
+/*
+ * v_wordw -- [count]w
+ * Move forward a word at a time.
+ *
+ * PUBLIC: int v_wordw __P((SCR *, VICMD *));
+ */
+int
+v_wordw(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (fword(sp, vp, LITTLEWORD));
+}
+
+/*
+ * fword --
+ * Move forward by words.
+ */
+static int
+fword(sp, vp, type)
+ SCR *sp;
+ VICMD *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, &cs))
+ return (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 (ISMOTION(vp) && cs.cs_flags != CS_EMP && cnt == 1) {
+ if (ISCMD(vp->rkp, 'c'))
+ return (0);
+ if (ISCMD(vp->rkp, 'd') || ISCMD(vp->rkp, 'y')) {
+ if (cs_fspace(sp, &cs))
+ return (1);
+ goto ret;
+ }
+ }
+ if (cs_fblank(sp, &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, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ 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 && ISMOTION(vp)) {
+ if ((ISCMD(vp->rkp, 'd') ||
+ ISCMD(vp->rkp, 'y')) &&
+ cs_fspace(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ 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 && ISMOTION(vp)) {
+ if ((ISCMD(vp->rkp, 'd') ||
+ ISCMD(vp->rkp, 'y')) &&
+ cs_fspace(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, &vp->m_start);
+ return (1);
+ }
+
+ /* Adjust the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+ if (ISMOTION(vp) && cs.cs_flags == 0)
+ --vp->m_stop.cno;
+
+ /*
+ * Non-motion commands move to the end of the range. Delete
+ * and yank stay at the start, ignore others.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_wordE -- [count]E
+ * Move forward to the end of the bigword.
+ *
+ * PUBLIC: int v_wordE __P((SCR *, VICMD *));
+ */
+int
+v_wordE(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (eword(sp, vp, BIGWORD));
+}
+
+/*
+ * v_worde -- [count]e
+ * Move forward to the end of the word.
+ *
+ * PUBLIC: int v_worde __P((SCR *, VICMD *));
+ */
+int
+v_worde(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (eword(sp, vp, LITTLEWORD));
+}
+
+/*
+ * eword --
+ * Move forward to the end of the word.
+ */
+static int
+eword(sp, vp, type)
+ SCR *sp;
+ VICMD *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the next character is whitespace, move past
+ * it. (This doesn't count as a word move.) Stay at the character
+ * past the current one, it sets word "state" for the 'e' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_next(sp, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_fblank(sp, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'e' command, the definition of a word keeps
+ * switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the start of the word after the last
+ * word, we're done. If we changed state, back up one
+ * to the end of the previous word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_prev(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ 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) {
+ if (cs.cs_flags == 0 && cs_prev(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * Non-motion commands move to the end of the range.
+ * Delete and yank stay at the start, ignore others.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_WordB -- [count]B
+ * Move backward a bigword at a time.
+ *
+ * PUBLIC: int v_wordB __P((SCR *, VICMD *));
+ */
+int
+v_wordB(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (bword(sp, vp, BIGWORD));
+}
+
+/*
+ * v_wordb -- [count]b
+ * Move backward a word at a time.
+ *
+ * PUBLIC: int v_wordb __P((SCR *, VICMD *));
+ */
+int
+v_wordb(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ return (bword(sp, vp, LITTLEWORD));
+}
+
+/*
+ * bword --
+ * Move backward by words.
+ */
+static int
+bword(sp, vp, type)
+ SCR *sp;
+ VICMD *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the previous character is whitespace, move
+ * past it. (This doesn't count as a word move.) Stay at the
+ * character before the current one, it sets word "state" for the
+ * 'b' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_bblank(sp, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the beginning of the previous word -- this
+ * involves skipping over word characters and then any trailing
+ * non-word characters. Note, for the 'b' command, the definition
+ * of a word keeps switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the end of the word before the last
+ * word, we're done. If we changed state, move forward
+ * one to the end of the next word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_next(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_bblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_prev(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ 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) {
+ if (cs.cs_flags == 0 && cs_next(sp, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_bblank(sp, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+
+ /* If we didn't move, we must be at SOF. */
+ret: if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * All commands move to the end of the range. Motion commands
+ * adjust the starting point to the character before the current
+ * one.
+ *
+ * !!!
+ * The historic vi didn't get this right -- the `yb' command yanked
+ * the right stuff and even updated the cursor value, but the cursor
+ * was not actually updated on the screen.
+ */
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_xchar.c b/contrib/nvi/vi/v_xchar.c
new file mode 100644
index 000000000000..15f155ffed33
--- /dev/null
+++ b/contrib/nvi/vi/v_xchar.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_xchar.c 10.9 (Berkeley) 10/23/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_xchar -- [buffer] [count]x
+ * Deletes the character(s) on which the cursor sits.
+ *
+ * PUBLIC: int v_xchar __P((SCR *, VICMD *));
+ */
+int
+v_xchar(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+ int isempty;
+
+ if (db_eget(sp, vp->m_start.lno, NULL, &len, &isempty)) {
+ if (isempty)
+ goto nodel;
+ return (1);
+ }
+ if (len == 0) {
+nodel: msgq(sp, M_BERR, "206|No characters to delete");
+ return (1);
+ }
+
+ /*
+ * Delete 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.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ vp->m_stop.cno += vp->count - 1;
+ if (vp->m_stop.cno >= len - 1) {
+ vp->m_stop.cno = len - 1;
+ vp->m_final.cno = vp->m_start.cno ? vp->m_start.cno - 1 : 0;
+ } else
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (del(sp, &vp->m_start, &vp->m_stop, 0));
+}
+
+/*
+ * v_Xchar -- [buffer] [count]X
+ * Deletes the character(s) immediately before the current cursor
+ * position.
+ *
+ * PUBLIC: int v_Xchar __P((SCR *, VICMD *));
+ */
+int
+v_Xchar(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ u_long cnt;
+
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt >= vp->m_start.cno)
+ vp->m_start.cno = 0;
+ else
+ vp->m_start.cno -= cnt;
+ --vp->m_stop.cno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (del(sp, &vp->m_start, &vp->m_stop, 0));
+}
diff --git a/contrib/nvi/vi/v_yank.c b/contrib/nvi/vi/v_yank.c
new file mode 100644
index 000000000000..4708f1996774
--- /dev/null
+++ b/contrib/nvi/vi/v_yank.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_yank.c 10.9 (Berkeley) 5/19/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_yank -- [buffer][count]y[count][motion]
+ * [buffer][count]Y
+ * Yank text (or lines of text) into a cut buffer.
+ *
+ * !!!
+ * Historic vi moved the cursor to the from MARK if it was before the current
+ * cursor and on a different line, e.g., "yk" moves the cursor but "yj" and
+ * "yl" do 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. Hopefully, the motion component code
+ * got it right... Unlike delete, we make no adjustments here.
+ *
+ * PUBLIC: int v_yank __P((SCR *, VICMD *));
+ */
+int
+v_yank(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ size_t len;
+
+ if (cut(sp,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, &vp->m_start,
+ &vp->m_stop, F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0))
+ return (1);
+ sp->rptlines[L_YANKED] += (vp->m_stop.lno - vp->m_start.lno) + 1;
+
+ /*
+ * One special correction, in case we've deleted the current line or
+ * character. We check it here instead of checking in every command
+ * that can be a motion component.
+ */
+ if (db_get(sp, vp->m_final.lno, DBG_FATAL, NULL, &len))
+ return (1);
+
+ /*
+ * !!!
+ * Cursor movements, other than those caused by a line mode command
+ * moving to another line, historically reset the relative position.
+ *
+ * This currently matches the check made in v_delete(), I'm hoping
+ * that they should be consistent...
+ */
+ if (!F_ISSET(vp, VM_LMODE)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SET);
+
+ /* Make sure the set cursor position exists. */
+ if (vp->m_final.cno >= len)
+ vp->m_final.cno = len ? len - 1 : 0;
+ }
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_z.c b/contrib/nvi/vi/v_z.c
new file mode 100644
index 000000000000..5f02ddf5a10b
--- /dev/null
+++ b/contrib/nvi/vi/v_z.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_z.c 10.10 (Berkeley) 5/16/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_z -- [count]z[count][-.+^<CR>]
+ * Move the screen.
+ *
+ * PUBLIC: int v_z __P((SCR *, VICMD *));
+ */
+int
+v_z(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ recno_t 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 (!db_exist(sp, lno) && db_last(sp, &lno))
+ return (1);
+ } else
+ lno = vp->m_start.lno;
+
+ /* Set default return cursor line. */
+ vp->m_final.lno = lno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ /*
+ * The second count is the displayed window size, i.e. the 'z' command
+ * is another way to get artificially small windows. Note, you can't
+ * grow beyond the size of the window.
+ *
+ * !!!
+ * A window size of 0 was historically allowed, and simply ignored.
+ * 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) {
+ if (vp->count2 > O_VAL(sp, O_WINDOW))
+ vp->count2 = O_VAL(sp, O_WINDOW);
+ if (vs_crel(sp, vp->count2))
+ return (1);
+ }
+
+ switch (vp->character) {
+ case '-': /* Put the line at the bottom. */
+ if (vs_sm_fill(sp, lno, P_BOTTOM))
+ return (1);
+ break;
+ case '.': /* Put the line in the middle. */
+ if (vs_sm_fill(sp, lno, P_MIDDLE))
+ return (1);
+ break;
+ case '+':
+ /*
+ * If the user specified a line number, put that line at the
+ * top and move the cursor to it. Otherwise, scroll forward
+ * a screen from the current screen.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (vs_sm_fill(sp, lno, P_TOP))
+ return (1);
+ if (vs_sm_position(sp, &vp->m_final, 0, P_TOP))
+ return (1);
+ } else
+ if (vs_sm_scroll(sp, &vp->m_final, sp->t_rows, Z_PLUS))
+ return (1);
+ break;
+ case '^':
+ /*
+ * If the user specified a line number, put that line at the
+ * bottom, move the cursor to it, and then display the screen
+ * before that one. Otherwise, scroll backward a screen from
+ * the current screen.
+ *
+ * !!!
+ * Note, we match the off-by-one characteristics of historic
+ * vi, here.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (vs_sm_fill(sp, lno, P_BOTTOM))
+ return (1);
+ if (vs_sm_position(sp, &vp->m_final, 0, P_TOP))
+ return (1);
+ if (vs_sm_fill(sp, vp->m_final.lno, P_BOTTOM))
+ return (1);
+ } else
+ if (vs_sm_scroll(sp, &vp->m_final, sp->t_rows, Z_CARAT))
+ return (1);
+ break;
+ default: /* Put the line at the top for <cr>. */
+ value = KEY_VAL(sp, vp->character);
+ if (value != K_CR && value != K_NL) {
+ v_emsg(sp, vp->kp->usage, VIM_USAGE);
+ return (1);
+ }
+ if (vs_sm_fill(sp, lno, P_TOP))
+ return (1);
+ break;
+ }
+ return (0);
+}
+
+/*
+ * vs_crel --
+ * Change the relative size of the current screen.
+ *
+ * PUBLIC: int vs_crel __P((SCR *, long));
+ */
+int
+vs_crel(sp, count)
+ SCR *sp;
+ long count;
+{
+ 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, SC_SCR_REDRAW);
+ return (0);
+}
diff --git a/contrib/nvi/vi/v_zexit.c b/contrib/nvi/vi/v_zexit.c
new file mode 100644
index 000000000000..3e454caa5bea
--- /dev/null
+++ b/contrib/nvi/vi/v_zexit.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)v_zexit.c 10.6 (Berkeley) 4/27/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * v_zexit -- ZZ
+ * Save the file and exit.
+ *
+ * PUBLIC: int v_zexit __P((SCR *, VICMD *));
+ */
+int
+v_zexit(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ /* Write back any modifications. */
+ if (F_ISSET(sp->ep, F_MODIFIED) &&
+ file_write(sp, NULL, NULL, NULL, FS_ALL))
+ return (1);
+
+ /* Check to make sure it's not a temporary file. */
+ if (file_m3(sp, 0))
+ return (1);
+
+ /* Check for more files to edit. */
+ if (ex_ncheck(sp, 0))
+ return (1);
+
+ F_SET(sp, SC_EXIT);
+ return (0);
+}
diff --git a/contrib/nvi/vi/vi.c b/contrib/nvi/vi/vi.c
new file mode 100644
index 000000000000..d20f7f284e39
--- /dev/null
+++ b/contrib/nvi/vi/vi.c
@@ -0,0 +1,1251 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vi.c 10.57 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+typedef enum {
+ GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK
+} gcret_t;
+
+static VIKEYS const
+ *v_alias __P((SCR *, VICMD *, VIKEYS const *));
+static gcret_t v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *));
+static int v_count __P((SCR *, ARG_CHAR_T, u_long *));
+static void v_dtoh __P((SCR *));
+static int v_init __P((SCR *));
+static gcret_t v_key __P((SCR *, int, EVENT *, u_int32_t));
+static int v_keyword __P((SCR *));
+static int v_motion __P((SCR *, VICMD *, VICMD *, int *));
+
+#if defined(DEBUG) && defined(COMLOG)
+static void v_comlog __P((SCR *, VICMD *));
+#endif
+
+/*
+ * 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.
+ *
+ * PUBLIC: int vi __P((SCR **));
+ */
+int
+vi(spp)
+ SCR **spp;
+{
+ GS *gp;
+ MARK abs;
+ SCR *next, *sp;
+ VICMD cmd, *vp;
+ VI_PRIVATE *vip;
+ int comcount, mapped, rval;
+
+ /* Get the first screen. */
+ sp = *spp;
+ gp = sp->gp;
+
+ /* Initialize the command structure. */
+ vp = &cmd;
+ memset(vp, 0, sizeof(VICMD));
+
+ /* Reset strange attraction. */
+ F_SET(vp, VM_RCM_SET);
+
+ /* Initialize the vi screen. */
+ if (v_init(sp))
+ return (1);
+
+ /* Set the focus. */
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+
+ for (vip = VIP(sp), rval = 0;;) {
+ /* Resolve messages. */
+ if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
+ goto ret;
+
+ /*
+ * If not skipping a refresh, return to command mode and
+ * refresh the screen.
+ */
+ if (F_ISSET(vip, VIP_S_REFRESH))
+ F_CLR(vip, VIP_S_REFRESH);
+ else {
+ sp->showmode = SM_COMMAND;
+ if (vs_refresh(sp, 0))
+ goto ret;
+ }
+
+ /* Set the new favorite position. */
+ if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
+ F_CLR(vip, VIP_RCM_LAST);
+ (void)vs_column(sp, &sp->rcm);
+ }
+
+ /*
+ * If not currently in a map, log the cursor position,
+ * and set a flag so that this command can become the
+ * DOT command.
+ */
+ if (MAPPED_KEYS_WAITING(sp))
+ mapped = 1;
+ else {
+ if (log_cursor(sp))
+ goto err;
+ mapped = 0;
+ }
+
+ /*
+ * There may be an ex command waiting, and we returned here
+ * only because we exited a screen or file. In this case,
+ * we simply go back into the ex parser.
+ */
+ if (EXCMD_RUNNING(gp)) {
+ vp->kp = &vikeys[':'];
+ goto ex_continue;
+ }
+
+ /* Refresh the command structure. */
+ memset(vp, 0, sizeof(VICMD));
+
+ /*
+ * 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.
+ *
+ * !!!
+ * Vi historically flushed mapped characters on error, but
+ * entering extra <escape> characters at the beginning of
+ * a map wasn't considered an error -- in fact, users would
+ * put leading <escape> characters in maps to clean up vi
+ * state before the map was interpreted. Beauty!
+ */
+ switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
+ case GC_ERR:
+ goto err;
+ case GC_ERR_NOFLUSH:
+ goto gc_err_noflush;
+ case GC_EVENT:
+ if (v_event_exec(sp, vp))
+ goto err;
+ goto gc_event;
+ case GC_FATAL:
+ goto ret;
+ case GC_INTERRUPT:
+ goto intr;
+ case GC_OK:
+ break;
+ }
+
+ /* Check for security setting. */
+ if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
+ ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
+ 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;
+
+ /* Copy the key flags into the local structure. */
+ F_SET(vp, vp->kp->flags);
+
+ /* Prepare to set the previous context. */
+ if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ }
+
+ /*
+ * Set the three cursor locations to the current cursor. The
+ * underlying routines don't bother if the cursor doesn't move.
+ * This also handles line commands (e.g. Y) defaulting to the
+ * current line.
+ */
+ vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
+ vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
+
+ /*
+ * Do any required motion; v_motion sets the from MARK and the
+ * line mode flag, as well as the VM_RCM flags.
+ */
+ if (F_ISSET(vp, V_MOTION) &&
+ v_motion(sp, DOTMOTION, vp, &mapped)) {
+ if (INTERRUPTED(sp))
+ goto intr;
+ goto err;
+ }
+
+ /*
+ * If a count is set and the command is line oriented, set the
+ * to MARK here relative to the cursor/from MARK. This is for
+ * commands that take both counts and motions, i.e. "4yy" and
+ * "y%". As there's no way the command can know which the user
+ * did, we have to do it here. (There are commands that are
+ * line oriented and that take counts ("#G", "#H"), for which
+ * this calculation is either completely meaningless or wrong.
+ * Each command must validate the value for itself.
+ */
+ if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
+ vp->m_stop.lno += vp->count - 1;
+
+ /* Increment the command count. */
+ ++sp->ccnt;
+
+#if defined(DEBUG) && defined(COMLOG)
+ v_comlog(sp, vp);
+#endif
+ /* Call the function. */
+ex_continue: if (vp->kp->func(sp, vp))
+ goto err;
+gc_event:
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(gp, G_TMP_INUSE)) {
+ F_CLR(gp, G_TMP_INUSE);
+ msgq(sp, M_ERR,
+ "232|vi: temporary buffer not released");
+ }
+#endif
+ /*
+ * If we're exiting this screen, move to the next one, or, if
+ * there aren't any more, return to the main editor loop. The
+ * ordering is careful, don't discard the contents of sp until
+ * the end.
+ */
+ if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
+ if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
+ goto ret;
+ if (vs_discard(sp, &next))
+ goto ret;
+ if (next == NULL && vs_swap(sp, &next, NULL))
+ goto ret;
+ *spp = next;
+ if (screen_end(sp))
+ goto ret;
+ if (next == NULL)
+ break;
+
+ /* Switch screens, change focus. */
+ sp = next;
+ vip = VIP(sp);
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+
+ /* Don't trust the cursor. */
+ F_SET(vip, VIP_CUR_INVALID);
+
+ continue;
+ }
+
+ /*
+ * Set the dot command structure.
+ *
+ * !!!
+ * Historically, commands which used mapped keys did not
+ * set the dot command, with the exception of the text
+ * input commands.
+ */
+ if (F_ISSET(vp, V_DOT) && !mapped) {
+ *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);
+
+ /* VM flags aren't retained. */
+ F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
+ }
+
+ /*
+ * Some vi row movements are "attracted" to the last position
+ * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
+ * commands' candle. 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 VM_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 (F_ISSET(vp, VM_RCM_MASK)) {
+ case 0:
+ case VM_RCM_SET:
+ break;
+ case VM_RCM:
+ vp->m_final.cno = vs_rcm(sp,
+ vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
+ break;
+ case VM_RCM_SETLAST:
+ F_SET(vip, VIP_RCM_LAST);
+ break;
+ case VM_RCM_SETFNB:
+ vp->m_final.cno = 0;
+ /* FALLTHROUGH */
+ case VM_RCM_SETNNB:
+ if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor. */
+ sp->lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+
+ /*
+ * Set the absolute mark -- set even if a tags or similar
+ * command, since the tag may be moving to the same file.
+ */
+ if ((F_ISSET(vp, V_ABS) ||
+ F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno ||
+ F_ISSET(vp, V_ABS_C) &&
+ (sp->lno != abs.lno || sp->cno != abs.cno)) &&
+ mark_set(sp, ABSMARK1, &abs, 1))
+ goto err;
+
+ if (0) {
+err: if (v_event_flush(sp, CH_MAPPED))
+ msgq(sp, M_BERR,
+ "110|Vi command failed: mapped keys discarded");
+ }
+
+ /*
+ * Check and clear interrupts. There's an obvious race, but
+ * it's not worth fixing.
+ */
+gc_err_noflush: if (INTERRUPTED(sp)) {
+intr: CLR_INTERRUPT(sp);
+ if (v_event_flush(sp, CH_MAPPED))
+ msgq(sp, M_ERR,
+ "231|Interrupted: mapped keys discarded");
+ else
+ msgq(sp, M_ERR, "236|Interrupted");
+ }
+
+ /* If the last command switched screens, update. */
+ if (F_ISSET(sp, SC_SSWITCH)) {
+ F_CLR(sp, SC_SSWITCH);
+
+ /*
+ * If the current screen is still displayed, it will
+ * need a new status line.
+ */
+ F_SET(sp, SC_STATUS);
+
+ /* Switch screens, change focus. */
+ sp = sp->nextdisp;
+ vip = VIP(sp);
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+
+ /* Don't trust the cursor. */
+ F_SET(vip, VIP_CUR_INVALID);
+
+ /* Refresh so we can display messages. */
+ if (vs_refresh(sp, 1))
+ return (1);
+ }
+
+ /* If the last command switched files, change focus. */
+ if (F_ISSET(sp, SC_FSWITCH)) {
+ F_CLR(sp, SC_FSWITCH);
+ (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
+ }
+
+ /* If leaving vi, return to the main editor loop. */
+ if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
+ *spp = sp;
+ v_dtoh(sp);
+ break;
+ }
+ }
+ if (0)
+ret: rval = 1;
+ return (rval);
+}
+
+#define KEY(key, ec_flags) { \
+ if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK) \
+ return (gcret); \
+ if (ev.e_value == K_ESCAPE) \
+ goto esc; \
+ if (F_ISSET(&ev.e_ch, CH_MAPPED)) \
+ *mappedp = 1; \
+ key = ev.e_c; \
+}
+
+/*
+ * The O_TILDEOP option makes the ~ command take a motion instead
+ * of a straight count. This is the replacement structure we use
+ * instead of the one currently in the VIKEYS table.
+ *
+ * XXX
+ * This should probably be deleted -- it's not all that useful, and
+ * we get help messages wrong.
+ */
+VIKEYS const tmotion = {
+ v_mulcase, V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
+ "[count]~[count]motion",
+ " ~ change case to motion"
+};
+
+/*
+ * v_cmd --
+ *
+ * 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 gcret_t
+v_cmd(sp, dp, vp, ismotion, comcountp, mappedp)
+ SCR *sp;
+ VICMD *dp, *vp;
+ VICMD *ismotion; /* Previous key if getting motion component. */
+ int *comcountp, *mappedp;
+{
+ enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
+ EVENT ev;
+ VIKEYS const *kp;
+ gcret_t gcret;
+ u_int flags;
+ CHAR_T key;
+ char *s;
+
+ /*
+ * Get a key.
+ *
+ * <escape> cancels partial commands, i.e. a command where at least
+ * one non-numeric character has been entered. Otherwise, it beeps
+ * the terminal.
+ *
+ * !!!
+ * POSIX 1003.2-1992 explicitly disallows cancelling commands where
+ * all that's been entered is a number, requiring that the terminal
+ * be alerted.
+ */
+ cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
+ if ((gcret =
+ v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
+ if (gcret == GC_EVENT)
+ vp->ev = ev;
+ return (gcret);
+ }
+ if (ev.e_value == K_ESCAPE)
+ goto esc;
+ if (F_ISSET(&ev.e_ch, CH_MAPPED))
+ *mappedp = 1;
+ key = ev.e_c;
+
+ if (ismotion == NULL)
+ cpart = NOTPARTIAL;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ cpart = ISPARTIAL;
+ if (ismotion != NULL) {
+ v_emsg(sp, NULL, VIM_COMBUF);
+ return (GC_ERR);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+
+ KEY(key, EC_MAPCOMMAND);
+ }
+
+ /*
+ * Pick up optional count, where a leading 0 is not a count,
+ * it's a command.
+ */
+ if (isdigit(key) && key != '0') {
+ if (v_count(sp, key, &vp->count))
+ return (GC_ERR);
+ F_SET(vp, VC_C1SET);
+ *comcountp = 1;
+
+ KEY(key, EC_MAPCOMMAND);
+ } else
+ *comcountp = 0;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ cpart = ISPARTIAL;
+ if (F_ISSET(vp, VC_BUFFER)) {
+ msgq(sp, M_ERR, "234|Only one buffer may be specified");
+ return (GC_ERR);
+ }
+ if (ismotion != NULL) {
+ v_emsg(sp, NULL, VIM_COMBUF);
+ return (GC_ERR);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+
+ KEY(key, EC_MAPCOMMAND);
+ }
+
+ /* Check for an OOB command key. */
+ cpart = ISPARTIAL;
+ if (key > MAXVIKEY) {
+ v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
+ return (GC_ERR);
+ }
+ kp = &vikeys[vp->key = key];
+
+ /*
+ * !!!
+ * Historically, D accepted and then ignored a count. Match it.
+ */
+ if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
+ *comcountp = 0;
+ vp->count = 0;
+ F_CLR(vp, VC_C1SET);
+ }
+
+ /* Check for command aliases. */
+ if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
+ return (GC_ERR);
+
+ /* The tildeop option makes the ~ command take a motion. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+
+ vp->kp = kp;
+
+ /*
+ * Find the command. The only legal command with no underlying
+ * function is dot. It's historic practice that <escape> doesn't
+ * just erase the preceding number, it beeps the terminal as well.
+ * It's a common problem, so just beep the terminal unless verbose
+ * was set.
+ */
+ if (kp->func == NULL) {
+ if (key != '.') {
+ v_emsg(sp, KEY_NAME(sp, key),
+ ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
+ return (GC_ERR);
+ }
+
+ /* If called for a motion command, stop now. */
+ if (dp == NULL)
+ goto usage;
+
+ /*
+ * !!!
+ * 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 (GC_OK);
+ }
+
+ /* Otherwise, a repeatable command must have been executed. */
+ if (!F_ISSET(dp, VC_ISDOT)) {
+ msgq(sp, M_ERR, "208|No command to repeat");
+ return (GC_ERR);
+ }
+
+ /* 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 (GC_OK);
+ }
+
+ /* Set the flags based on the command flags. */
+ 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);
+ F_SET(vp, VC_BUFFER);
+ }
+ }
+
+ /*
+ * 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') {
+ /*
+ * Historically, half entered [[, ]] or Z commands weren't
+ * cancelled by <escape>, the terminal was beeped instead.
+ * POSIX.2-1992 probably didn't notice, and requires that
+ * they be cancelled instead of beeping. Seems fine to me.
+ *
+ * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
+ * vi meta-character, and we don't want the user to wait while
+ * we time out a possible mapping. This *appears* to match
+ * historic vi practice, but with mapping characters, you Just
+ * Never Know.
+ */
+ KEY(key, 0);
+
+ if (vp->key != key) {
+usage: if (ismotion == NULL)
+ s = kp->usage;
+ else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
+ s = tmotion.usage;
+ else
+ s = vikeys[ismotion->key].usage;
+ v_emsg(sp, s, VIM_USAGE);
+ return (GC_ERR);
+ }
+ }
+ /* Special case: 'z' command. */
+ if (vp->key == 'z') {
+ KEY(vp->character, 0);
+ if (isdigit(vp->character)) {
+ if (v_count(sp, vp->character, &vp->count2))
+ return (GC_ERR);
+ F_SET(vp, VC_C2SET);
+ KEY(vp->character, 0);
+ }
+ }
+
+ /*
+ * Commands that have motion components can be doubled to
+ * imply the current line.
+ */
+ if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
+ msgq(sp, M_ERR, "210|%s may not be used as a motion command",
+ KEY_NAME(sp, key));
+ return (GC_ERR);
+ }
+
+ /* Required character. */
+ if (LF_ISSET(V_CHAR))
+ KEY(vp->character, 0);
+
+ /* Get any associated cursor word. */
+ if (F_ISSET(kp, V_KEYW) && v_keyword(sp))
+ return (GC_ERR);
+
+ return (GC_OK);
+
+esc: switch (cpart) {
+ case COMMANDMODE:
+ msgq(sp, M_BERR, "211|Already in command mode");
+ return (GC_ERR_NOFLUSH);
+ case ISPARTIAL:
+ break;
+ case NOTPARTIAL:
+ (void)sp->gp->scr_bell(sp);
+ break;
+ }
+ return (GC_ERR);
+}
+
+/*
+ * v_motion --
+ *
+ * Get resulting motion mark.
+ */
+static int
+v_motion(sp, dm, vp, mappedp)
+ SCR *sp;
+ VICMD *dm, *vp;
+ int *mappedp;
+{
+ VICMD motion;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int tilde_reset, notused;
+
+ /*
+ * If '.' command, use the dot motion, else get the motion command.
+ * Clear any line motion flags, the subsequent motion isn't always
+ * the same, i.e. "/aaa" may or may not be a line motion.
+ */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ motion = *dm;
+ F_SET(&motion, VC_ISDOT);
+ F_CLR(&motion, VM_COMMASK);
+ } else {
+ memset(&motion, 0, sizeof(VICMD));
+ if (v_cmd(sp, NULL, &motion, vp, &notused, mappedp) != GC_OK)
+ 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, VM_LDOUBLE | VM_LMODE);
+
+ /* Set the origin of the command. */
+ vp->m_start.lno = sp->lno;
+ vp->m_start.cno = 0;
+
+ /*
+ * Set the end of the command.
+ *
+ * If the current line is missing, i.e. the file is empty,
+ * historic vi permitted a "cc" or "!!" command to insert
+ * text.
+ */
+ vp->m_stop.lno = sp->lno + motion.count - 1;
+ if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
+ if (vp->m_stop.lno != 1 ||
+ vp->key != 'c' && vp->key != '!') {
+ v_emsg(sp, NULL, VIM_EMPTY);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ } else
+ vp->m_stop.cno = len ? len - 1 : 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.
+ */
+ motion.rkp = vp->kp;
+
+ /*
+ * XXX
+ * Use yank instead of creating a new motion command, it's a
+ * lot easier for now.
+ */
+ if (vp->kp == &tmotion) {
+ tilde_reset = 1;
+ vp->kp = &vikeys['y'];
+ } else
+ tilde_reset = 0;
+
+ /*
+ * Copy the key flags into the local structure, except for the
+ * RCM flags -- the motion command will set the RCM flags in
+ * the vp structure if necessary. This means that the motion
+ * command is expected to determine where the cursor ends up!
+ * However, we save off the current RCM mask and restore it if
+ * it no RCM flags are set by the motion command, with a small
+ * modification.
+ *
+ * We replace the VM_RCM_SET flag with the VM_RCM flag. This
+ * is so that cursor movement doesn't set the relative position
+ * unless the motion command explicitly specified it. This
+ * appears to match historic practice, but I've never been able
+ * to develop a hard-and-fast rule.
+ */
+ flags = F_ISSET(vp, VM_RCM_MASK);
+ if (LF_ISSET(VM_RCM_SET)) {
+ LF_SET(VM_RCM);
+ LF_CLR(VM_RCM_SET);
+ }
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
+
+ /*
+ * Set the three cursor locations to the current cursor. 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.
+ */
+ motion.m_final.lno =
+ motion.m_stop.lno = motion.m_start.lno = sp->lno;
+ motion.m_final.cno =
+ motion.m_stop.cno = motion.m_start.cno = sp->cno;
+
+ /* Run the function. */
+ if ((motion.kp->func)(sp, &motion))
+ return (1);
+
+ /*
+ * If the current line is missing, i.e. the file is empty,
+ * historic vi allowed "c<motion>" or "!<motion>" to insert
+ * text. Otherwise fail -- most motion commands will have
+ * already failed, but some, e.g. G, succeed in empty files.
+ */
+ if (!db_exist(sp, vp->m_stop.lno)) {
+ if (vp->m_stop.lno != 1 ||
+ vp->key != 'c' && vp->key != '!') {
+ v_emsg(sp, NULL, VIM_EMPTY);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ }
+
+ /*
+ * XXX
+ * See above.
+ */
+ if (tilde_reset)
+ vp->kp = &tmotion;
+
+ /*
+ * Copy cut buffer, line mode and cursor position information
+ * from the motion command structure, i.e. anything that the
+ * motion command can set for us. The commands can flag the
+ * movement as a line motion (see v_sentence) as well as set
+ * the VM_RCM_* flags explicitly.
+ */
+ F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
+
+ /*
+ * If the motion command set no relative motion flags, use
+ * the (slightly) modified previous values.
+ */
+ if (!F_ISSET(vp, VM_RCM_MASK))
+ F_SET(vp, flags);
+
+ /*
+ * Commands can change behaviors based on the motion command
+ * used, for example, the ! command repeated the last bang
+ * command if N or n was used as the motion.
+ */
+ vp->rkp = motion.kp;
+
+ /*
+ * Motion commands can reset all of the cursor information.
+ * If the motion is in the reverse direction, switch the
+ * from and to MARK's so that it's in a forward direction.
+ * Motions are from the from MARK to the to MARK (inclusive).
+ */
+ if (motion.m_start.lno > motion.m_stop.lno ||
+ motion.m_start.lno == motion.m_stop.lno &&
+ motion.m_start.cno > motion.m_stop.cno) {
+ vp->m_start = motion.m_stop;
+ vp->m_stop = motion.m_start;
+ } else {
+ vp->m_start = motion.m_start;
+ vp->m_stop = motion.m_stop;
+ }
+ vp->m_final = motion.m_final;
+ }
+
+ /*
+ * 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;
+ }
+ return (0);
+}
+
+/*
+ * v_init --
+ * Initialize the vi screen.
+ */
+static int
+v_init(sp)
+ SCR *sp;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /* Switch into vi. */
+ if (gp->scr_screen(sp, SC_VI))
+ return (1);
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
+
+ F_CLR(sp, SC_EX | SC_SCR_EX);
+ F_SET(sp, SC_VI);
+
+ /*
+ * Initialize screen values.
+ *
+ * Small windows: see vs_refresh(), section 6a.
+ *
+ * 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 = vip->srows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
+ if (sp->rows != 1) {
+ if (sp->t_rows > sp->rows - 1) {
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+ msgq(sp, M_INFO,
+ "214|Windows option value is too large, max is %u",
+ sp->t_rows);
+ }
+ sp->t_maxrows = sp->rows - 1;
+ } else
+ sp->t_maxrows = 1;
+ sp->woff = 0;
+
+ /* Create a screen map. */
+ CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ TMAP = HMAP + (sp->t_rows - 1);
+ HMAP->lno = sp->lno;
+ HMAP->coff = 0;
+ HMAP->soff = 1;
+
+ /*
+ * Fill the screen map from scratch -- try and center the line. That
+ * way if we're starting with a file we've seen before, we'll put the
+ * line in the middle, otherwise, it won't work and we'll end up with
+ * the line at the top.
+ */
+ F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
+
+ /* Invalidate the cursor. */
+ F_SET(vip, VIP_CUR_INVALID);
+
+ /* Paint the screen image from scratch. */
+ F_SET(vip, VIP_N_EX_PAINT);
+
+ return (0);
+}
+
+/*
+ * v_dtoh --
+ * Move all but the current screen to the hidden queue.
+ */
+static void
+v_dtoh(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SCR *tsp;
+ int hidden;
+
+ /* Move all screens to the hidden queue, tossing screen maps. */
+ for (hidden = 0, gp = sp->gp;
+ (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) {
+ if (_HMAP(tsp) != NULL) {
+ free(_HMAP(tsp));
+ _HMAP(tsp) = NULL;
+ }
+ CIRCLEQ_REMOVE(&gp->dq, tsp, q);
+ CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q);
+ }
+
+ /* Move current screen back to the display queue. */
+ CIRCLEQ_REMOVE(&gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q);
+
+ /*
+ * XXX
+ * Don't bother internationalizing this message, it's going to
+ * go away as soon as we have one-line screens. --TK
+ */
+ if (hidden > 1)
+ msgq(sp, M_INFO,
+ "%d screens backgrounded; use :display to list them",
+ hidden - 1);
+}
+
+/*
+ * v_keyword --
+ * Get the word (or non-word) the cursor is on.
+ */
+static int
+v_keyword(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+ size_t beg, end, len;
+ int moved, state;
+ char *p;
+
+ if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
+ return (1);
+
+ /*
+ * !!!
+ * Historically, tag commands skipped over any leading whitespace
+ * characters. Make this true in general when using cursor words.
+ * If movement, getting a cursor word implies moving the cursor to
+ * its beginning. Refresh now.
+ *
+ * !!!
+ * Find the beginning/end of the keyword. Keywords are currently
+ * 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 consistency, we make cursor word searches
+ * follow the same rule.
+ */
+ for (moved = 0,
+ beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg);
+ if (beg >= len) {
+ msgq(sp, M_BERR, "212|Cursor not in a word");
+ return (1);
+ }
+ if (moved) {
+ sp->cno = beg;
+ (void)vs_refresh(sp, 0);
+ }
+
+ /* Find the end of the word. */
+ for (state = inword(p[beg]),
+ end = beg; ++end < len && state == inword(p[end]););
+
+ vip = VIP(sp);
+ len = (end - beg);
+ BINC_RET(sp, vip->keyw, vip->klen, len);
+ memmove(vip->keyw, p + beg, len);
+ vip->keyw[len] = '\0'; /* XXX */
+ return (0);
+}
+
+/*
+ * v_alias --
+ * Check for a command alias.
+ */
+static VIKEYS const *
+v_alias(sp, vp, kp)
+ SCR *sp;
+ VICMD *vp;
+ VIKEYS const *kp;
+{
+ CHAR_T push;
+
+ switch (vp->key) {
+ case 'C': /* C -> c$ */
+ push = '$';
+ vp->key = 'c';
+ break;
+ case 'D': /* D -> d$ */
+ push = '$';
+ vp->key = 'd';
+ break;
+ case 'S': /* S -> c_ */
+ push = '_';
+ vp->key = 'c';
+ break;
+ case 'Y': /* Y -> y_ */
+ push = '_';
+ vp->key = 'y';
+ break;
+ default:
+ return (kp);
+ }
+ return (v_event_push(sp,
+ NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]);
+}
+
+/*
+ * v_count --
+ * Return the next count.
+ */
+static int
+v_count(sp, fkey, countp)
+ SCR *sp;
+ ARG_CHAR_T fkey;
+ u_long *countp;
+{
+ EVENT ev;
+ u_long count, tc;
+
+ ev.e_c = fkey;
+ count = tc = 0;
+ do {
+ /*
+ * XXX
+ * Assume that overflow results in a smaller number.
+ */
+ tc = count * 10 + ev.e_c - '0';
+ if (count > tc) {
+ /* Toss to the next non-digit. */
+ do {
+ if (v_key(sp, 0, &ev,
+ EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
+ return (1);
+ } while (isdigit(ev.e_c));
+ msgq(sp, M_ERR,
+ "235|Number larger than %lu", ULONG_MAX);
+ return (1);
+ }
+ count = tc;
+ if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
+ return (1);
+ } while (isdigit(ev.e_c));
+ *countp = count;
+ return (0);
+}
+
+/*
+ * v_key --
+ * Return the next event.
+ */
+static gcret_t
+v_key(sp, command_events, evp, ec_flags)
+ SCR *sp;
+ int command_events;
+ EVENT *evp;
+ u_int32_t ec_flags;
+{
+ u_int32_t quote;
+
+ for (quote = 0;;) {
+ if (v_event_get(sp, evp, 0, ec_flags | quote))
+ return (GC_FATAL);
+ quote = 0;
+
+ switch (evp->e_event) {
+ case E_CHARACTER:
+ /*
+ * !!!
+ * Historically, ^V was ignored in the command stream,
+ * although it had a useful side-effect of interrupting
+ * mappings. Adding a quoting bit to the call probably
+ * extends historic practice, but it feels right.
+ */
+ if (evp->e_value == K_VLNEXT) {
+ quote = EC_QUOTED;
+ break;
+ }
+ return (GC_OK);
+ case E_ERR:
+ case E_EOF:
+ return (GC_FATAL);
+ case E_INTERRUPT:
+ /*
+ * !!!
+ * Historically, vi beeped on command level interrupts.
+ *
+ * Historically, vi exited to ex mode if no file was
+ * named on the command line, and two interrupts were
+ * generated in a row. (Just figured you might want
+ * to know that.)
+ */
+ (void)sp->gp->scr_bell(sp);
+ return (GC_INTERRUPT);
+ case E_REPAINT:
+ if (vs_repaint(sp, evp))
+ return (GC_FATAL);
+ break;
+ case E_WRESIZE:
+ return (GC_ERR);
+ case E_QUIT:
+ case E_WRITE:
+ if (command_events)
+ return (GC_EVENT);
+ /* FALLTHROUGH */
+ default:
+ v_event_err(sp, evp);
+ return (GC_ERR);
+ }
+ }
+ /* NOTREACHED */
+}
+
+#if defined(DEBUG) && defined(COMLOG)
+/*
+ * v_comlog --
+ * Log the contents of the command structure.
+ */
+static void
+v_comlog(sp, vp)
+ SCR *sp;
+ VICMD *vp;
+{
+ TRACE(sp, "vcmd: %c", vp->key);
+ if (F_ISSET(vp, VC_BUFFER))
+ TRACE(sp, " buffer: %c", vp->buffer);
+ if (F_ISSET(vp, VC_C1SET))
+ TRACE(sp, " c1: %lu", vp->count);
+ if (F_ISSET(vp, VC_C2SET))
+ TRACE(sp, " c2: %lu", vp->count2);
+ TRACE(sp, " flags: 0x%x\n", vp->flags);
+}
+#endif
diff --git a/contrib/nvi/vi/vi.h b/contrib/nvi/vi/vi.h
new file mode 100644
index 000000000000..bede3a651817
--- /dev/null
+++ b/contrib/nvi/vi/vi.h
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ *
+ * @(#)vi.h 10.19 (Berkeley) 6/30/96
+ */
+
+/* Definition of a vi "word". */
+#define inword(ch) (isalnum(ch) || (ch) == '_')
+
+typedef struct _vikeys VIKEYS;
+
+/* Structure passed around to functions implementing vi commands. */
+typedef struct _vicmd {
+ CHAR_T key; /* Command key. */
+ CHAR_T buffer; /* Buffer. */
+ CHAR_T character; /* Character. */
+ u_long count; /* Count. */
+ u_long count2; /* Second count (only used by z). */
+ EVENT ev; /* Associated event. */
+
+#define ISCMD(p, key) ((p) == &vikeys[key])
+ VIKEYS const *kp; /* Command/Motion VIKEYS entry. */
+#define ISMOTION(vp) (vp->rkp != NULL && F_ISSET(vp->rkp, V_MOTION))
+ VIKEYS const *rkp; /* Related C/M VIKEYS entry. */
+
+ /*
+ * 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, while "d/b" 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. The special cases are handled
+ * by setting flags per command so that the underlying command and
+ * motion routines know what's really going on.
+ *
+ * The VM_* flags are set in the vikeys array and by the underlying
+ * functions (motion component or command) as well. For this reason,
+ * the flags in the VICMD and VIKEYS structures live in the same name
+ * space.
+ */
+#define VM_CMDFAILED 0x00000001 /* Command failed. */
+#define VM_CUTREQ 0x00000002 /* Always cut into numeric buffers. */
+#define VM_LDOUBLE 0x00000004 /* Doubled command for line mode. */
+#define VM_LMODE 0x00000008 /* Motion is line oriented. */
+#define VM_COMMASK 0x0000000f /* Mask for VM flags. */
+
+ /*
+ * The VM_RCM_* flags are single usage, i.e. if you set one, you have
+ * to clear the others.
+ */
+#define VM_RCM 0x00000010 /* Use relative cursor movment (RCM). */
+#define VM_RCM_SET 0x00000020 /* RCM: set to current position. */
+#define VM_RCM_SETFNB 0x00000040 /* RCM: set to first non-blank (FNB). */
+#define VM_RCM_SETLAST 0x00000080 /* RCM: set to last character. */
+#define VM_RCM_SETNNB 0x00000100 /* RCM: set to next non-blank. */
+#define VM_RCM_MASK 0x000001f0 /* Mask for RCM flags. */
+
+ /* Flags for the underlying function. */
+#define VC_BUFFER 0x00000200 /* The buffer was set. */
+#define VC_C1RESET 0x00000400 /* Reset C1SET flag for dot commands. */
+#define VC_C1SET 0x00000800 /* Count 1 was set. */
+#define VC_C2SET 0x00001000 /* Count 2 was set. */
+#define VC_ISDOT 0x00002000 /* Command was the dot command. */
+ u_int32_t flags;
+
+ /*
+ * There are four cursor locations that we worry about: the initial
+ * cursor position, the start of the range, the end of the range,
+ * and the final cursor position. The initial cursor position and
+ * the start of the range are both m_start, and are always the same.
+ * All locations are initialized to the starting cursor position by
+ * the main vi routines, and the underlying functions depend on this.
+ *
+ * Commands that can be motion components set the end of the range
+ * cursor position, m_stop. All commands must set the ending cursor
+ * position, m_final. The reason that m_stop isn't the same as m_final
+ * is that there are situations where the final position of the cursor
+ * is outside of the cut/delete range (e.g. 'd[[' from the first column
+ * of a line). The final cursor position often varies based on the
+ * direction of the movement, as well as the command. The only special
+ * case that the delete code handles is that it will make adjustments
+ * if the final cursor position is deleted.
+ *
+ * The reason for all of this is that the historic vi semantics were
+ * defined command-by-command. Every function has to roll its own
+ * starting and stopping positions, and adjust them if it's being used
+ * as a motion component. The general rules are as follows:
+ *
+ * 1: If not a motion component, the final cursor is at the end
+ * of the range.
+ * 2: If moving backward in the file, delete and yank move the
+ * final cursor to the end of the range.
+ * 3: If moving forward in the file, delete and yank leave the
+ * final cursor at the start of the range.
+ *
+ * Usually, if moving backward in the file and it's a motion component,
+ * the starting cursor is decremented by a single character (or, in a
+ * few cases, to the end of the previous line) so that the starting
+ * cursor character isn't cut or deleted. No cursor adjustment is
+ * needed for moving forward, because the cut/delete routines handle
+ * m_stop inclusively, i.e. the last character in the range is cut or
+ * deleted. This makes cutting to the EOF/EOL reasonable.
+ *
+ * The 'c', '<', '>', and '!' commands are special cases. We ignore
+ * the final cursor position for all of them: for 'c', the text input
+ * routines set the cursor to the last character inserted; for '<',
+ * '>' and '!', the underlying ex commands that do the operation will
+ * set the cursor for us, usually to something related to the first
+ * <nonblank>.
+ */
+ MARK m_start; /* mark: initial cursor, range start. */
+ MARK m_stop; /* mark: range end. */
+ MARK m_final; /* mark: final cursor position. */
+} VICMD;
+
+/* Vi command table structure. */
+struct _vikeys { /* Underlying function. */
+ int (*func) __P((SCR *, VICMD *));
+#define V_ABS 0x00004000 /* Absolute movement, set '' mark. */
+#define V_ABS_C 0x00008000 /* V_ABS: if the line/column changed. */
+#define V_ABS_L 0x00010000 /* V_ABS: if the line changed. */
+#define V_CHAR 0x00020000 /* Character (required, trailing). */
+#define V_CNT 0x00040000 /* Count (optional, leading). */
+#define V_DOT 0x00080000 /* On success, sets dot command. */
+#define V_KEYW 0x00100000 /* Cursor referenced word. */
+#define V_MOTION 0x00200000 /* Motion (required, trailing). */
+#define V_MOVE 0x00400000 /* Command defines movement. */
+#define V_OBUF 0x00800000 /* Buffer (optional, leading). */
+#define V_RBUF 0x01000000 /* Buffer (required, trailing). */
+#define V_SECURE 0x02000000 /* Permission denied if O_SECURE set. */
+ u_int32_t flags;
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+};
+#define MAXVIKEY 126 /* List of vi commands. */
+extern VIKEYS const vikeys[MAXVIKEY + 1];
+extern VIKEYS const tmotion; /* XXX Hacked ~ command. */
+
+/* Character stream structure, prototypes. */
+typedef struct _vcs {
+ recno_t cs_lno; /* Line. */
+ size_t cs_cno; /* Column. */
+ CHAR_T *cs_bp; /* Buffer. */
+ size_t cs_len; /* Length. */
+ CHAR_T 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 *, VCS *));
+int cs_fblank __P((SCR *, VCS *));
+int cs_fspace __P((SCR *, VCS *));
+int cs_init __P((SCR *, VCS *));
+int cs_next __P((SCR *, VCS *));
+int cs_prev __P((SCR *, VCS *));
+
+/*
+ * We use a single "window" for each set of vi screens. 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.
+ *
+ * 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.
+ *
+ * The SMAP always has an entry for each line of the physical screen, plus a
+ * slot for the colon command line, so there is room to add any screen into
+ * another one at screen exit.
+ *
+ * Lno is the line number. If doing the historic vi long line folding, 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. In the case of
+ * long lines, the screen map will tend to be staggered, e.g., 1:1, 1:2, 1:3,
+ * 2:1, 3:1, etc. If doing left-right scrolling, the off field is the screen
+ * column offset into the lines, and can take on any value, as it's adjusted
+ * by the user set value O_SIDESCROLL.
+ */
+typedef struct _smap {
+ recno_t lno; /* 1-N: Physical file line number. */
+ size_t coff; /* 0-N: Column offset in the line. */
+ size_t soff; /* 1-N: Screen offset in the line. */
+
+ /* vs_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_int8_t c_scoff; /* 0-N: offset into the first character. */
+ u_int8_t c_eclen; /* 1-N: columns from the last character. */
+ u_int8_t 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)
+
+ /* Character search information. */
+typedef enum { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH } cdir_t;
+
+typedef enum { AB_NOTSET, AB_NOTWORD, AB_INWORD } abb_t;
+typedef enum { Q_NOTSET, Q_BNEXT, Q_BTHIS, Q_VNEXT, Q_VTHIS } quote_t;
+
+/* Vi private, per-screen memory. */
+typedef struct _vi_private {
+ VICMD cmd; /* Current command, motion. */
+ VICMD motion;
+
+ /*
+ * !!!
+ * The saved command structure can be modified by the underlying
+ * vi functions, see v_Put() and v_put().
+ */
+ VICMD sdot; /* Saved dot, motion command. */
+ VICMD sdotmotion;
+
+ CHAR_T *keyw; /* Keyword buffer. */
+ size_t klen; /* Keyword length. */
+ size_t keywlen; /* Keyword buffer length. */
+
+ CHAR_T rlast; /* Last 'r' replacement character. */
+ e_key_t rvalue; /* Value of last replacement character. */
+
+ EVENT *rep; /* Input replay buffer. */
+ size_t rep_len; /* Input replay buffer length. */
+ size_t rep_cnt; /* Input replay buffer characters. */
+
+ mtype_t mtype; /* Last displayed message type. */
+ size_t linecount; /* 1-N: Output overwrite count. */
+ size_t lcontinue; /* 1-N: Output line continue value. */
+ size_t totalcount; /* 1-N: Output overwrite count. */
+
+ /* Busy state. */
+ int busy_ref; /* Busy reference count. */
+ int busy_ch; /* Busy character. */
+ size_t busy_fx; /* Busy character x coordinate. */
+ size_t busy_oldy; /* Saved y coordinate. */
+ size_t busy_oldx; /* Saved x coordinate. */
+ struct timeval busy_tv; /* Busy timer. */
+
+ char *ps; /* Paragraph plus section list. */
+
+ u_long u_ccnt; /* Undo command count. */
+
+ CHAR_T lastckey; /* Last search character. */
+ cdir_t csearchdir; /* Character search direction. */
+
+ SMAP *h_smap; /* First slot of the line map. */
+ SMAP *t_smap; /* Last slot of the line map. */
+
+ /*
+ * One extra slot is always allocated for the map so that we can use
+ * it to do vi :colon command input; see v_tcmd().
+ */
+ recno_t sv_tm_lno; /* tcmd: saved TMAP lno field. */
+ size_t sv_tm_coff; /* tcmd: saved TMAP coff field. */
+ size_t sv_tm_soff; /* tcmd: saved TMAP soff field. */
+ size_t sv_t_maxrows; /* tcmd: saved t_maxrows. */
+ size_t sv_t_minrows; /* tcmd: saved t_minrows. */
+ size_t sv_t_rows; /* tcmd: saved t_rows. */
+#define SIZE_HMAP(sp) (VIP(sp)->srows + 1)
+
+ /*
+ * Macros to get to the head/tail of the smap. If the screen only has
+ * one line, HMAP can be equal to TMAP, so the code has to understand
+ * the off-by-one errors that can result. If stepping through an SMAP
+ * and operating on each entry, use sp->t_rows as the count of slots,
+ * don't use a loop that compares <= TMAP.
+ */
+#define _HMAP(sp) (VIP(sp)->h_smap)
+#define HMAP _HMAP(sp)
+#define _TMAP(sp) (VIP(sp)->t_smap)
+#define TMAP _TMAP(sp)
+
+ recno_t ss_lno; /* 1-N: vi_opt_screens cached line number. */
+ size_t ss_screens; /* vi_opt_screens cached return value. */
+#define VI_SCR_CFLUSH(vip) vip->ss_lno = OOBLNO
+
+ size_t srows; /* 1-N: rows in the terminal/window. */
+ 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. */
+ SMAP *sc_smap; /* SMAP entry where sc_col occurs. */
+
+#define VIP_CUR_INVALID 0x0001 /* Cursor position is unknown. */
+#define VIP_DIVIDER 0x0002 /* Divider line was displayed. */
+#define VIP_N_EX_PAINT 0x0004 /* Clear and repaint when ex finishes. */
+#define VIP_N_EX_REDRAW 0x0008 /* Schedule SC_SCR_REDRAW when ex finishes. */
+#define VIP_N_REFRESH 0x0010 /* Repaint (from SMAP) on the next refresh. */
+#define VIP_N_RENUMBER 0x0020 /* Renumber screen on the next refresh. */
+#define VIP_RCM_LAST 0x0040 /* Cursor drawn to the last column. */
+#define VIP_S_MODELINE 0x0080 /* Skip next modeline refresh. */
+#define VIP_S_REFRESH 0x0100 /* Skip next refresh. */
+ u_int16_t flags;
+} VI_PRIVATE;
+
+/* Vi private area. */
+#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private))
+
+#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */
+#define O_NUMBER_LENGTH 8
+#define SCREEN_COLS(sp) /* Screen columns. */ \
+ ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols))
+
+/*
+ * LASTLINE is the zero-based, last line in the screen. Note that it is correct
+ * regardless of the changes in the screen to permit text input on the last line
+ * of the screen, or the existence of small screens.
+ */
+#define LASTLINE(sp) \
+ ((sp)->t_maxrows < (sp)->rows ? (sp)->t_maxrows : (sp)->rows - 1)
+
+/*
+ * Small screen (see vs_refresh.c, section 6a) and one-line screen test.
+ * Note, both cannot be true for the same screen.
+ */
+#define IS_SMALL(sp) ((sp)->t_minrows != (sp)->t_maxrows)
+#define IS_ONELINE(sp) ((sp)->rows == 1)
+
+#define HALFTEXT(sp) /* Half text. */ \
+ ((sp)->t_rows == 1 ? 1 : (sp)->t_rows / 2)
+#define HALFSCREEN(sp) /* Half text screen. */ \
+ ((sp)->t_maxrows == 1 ? 1 : (sp)->t_maxrows / 2)
+
+/*
+ * Next tab offset.
+ *
+ * !!!
+ * There are problems with how the historical vi handled tabs. For example,
+ * by doing "set ts=3" and building lines that fold, you can get it to step
+ * through tabs as if they were spaces and move inserted characters to new
+ * positions when <esc> is entered. I believe that nvi does tabs correctly,
+ * but there are some historical incompatibilities.
+ */
+#define TAB_OFF(c) COL_OFF((c), O_VAL(sp, O_TABSTOP))
+
+/* If more than one screen being shown. */
+#define IS_SPLIT(sp) \
+ ((sp)->q.cqe_next != (void *)&(sp)->gp->dq || \
+ (sp)->q.cqe_prev != (void *)&(sp)->gp->dq)
+
+/* Screen adjustment operations. */
+typedef enum { A_DECREASE, A_INCREASE, A_SET } adj_t;
+
+/* Screen position operations. */
+typedef enum { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP } pos_t;
+
+/* Scrolling operations. */
+typedef enum {
+ CNTRL_B, CNTRL_D, CNTRL_E, CNTRL_F,
+ CNTRL_U, CNTRL_Y, Z_CARAT, Z_PLUS
+} scroll_t;
+
+/* Vi common error messages. */
+typedef enum {
+ VIM_COMBUF, VIM_EMPTY, VIM_EOF, VIM_EOL,
+ VIM_NOCOM, VIM_NOCOM_B, VIM_USAGE, VIM_WRESIZE
+} vim_t;
+
+#include "vi_extern.h"
diff --git a/contrib/nvi/vi/vs_line.c b/contrib/nvi/vi/vs_line.c
new file mode 100644
index 000000000000..b439de925ce1
--- /dev/null
+++ b/contrib/nvi/vi/vs_line.c
@@ -0,0 +1,514 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_line.c 10.19 (Berkeley) 9/26/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+#ifdef VISIBLE_TAB_CHARS
+#define TABCH '-'
+#else
+#define TABCH ' '
+#endif
+
+/*
+ * vs_line --
+ * Update one line on the screen.
+ *
+ * PUBLIC: int vs_line __P((SCR *, SMAP *, size_t *, size_t *));
+ */
+int
+vs_line(sp, smp, yp, xp)
+ SCR *sp;
+ SMAP *smp;
+ size_t *xp, *yp;
+{
+ CHAR_T *kp;
+ GS *gp;
+ SMAP *tsmp;
+ size_t chlen, cno_cnt, cols_per_screen, len, nlen;
+ size_t offset_in_char, offset_in_line, oldx, oldy;
+ size_t scno, skip_cols, skip_screens;
+ int ch, dne, is_cached, is_partial, is_tab;
+ int list_tab, list_dollar;
+ char *p, *cbp, *ecbp, cbuf[128];
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "vs_line: row %u: line: %u off: %u\n",
+ smp - HMAP, smp->lno, smp->off);
+#endif
+ /*
+ * If ex modifies the screen after ex output is already on the screen,
+ * don't touch it -- we'll get scrolling wrong, at best.
+ */
+ if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1)
+ return (0);
+ if (F_ISSET(sp, SC_SCR_EXWROTE) && smp - HMAP != LASTLINE(sp))
+ return (0);
+
+ /*
+ * 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 vs_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.
+ */
+ gp = sp->gp;
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+ (void)gp->scr_move(sp, smp - HMAP, 0);
+
+ /* Get the line. */
+ dne = db_get(sp, smp->lno, 0, &p, &len);
+
+ /*
+ * Special case if we're printing the info/mode line. Skip printing
+ * the leading number, as well as other minor setup. 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 columns for this screen.
+ * Set the number of chars or screens to skip until a character is to
+ * be displayed.
+ */
+ cols_per_screen = sp->cols;
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ skip_screens = 0;
+ skip_cols = smp->coff;
+ } else {
+ skip_screens = smp->soff - 1;
+ skip_cols = skip_screens * cols_per_screen;
+ }
+
+ list_tab = O_ISSET(sp, O_LIST);
+ if (F_ISSET(sp, SC_TINPUT_INFO))
+ list_dollar = 0;
+ else {
+ list_dollar = list_tab;
+
+ /*
+ * If O_NUMBER is set, the line doesn't exist and it's line
+ * number 1, i.e., an empty file, display the line number.
+ *
+ * If O_NUMBER is set, the line exists and the first character
+ * on the screen is the first character in the line, display
+ * the line number.
+ *
+ * !!!
+ * If O_NUMBER set, decrement the number of columns in the
+ * first screen. DO NOT CHANGE THIS -- IT'S RIGHT! The
+ * rest of the code expects this to reflect the number of
+ * columns in the first screen, regardless of the number of
+ * columns we're going to skip.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ cols_per_screen -= O_NUMBER_LENGTH;
+ if ((!dne || smp->lno == 1) && skip_cols == 0) {
+ nlen = snprintf(cbuf,
+ sizeof(cbuf), O_NUMBER_FMT, smp->lno);
+ (void)gp->scr_addstr(sp, cbuf, nlen);
+ }
+ }
+ }
+
+ /*
+ * Special case non-existent lines and the first line of an empty
+ * file. In both cases, the cursor position is 0, but corrected
+ * as necessary for the O_NUMBER field, if it was displayed.
+ */
+ if (dne || 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 ret1;
+
+ /* Set line cache information. */
+ smp->c_sboff = smp->c_eboff = 0;
+ smp->c_scoff = smp->c_eclen = 0;
+
+ /*
+ * Lots of special cases for empty lines, but they only apply
+ * if we're displaying the first screen of the line.
+ */
+ if (skip_cols == 0)
+ if (dne) {
+ if (smp->lno == 1) {
+ if (list_dollar) {
+ ch = '$';
+ goto empty;
+ }
+ } else {
+ ch = '~';
+ goto empty;
+ }
+ } else
+ if (list_dollar) {
+ ch = '$';
+empty: (void)gp->scr_addstr(sp,
+ KEY_NAME(sp, ch), KEY_LEN(sp, ch));
+ }
+
+ (void)gp->scr_clrtoeol(sp);
+ (void)gp->scr_move(sp, oldy, oldx);
+ return (0);
+ }
+
+ /*
+ * If we just wrote this or a previous line, 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 direction, 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.
+ *
+ * Test for the first screen of the line.
+ */
+ if (skip_cols == 0) {
+ smp->c_sboff = offset_in_line = 0;
+ smp->c_scoff = offset_in_char = 0;
+ p = &p[offset_in_line];
+ goto display;
+ }
+
+ /* Test to see if we've seen this exact line before. */
+ if (is_cached) {
+ offset_in_line = smp->c_sboff;
+ offset_in_char = smp->c_scoff;
+ p = &p[offset_in_line];
+
+ /* Set cols_per_screen to 2nd and later line length. */
+ if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
+ cols_per_screen = sp->cols;
+ goto display;
+ }
+
+ /* Test to see if we saw an earlier part of this line before. */
+ 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];
+
+ /* Set cols_per_screen to 2nd and later line length. */
+ if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
+ cols_per_screen = sp->cols;
+ goto display;
+ }
+
+ scno = 0;
+ offset_in_line = 0;
+ offset_in_char = 0;
+
+ /* Do it the hard way, for leftright scrolling screens. */
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ for (; offset_in_line < len; ++offset_in_line) {
+ chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ?
+ TAB_OFF(scno) : KEY_LEN(sp, ch);
+ if ((scno += chlen) >= skip_cols)
+ break;
+ }
+
+ /* Set cols_per_screen to 2nd and later line length. */
+ cols_per_screen = sp->cols;
+
+ /* Put starting info for this line in the cache. */
+ if (scno != skip_cols) {
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff =
+ offset_in_char = chlen - (scno - skip_cols);
+ --p;
+ } else {
+ smp->c_sboff = ++offset_in_line;
+ smp->c_scoff = 0;
+ }
+ }
+
+ /* Do it the hard way, for historic line-folding screens. */
+ else {
+ for (; offset_in_line < len; ++offset_in_line) {
+ chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ?
+ TAB_OFF(scno) : KEY_LEN(sp, ch);
+ if ((scno += chlen) < cols_per_screen)
+ continue;
+ scno -= cols_per_screen;
+
+ /* Set cols_per_screen to 2nd and later line length. */
+ cols_per_screen = sp->cols;
+
+ /*
+ * If crossed the last skipped screen boundary, start
+ * displaying the characters.
+ */
+ if (--skip_screens == 0)
+ break;
+ }
+
+ /* Put starting info for this line in the cache. */
+ if (scno != 0) {
+ 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;
+ }
+ }
+
+display:
+ /*
+ * Set the number of characters to skip before reaching the cursor
+ * character. Offset by 1 and use 0 as a flag value. Vs_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 ret1;
+ } else
+ cno_cnt = (sp->cno - offset_in_line) + 1;
+
+ /* This is the loop that actually displays characters. */
+ ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
+ for (is_partial = 0, scno = 0;
+ offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
+ if ((ch = *(u_char *)p++) == '\t' && !list_tab) {
+ scno += chlen = TAB_OFF(scno) - offset_in_char;
+ is_tab = 1;
+ } else {
+ scno += chlen = KEY_LEN(sp, ch) - 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) {
+ if (is_tab == 1) {
+ chlen -= scno - cols_per_screen;
+ smp->c_ecsize = smp->c_eclen = chlen;
+ scno = cols_per_screen;
+ } else {
+ smp->c_ecsize = chlen;
+ chlen -= scno - cols_per_screen;
+ smp->c_eclen = chlen;
+
+ if (scno > cols_per_screen)
+ is_partial = 1;
+ }
+ smp->c_eboff = offset_in_line;
+
+ /* 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, SC_TINPUT) || !is_partial)) {
+ *yp = smp - HMAP;
+ if (F_ISSET(sp, SC_TINPUT))
+ *xp = scno - chlen;
+ else
+ *xp = scno - 1;
+ if (O_ISSET(sp, O_NUMBER) &&
+ !F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0)
+ *xp += O_NUMBER_LENGTH;
+
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret1;
+ }
+
+ /* If the line is on the screen, don't display anything. */
+ if (is_cached)
+ continue;
+
+#define FLUSH { \
+ *cbp = '\0'; \
+ (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \
+ cbp = cbuf; \
+}
+ /*
+ * Display the character. We do tab expansion here because
+ * the screen interface doesn't have any way to set the tab
+ * length. Note, it's theoretically possible for chlen to
+ * be larger than cbuf, if the user set a impossibly large
+ * tabstop.
+ */
+ if (is_tab)
+ while (chlen--) {
+ if (cbp >= ecbp)
+ FLUSH;
+ *cbp++ = TABCH;
+ }
+ else {
+ if (cbp + chlen >= ecbp)
+ FLUSH;
+ for (kp = KEY_NAME(sp, ch) + offset_in_char; chlen--;)
+ *cbp++ = *kp++;
+ }
+ }
+
+ if (scno < cols_per_screen) {
+ /* If didn't paint the whole line, update the cache. */
+ smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch);
+ 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 (list_dollar) {
+ ++scno;
+
+ chlen = KEY_LEN(sp, '$');
+ if (cbp + chlen >= ecbp)
+ FLUSH;
+ for (kp = KEY_NAME(sp, '$'); chlen--;)
+ *cbp++ = *kp++;
+ }
+
+ /* If still didn't paint the whole line, clear the rest. */
+ if (scno < cols_per_screen)
+ (void)gp->scr_clrtoeol(sp);
+ }
+
+ /* Flush any buffered characters. */
+ if (cbp > cbuf)
+ FLUSH;
+
+ret1: (void)gp->scr_move(sp, oldy, oldx);
+ return (0);
+}
+
+/*
+ * vs_number --
+ * Repaint the numbers on all the lines.
+ *
+ * PUBLIC: int vs_number __P((SCR *));
+ */
+int
+vs_number(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SMAP *smp;
+ VI_PRIVATE *vip;
+ size_t len, oldy, oldx;
+ int exist;
+ char nbuf[10];
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /* No reason to do anything if we're in input mode on the info line. */
+ if (F_ISSET(sp, SC_TINPUT_INFO))
+ return (0);
+
+ /*
+ * 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. (The problem is that file_lline will lie, and tell us
+ * that the info line is the last line in the file.) If that test
+ * fails, we have to check each line for existence.
+ */
+ exist = db_exist(sp, TMAP->lno + 1);
+
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+ for (smp = HMAP; smp <= TMAP; ++smp) {
+ /* Numbers are only displayed for the first screen line. */
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ if (smp->coff != 0)
+ continue;
+ } else
+ if (smp->soff != 1)
+ continue;
+
+ /*
+ * The first line of an empty file gets numbered, otherwise
+ * number any existing line.
+ */
+ if (smp->lno != 1 && !exist && !db_exist(sp, smp->lno))
+ break;
+
+ (void)gp->scr_move(sp, smp - HMAP, 0);
+ len = snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+ (void)gp->scr_addstr(sp, nbuf, len);
+ }
+ (void)gp->scr_move(sp, oldy, oldx);
+ return (0);
+}
diff --git a/contrib/nvi/vi/vs_msg.c b/contrib/nvi/vi/vs_msg.c
new file mode 100644
index 000000000000..7ef8f5343c15
--- /dev/null
+++ b/contrib/nvi/vi/vs_msg.c
@@ -0,0 +1,927 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_msg.c 10.77 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+typedef enum {
+ SCROLL_W, /* User wait. */
+ SCROLL_W_EX, /* User wait, or enter : to continue. */
+ SCROLL_W_QUIT /* User wait, or enter q to quit. */
+ /*
+ * SCROLL_W_QUIT has another semantic
+ * -- only wait if the screen is full
+ */
+} sw_t;
+
+static void vs_divider __P((SCR *));
+static void vs_msgsave __P((SCR *, mtype_t, char *, size_t));
+static void vs_output __P((SCR *, mtype_t, const char *, int));
+static void vs_scroll __P((SCR *, int *, sw_t));
+static void vs_wait __P((SCR *, int *, sw_t));
+
+/*
+ * vs_busy --
+ * Display, update or clear a busy message.
+ *
+ * This routine is the default editor interface for vi busy messages. It
+ * implements a standard strategy of stealing lines from the bottom of the
+ * vi text screen. Screens using an alternate method of displaying busy
+ * messages, e.g. X11 clock icons, should set their scr_busy function to the
+ * correct function before calling the main editor routine.
+ *
+ * PUBLIC: void vs_busy __P((SCR *, const char *, busy_t));
+ */
+void
+vs_busy(sp, msg, btype)
+ SCR *sp;
+ const char *msg;
+ busy_t btype;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+ static const char flagc[] = "|/-\\";
+ struct timeval tv;
+ size_t len, notused;
+ const char *p;
+
+ /* Ex doesn't display busy messages. */
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
+ return;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /*
+ * Most of this routine is to deal with the screen sharing real estate
+ * between the normal edit messages and the busy messages. Logically,
+ * all that's needed is something that puts up a message, periodically
+ * updates it, and then goes away.
+ */
+ switch (btype) {
+ case BUSY_ON:
+ ++vip->busy_ref;
+ if (vip->totalcount != 0 || vip->busy_ref != 1)
+ break;
+
+ /* Initialize state for updates. */
+ vip->busy_ch = 0;
+ (void)gettimeofday(&vip->busy_tv, NULL);
+
+ /* Save the current cursor. */
+ (void)gp->scr_cursor(sp, &vip->busy_oldy, &vip->busy_oldx);
+
+ /* Display the busy message. */
+ p = msg_cat(sp, msg, &len);
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_addstr(sp, p, len);
+ (void)gp->scr_cursor(sp, &notused, &vip->busy_fx);
+ (void)gp->scr_clrtoeol(sp);
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ break;
+ case BUSY_OFF:
+ if (vip->busy_ref == 0)
+ break;
+ --vip->busy_ref;
+
+ /*
+ * If the line isn't in use for another purpose, clear it.
+ * Always return to the original position.
+ */
+ if (vip->totalcount == 0 && vip->busy_ref == 0) {
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+ }
+ (void)gp->scr_move(sp, vip->busy_oldy, vip->busy_oldx);
+ break;
+ case BUSY_UPDATE:
+ if (vip->totalcount != 0 || vip->busy_ref == 0)
+ break;
+
+ /* Update no more than every 1/8 of a second. */
+ (void)gettimeofday(&tv, NULL);
+ if (((tv.tv_sec - vip->busy_tv.tv_sec) * 1000000 +
+ (tv.tv_usec - vip->busy_tv.tv_usec)) < 125000)
+ return;
+ vip->busy_tv = tv;
+
+ /* Display the update. */
+ if (vip->busy_ch == sizeof(flagc) - 1)
+ vip->busy_ch = 0;
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ (void)gp->scr_addstr(sp, flagc + vip->busy_ch++, 1);
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->busy_fx);
+ break;
+ }
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_home --
+ * Home the cursor to the bottom row, left-most column.
+ *
+ * PUBLIC: void vs_home __P((SCR *));
+ */
+void
+vs_home(sp)
+ SCR *sp;
+{
+ (void)sp->gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)sp->gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_update --
+ * Update a command.
+ *
+ * PUBLIC: void vs_update __P((SCR *, const char *, const char *));
+ */
+void
+vs_update(sp, m1, m2)
+ SCR *sp;
+ const char *m1, *m2;
+{
+ GS *gp;
+ size_t len, mlen, oldx, oldy;
+
+ gp = sp->gp;
+
+ /*
+ * This routine displays a message on the bottom line of the screen,
+ * without updating any of the command structures that would keep it
+ * there for any period of time, i.e. it is overwritten immediately.
+ *
+ * It's used by the ex read and ! commands when the user's command is
+ * expanded, and by the ex substitution confirmation prompt.
+ */
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ (void)ex_printf(sp,
+ "%s\n", m1 == NULL? "" : m1, m2 == NULL ? "" : m2);
+ (void)ex_fflush(sp);
+ }
+
+ /*
+ * Save the cursor position, the substitute-with-confirmation code
+ * will have already set it correctly.
+ */
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+
+ /* Clear the bottom line. */
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+
+ /*
+ * XXX
+ * Don't let long file names screw up the screen.
+ */
+ if (m1 != NULL) {
+ mlen = len = strlen(m1);
+ if (len > sp->cols - 2)
+ mlen = len = sp->cols - 2;
+ (void)gp->scr_addstr(sp, m1, mlen);
+ } else
+ len = 0;
+ if (m2 != NULL) {
+ mlen = strlen(m2);
+ if (len + mlen > sp->cols - 2)
+ mlen = (sp->cols - 2) - len;
+ (void)gp->scr_addstr(sp, m2, mlen);
+ }
+
+ (void)gp->scr_move(sp, oldy, oldx);
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_msg --
+ * Display ex output or error messages for the screen.
+ *
+ * This routine is the default editor interface for all ex output, and all ex
+ * and vi error/informational messages. It implements the standard strategy
+ * of stealing lines from the bottom of the vi text screen. Screens using an
+ * alternate method of displaying messages, e.g. dialog boxes, should set their
+ * scr_msg function to the correct function before calling the editor.
+ *
+ * PUBLIC: void vs_msg __P((SCR *, mtype_t, char *, size_t));
+ */
+void
+vs_msg(sp, mtype, line, len)
+ SCR *sp;
+ mtype_t mtype;
+ char *line;
+ size_t len;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+ size_t maxcols, oldx, oldy, padding;
+ const char *e, *s, *t;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ /*
+ * Ring the bell if it's scheduled.
+ *
+ * XXX
+ * Shouldn't we save this, too?
+ */
+ if (F_ISSET(sp, SC_TINPUT_INFO) || F_ISSET(gp, G_BELLSCHED))
+ if (F_ISSET(sp, SC_SCR_VI)) {
+ F_CLR(gp, G_BELLSCHED);
+ (void)gp->scr_bell(sp);
+ } else
+ F_SET(gp, G_BELLSCHED);
+
+ /*
+ * If vi is using the error line for text input, there's no screen
+ * real-estate for the error message. Nothing to do without some
+ * information as to how important the error message is.
+ */
+ if (F_ISSET(sp, SC_TINPUT_INFO))
+ return;
+
+ /*
+ * Ex or ex controlled screen output.
+ *
+ * If output happens during startup, e.g., a .exrc file, we may be
+ * in ex mode but haven't initialized the screen. Initialize here,
+ * and in this case, stay in ex mode.
+ *
+ * If the SC_SCR_EXWROTE bit is set, then we're switching back and
+ * forth between ex and vi, but the screen is trashed and we have
+ * to respect that. Switch to ex mode long enough to put out the
+ * message.
+ *
+ * If the SC_EX_WAIT_NO bit is set, turn it off -- we're writing to
+ * the screen, so previous opinions are ignored.
+ */
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
+ if (!F_ISSET(sp, SC_SCR_EX))
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ if (sp->gp->scr_screen(sp, SC_EX))
+ return;
+ } else
+ if (ex_init(sp))
+ return;
+
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+ (void)printf("%.*s", (int)len, line);
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+ (void)fflush(stdout);
+
+ F_CLR(sp, SC_EX_WAIT_NO);
+
+ if (!F_ISSET(sp, SC_SCR_EX))
+ (void)sp->gp->scr_screen(sp, SC_VI);
+ return;
+ }
+
+ /* If the vi screen isn't ready, save the message. */
+ if (!F_ISSET(sp, SC_SCR_VI)) {
+ (void)vs_msgsave(sp, mtype, line, len);
+ return;
+ }
+
+ /* Save the cursor position. */
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+
+ /* If it's an ex output message, just write it out. */
+ if (mtype == M_NONE) {
+ vs_output(sp, mtype, line, len);
+ goto ret;
+ }
+
+ /*
+ * If it's a vi message, strip the trailing <newline> so we can
+ * try and paste messages together.
+ */
+ if (line[len - 1] == '\n')
+ --len;
+
+ /*
+ * If a message won't fit on a single line, try to split on a <blank>.
+ * If a subsequent message fits on the same line, write a separator
+ * and output it. Otherwise, put out a newline.
+ *
+ * Need up to two padding characters normally; a semi-colon and a
+ * separating space. If only a single line on the screen, add some
+ * more for the trailing continuation message.
+ *
+ * XXX
+ * Assume that periods and semi-colons take up a single column on the
+ * screen.
+ *
+ * XXX
+ * There are almost certainly pathological cases that will break this
+ * code.
+ */
+ if (IS_ONELINE(sp))
+ (void)msg_cmsg(sp, CMSG_CONT_S, &padding);
+ else
+ padding = 0;
+ padding += 2;
+
+ maxcols = sp->cols - 1;
+ if (vip->lcontinue != 0)
+ if (len + vip->lcontinue + padding > maxcols)
+ vs_output(sp, vip->mtype, ".\n", 2);
+ else {
+ vs_output(sp, vip->mtype, ";", 1);
+ vs_output(sp, M_NONE, " ", 1);
+ }
+ vip->mtype = mtype;
+ for (s = line;; s = t) {
+ for (; len > 0 && isblank(*s); --len, ++s);
+ if (len == 0)
+ break;
+ if (len + vip->lcontinue > maxcols) {
+ for (e = s + (maxcols - vip->lcontinue);
+ e > s && !isblank(*e); --e);
+ if (e == s)
+ e = t = s + (maxcols - vip->lcontinue);
+ else
+ for (t = e; isblank(e[-1]); --e);
+ } else
+ e = t = s + len;
+
+ /*
+ * If the message ends in a period, discard it, we want to
+ * gang messages where possible.
+ */
+ len -= t - s;
+ if (len == 0 && (e - s) > 1 && s[(e - s) - 1] == '.')
+ --e;
+ vs_output(sp, mtype, s, e - s);
+
+ if (len != 0)
+ vs_output(sp, M_NONE, "\n", 1);
+
+ if (INTERRUPTED(sp))
+ break;
+ }
+
+ret: (void)gp->scr_move(sp, oldy, oldx);
+ (void)gp->scr_refresh(sp, 0);
+}
+
+/*
+ * vs_output --
+ * Output the text to the screen.
+ */
+static void
+vs_output(sp, mtype, line, llen)
+ SCR *sp;
+ mtype_t mtype;
+ const char *line;
+ int llen;
+{
+ CHAR_T *kp;
+ GS *gp;
+ VI_PRIVATE *vip;
+ size_t chlen, notused;
+ int ch, len, rlen, tlen;
+ const char *p, *t;
+ char *cbp, *ecbp, cbuf[128];
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ for (p = line, rlen = llen; llen > 0;) {
+ /* Get the next physical 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 + vip->lcontinue > sp->cols)
+ len = sp->cols - vip->lcontinue;
+
+ /*
+ * If the first line output, do nothing. If the second line
+ * output, draw the divider line. If drew a full screen, we
+ * remove the divider line. If it's a continuation line, move
+ * to the continuation point, else, move the screen up.
+ */
+ if (vip->lcontinue == 0) {
+ if (!IS_ONELINE(sp)) {
+ if (vip->totalcount == 1) {
+ (void)gp->scr_move(sp,
+ LASTLINE(sp) - 1, 0);
+ (void)gp->scr_clrtoeol(sp);
+ (void)vs_divider(sp);
+ F_SET(vip, VIP_DIVIDER);
+ ++vip->totalcount;
+ ++vip->linecount;
+ }
+ if (vip->totalcount == sp->t_maxrows &&
+ F_ISSET(vip, VIP_DIVIDER)) {
+ --vip->totalcount;
+ --vip->linecount;
+ F_CLR(vip, VIP_DIVIDER);
+ }
+ }
+ if (vip->totalcount != 0)
+ vs_scroll(sp, NULL, SCROLL_W_QUIT);
+
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ ++vip->totalcount;
+ ++vip->linecount;
+
+ if (INTERRUPTED(sp))
+ break;
+ } else
+ (void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);
+
+ /* Error messages are in inverse video. */
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+
+ /* Display the line, doing character translation. */
+#define FLUSH { \
+ *cbp = '\0'; \
+ (void)gp->scr_addstr(sp, cbuf, cbp - cbuf); \
+ cbp = cbuf; \
+}
+ ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
+ for (t = line, tlen = len; tlen--; ++t) {
+ ch = *t;
+ /*
+ * Replace tabs with spaces, there are places in
+ * ex that do column calculations without looking
+ * at <tabs> -- and all routines that care about
+ * <tabs> do their own expansions. This catches
+ * <tabs> in things like tag search strings.
+ */
+ if (ch == '\t')
+ ch = ' ';
+ chlen = KEY_LEN(sp, ch);
+ if (cbp + chlen >= ecbp)
+ FLUSH;
+ for (kp = KEY_NAME(sp, ch); chlen--;)
+ *cbp++ = *kp++;
+ }
+ if (cbp > cbuf)
+ FLUSH;
+ if (mtype == M_ERR)
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+
+ /* Clear the rest of the line. */
+ (void)gp->scr_clrtoeol(sp);
+
+ /* If we loop, it's a new line. */
+ vip->lcontinue = 0;
+
+ /* Reset for the next line. */
+ line += len;
+ llen -= len;
+ if (p != NULL) {
+ ++line;
+ --llen;
+ }
+ }
+
+ /* Set up next continuation line. */
+ if (p == NULL)
+ gp->scr_cursor(sp, &notused, &vip->lcontinue);
+}
+
+/*
+ * vs_ex_resolve --
+ * Deal with ex message output.
+ *
+ * This routine is called when exiting a colon command to resolve any ex
+ * output that may have occurred.
+ *
+ * PUBLIC: int vs_ex_resolve __P((SCR *, int *));
+ */
+int
+vs_ex_resolve(sp, continuep)
+ SCR *sp;
+ int *continuep;
+{
+ EVENT ev;
+ GS *gp;
+ VI_PRIVATE *vip;
+ sw_t wtype;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ *continuep = 0;
+
+ /* If we ran any ex command, we can't trust the cursor position. */
+ F_SET(vip, VIP_CUR_INVALID);
+
+ /* Terminate any partially written message. */
+ if (vip->lcontinue != 0) {
+ vs_output(sp, vip->mtype, ".", 1);
+ vip->lcontinue = 0;
+
+ vip->mtype = M_NONE;
+ }
+
+ /*
+ * If we switched out of the vi screen into ex, switch back while we
+ * figure out what to do with the screen and potentially get another
+ * command to execute.
+ *
+ * If we didn't switch into ex, we're not required to wait, and less
+ * than 2 lines of output, we can continue without waiting for the
+ * wait.
+ *
+ * Note, all other code paths require waiting, so we leave the report
+ * of modified lines until later, so that we won't wait for no other
+ * reason than a threshold number of lines were modified. This means
+ * we display cumulative line modification reports for groups of ex
+ * commands. That seems right to me (well, at least not wrong).
+ */
+ if (F_ISSET(sp, SC_SCR_EXWROTE)) {
+ if (sp->gp->scr_screen(sp, SC_VI))
+ return (1);
+ } else
+ if (!F_ISSET(sp, SC_EX_WAIT_YES) && vip->totalcount < 2) {
+ F_CLR(sp, SC_EX_WAIT_NO);
+ return (0);
+ }
+
+ /* Clear the required wait flag, it's no longer needed. */
+ F_CLR(sp, SC_EX_WAIT_YES);
+
+ /*
+ * Wait, unless explicitly told not to wait or the user interrupted
+ * the command. If the user is leaving the screen, for any reason,
+ * they can't continue with further ex commands.
+ */
+ if (!F_ISSET(sp, SC_EX_WAIT_NO) && !INTERRUPTED(sp)) {
+ wtype = F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE |
+ SC_FSWITCH | SC_SSWITCH) ? SCROLL_W : SCROLL_W_EX;
+ if (F_ISSET(sp, SC_SCR_EXWROTE))
+ vs_wait(sp, continuep, wtype);
+ else
+ vs_scroll(sp, continuep, wtype);
+ if (*continuep)
+ return (0);
+ }
+
+ /* If ex wrote on the screen, refresh the screen image. */
+ if (F_ISSET(sp, SC_SCR_EXWROTE))
+ F_SET(vip, VIP_N_EX_PAINT);
+
+ /*
+ * If we're not the bottom of the split screen stack, the screen
+ * image itself is wrong, so redraw everything.
+ */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ F_SET(sp, SC_SCR_REDRAW);
+
+ /* If ex changed the underlying file, the map itself is wrong. */
+ if (F_ISSET(vip, VIP_N_EX_REDRAW))
+ F_SET(sp, SC_SCR_REFORMAT);
+
+ /* Ex may have switched out of the alternate screen, return. */
+ (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
+
+ /*
+ * Whew. We're finally back home, after what feels like years.
+ * Kiss the ground.
+ */
+ F_CLR(sp, SC_SCR_EXWROTE | SC_EX_WAIT_NO);
+
+ /*
+ * We may need to repaint some of the screen, e.g.:
+ *
+ * :set
+ * :!ls
+ *
+ * gives us a combination of some lines that are "wrong", and a need
+ * for a full refresh.
+ */
+ if (vip->totalcount > 1) {
+ /* Set up the redraw of the overwritten lines. */
+ ev.e_event = E_REPAINT;
+ ev.e_flno = vip->totalcount >=
+ sp->rows ? 1 : sp->rows - vip->totalcount;
+ ev.e_tlno = sp->rows;
+
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ /* Redraw. */
+ (void)vs_repaint(sp, &ev);
+ } else
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ return (0);
+}
+
+/*
+ * vs_resolve --
+ * Deal with message output.
+ *
+ * PUBLIC: int vs_resolve __P((SCR *, SCR *, int));
+ */
+int
+vs_resolve(sp, csp, forcewait)
+ SCR *sp, *csp;
+ int forcewait;
+{
+ EVENT ev;
+ GS *gp;
+ MSGS *mp;
+ VI_PRIVATE *vip;
+ size_t oldy, oldx;
+ int redraw;
+
+ /*
+ * Vs_resolve is called from the main vi loop and the refresh function
+ * to periodically ensure that the user has seen any messages that have
+ * been displayed and that any status lines are correct. The sp screen
+ * is the screen we're checking, usually the current screen. When it's
+ * not, csp is the current screen, used for final cursor positioning.
+ */
+ gp = sp->gp;
+ vip = VIP(sp);
+ if (csp == NULL)
+ csp = sp;
+
+ /* Save the cursor position. */
+ (void)gp->scr_cursor(csp, &oldy, &oldx);
+
+ /* Ring the bell if it's scheduled. */
+ if (F_ISSET(gp, G_BELLSCHED)) {
+ F_CLR(gp, G_BELLSCHED);
+ (void)gp->scr_bell(sp);
+ }
+
+ /* Display new file status line. */
+ if (F_ISSET(sp, SC_STATUS)) {
+ F_CLR(sp, SC_STATUS);
+ msgq_status(sp, sp->lno, MSTAT_TRUNCATE);
+ }
+
+ /* Report on line modifications. */
+ mod_rpt(sp);
+
+ /*
+ * Flush any saved messages. If the screen isn't ready, refresh
+ * it. (A side-effect of screen refresh is that we can display
+ * messages.) Once this is done, don't trust the cursor. That
+ * extra refresh screwed the pooch.
+ */
+ if (gp->msgq.lh_first != NULL) {
+ if (!F_ISSET(sp, SC_SCR_VI) && vs_refresh(sp, 1))
+ return (1);
+ while ((mp = gp->msgq.lh_first) != NULL) {
+ gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
+ LIST_REMOVE(mp, q);
+ free(mp->buf);
+ free(mp);
+ }
+ F_SET(vip, VIP_CUR_INVALID);
+ }
+
+ switch (vip->totalcount) {
+ case 0:
+ redraw = 0;
+ break;
+ case 1:
+ /*
+ * If we're switching screens, we have to wait for messages,
+ * regardless. If we don't wait, skip updating the modeline.
+ */
+ if (forcewait)
+ vs_scroll(sp, NULL, SCROLL_W);
+ else
+ F_SET(vip, VIP_S_MODELINE);
+
+ redraw = 0;
+ break;
+ default:
+ /*
+ * If >1 message line in use, prompt the user to continue and
+ * repaint overwritten lines.
+ */
+ vs_scroll(sp, NULL, SCROLL_W);
+
+ ev.e_event = E_REPAINT;
+ ev.e_flno = vip->totalcount >=
+ sp->rows ? 1 : sp->rows - vip->totalcount;
+ ev.e_tlno = sp->rows;
+
+ redraw = 1;
+ break;
+ }
+
+ /* Reset the count of overwriting lines. */
+ vip->linecount = vip->lcontinue = vip->totalcount = 0;
+
+ /* Redraw. */
+ if (redraw)
+ (void)vs_repaint(sp, &ev);
+
+ /* Restore the cursor position. */
+ (void)gp->scr_move(csp, oldy, oldx);
+
+ return (0);
+}
+
+/*
+ * vs_scroll --
+ * Scroll the screen for output.
+ */
+static void
+vs_scroll(sp, continuep, wtype)
+ SCR *sp;
+ int *continuep;
+ sw_t wtype;
+{
+ GS *gp;
+ VI_PRIVATE *vip;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ if (!IS_ONELINE(sp)) {
+ /*
+ * 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.
+ */
+ (void)gp->scr_move(sp, vip->totalcount <
+ sp->rows ? LASTLINE(sp) - vip->totalcount : 0, 0);
+ (void)gp->scr_deleteln(sp);
+
+ /* If there are screens below us, push them back into place. */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_insertln(sp);
+ }
+ }
+ if (wtype == SCROLL_W_QUIT && vip->linecount < sp->t_maxrows)
+ return;
+ vs_wait(sp, continuep, wtype);
+}
+
+/*
+ * vs_wait --
+ * Prompt the user to continue.
+ */
+static void
+vs_wait(sp, continuep, wtype)
+ SCR *sp;
+ int *continuep;
+ sw_t wtype;
+{
+ EVENT ev;
+ VI_PRIVATE *vip;
+ const char *p;
+ GS *gp;
+ size_t len;
+
+ gp = sp->gp;
+ vip = VIP(sp);
+
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ if (IS_ONELINE(sp))
+ p = msg_cmsg(sp, CMSG_CONT_S, &len);
+ else
+ switch (wtype) {
+ case SCROLL_W_QUIT:
+ p = msg_cmsg(sp, CMSG_CONT_Q, &len);
+ break;
+ case SCROLL_W_EX:
+ p = msg_cmsg(sp, CMSG_CONT_EX, &len);
+ break;
+ case SCROLL_W:
+ p = msg_cmsg(sp, CMSG_CONT, &len);
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ (void)gp->scr_addstr(sp, p, len);
+
+ ++vip->totalcount;
+ vip->linecount = 0;
+
+ (void)gp->scr_clrtoeol(sp);
+ (void)gp->scr_refresh(sp, 0);
+
+ /* Get a single character from the terminal. */
+ if (continuep != NULL)
+ *continuep = 0;
+ for (;;) {
+ if (v_event_get(sp, &ev, 0, 0))
+ return;
+ if (ev.e_event == E_CHARACTER)
+ break;
+ if (ev.e_event == E_INTERRUPT) {
+ ev.e_c = CH_QUIT;
+ F_SET(gp, G_INTERRUPTED);
+ break;
+ }
+ (void)gp->scr_bell(sp);
+ }
+ switch (wtype) {
+ case SCROLL_W_QUIT:
+ if (ev.e_c == CH_QUIT)
+ F_SET(gp, G_INTERRUPTED);
+ break;
+ case SCROLL_W_EX:
+ if (ev.e_c == ':' && continuep != NULL)
+ *continuep = 1;
+ break;
+ case SCROLL_W:
+ break;
+ }
+}
+
+/*
+ * vs_divider --
+ * Draw a dividing line between the screen and the output.
+ */
+static void
+vs_divider(sp)
+ SCR *sp;
+{
+ GS *gp;
+ size_t len;
+
+#define DIVIDESTR "+=+=+=+=+=+=+=+"
+ len =
+ sizeof(DIVIDESTR) - 1 > sp->cols ? sp->cols : sizeof(DIVIDESTR) - 1;
+ gp = sp->gp;
+ (void)gp->scr_attr(sp, SA_INVERSE, 1);
+ (void)gp->scr_addstr(sp, DIVIDESTR, len);
+ (void)gp->scr_attr(sp, SA_INVERSE, 0);
+}
+
+/*
+ * vs_msgsave --
+ * Save a message for later display.
+ */
+static void
+vs_msgsave(sp, mt, p, len)
+ SCR *sp;
+ mtype_t mt;
+ char *p;
+ size_t len;
+{
+ GS *gp;
+ MSGS *mp_c, *mp_n;
+
+ /*
+ * We have to handle messages before we have any place to put them.
+ * If there's no screen support yet, allocate a msg structure, copy
+ * in the message, and queue it on the global structure. If we can't
+ * allocate memory here, we're genuinely screwed, dump the message
+ * to stderr in the (probably) vain hope that someone will see it.
+ */
+ CALLOC_GOTO(sp, mp_n, MSGS *, 1, sizeof(MSGS));
+ MALLOC_GOTO(sp, mp_n->buf, char *, len);
+
+ memmove(mp_n->buf, p, len);
+ mp_n->len = len;
+ mp_n->mtype = mt;
+
+ gp = sp->gp;
+ if ((mp_c = gp->msgq.lh_first) == NULL) {
+ LIST_INSERT_HEAD(&gp->msgq, mp_n, q);
+ } else {
+ for (; mp_c->q.le_next != NULL; mp_c = mp_c->q.le_next);
+ LIST_INSERT_AFTER(mp_c, mp_n, q);
+ }
+ return;
+
+alloc_err:
+ if (mp_n != NULL)
+ free(mp_n);
+ (void)fprintf(stderr, "%.*s\n", (int)len, p);
+}
diff --git a/contrib/nvi/vi/vs_refresh.c b/contrib/nvi/vi/vs_refresh.c
new file mode 100644
index 000000000000..81587608c1a2
--- /dev/null
+++ b/contrib/nvi/vi/vs_refresh.c
@@ -0,0 +1,885 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_refresh.c 10.44 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+#define UPDATE_CURSOR 0x01 /* Update the cursor. */
+#define UPDATE_SCREEN 0x02 /* Flush to screen. */
+
+static void vs_modeline __P((SCR *));
+static int vs_paint __P((SCR *, u_int));
+
+/*
+ * v_repaint --
+ * Repaint selected lines from the screen.
+ *
+ * PUBLIC: int vs_repaint __P((SCR *, EVENT *));
+ */
+int
+vs_repaint(sp, evp)
+ SCR *sp;
+ EVENT *evp;
+{
+ SMAP *smp;
+
+ for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) {
+ smp = HMAP + evp->e_flno - 1;
+ SMAP_FLUSH(smp);
+ if (vs_line(sp, smp, NULL, NULL))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * vs_refresh --
+ * Refresh all screens.
+ *
+ * PUBLIC: int vs_refresh __P((SCR *, int));
+ */
+int
+vs_refresh(sp, forcepaint)
+ SCR *sp;
+ int forcepaint;
+{
+ GS *gp;
+ SCR *tsp;
+ int need_refresh;
+ u_int priv_paint, pub_paint;
+
+ gp = sp->gp;
+
+ /*
+ * 1: Refresh the screen.
+ *
+ * If SC_SCR_REDRAW is set in the current screen, repaint everything
+ * that we can find, including status lines.
+ */
+ if (F_ISSET(sp, SC_SCR_REDRAW))
+ for (tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp)
+ F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
+
+ /*
+ * 2: 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 that aren't exiting that
+ * have paint or dirty bits set. Always update their screens, we
+ * are not likely to get another chance. Finally, if we refresh any
+ * screens other than the current one, the cursor will be trashed.
+ */
+ pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
+ priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
+ if (O_ISSET(sp, O_NUMBER))
+ priv_paint |= VIP_N_RENUMBER;
+ for (tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
+ (F_ISSET(tsp, pub_paint) ||
+ F_ISSET(VIP(tsp), priv_paint))) {
+ (void)vs_paint(tsp,
+ (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
+ UPDATE_CURSOR : 0) | UPDATE_SCREEN);
+ F_SET(VIP(sp), VIP_CUR_INVALID);
+ }
+
+ /*
+ * 3: Refresh the current screen.
+ *
+ * Always refresh the current screen, it may be a cursor movement.
+ * Also, always do it last -- that way, SC_SCR_REDRAW can be set
+ * in the current screen only, and the screen won't flash.
+ */
+ if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
+ F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
+ return (1);
+
+ /*
+ * 4: Paint any missing status lines.
+ *
+ * XXX
+ * This is fairly evil. Status lines are written using the vi message
+ * mechanism, since we have no idea how long they are. Since we may be
+ * painting screens other than the current one, we don't want to make
+ * the user wait. We depend heavily on there not being any other lines
+ * currently waiting to be displayed and the message truncation code in
+ * the msgq_status routine working.
+ *
+ * And, finally, if we updated any status lines, make sure the cursor
+ * gets back to where it belongs.
+ */
+ for (need_refresh = 0, tsp = gp->dq.cqh_first;
+ tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
+ if (F_ISSET(tsp, SC_STATUS)) {
+ need_refresh = 1;
+ vs_resolve(tsp, sp, 0);
+ }
+ if (need_refresh)
+ (void)gp->scr_refresh(sp, 0);
+
+ /*
+ * A side-effect of refreshing the screen is that it's now ready
+ * for everything else, i.e. messages.
+ */
+ F_SET(sp, SC_SCR_VI);
+ return (0);
+}
+
+/*
+ * vs_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.
+ */
+static int
+vs_paint(sp, flags)
+ SCR *sp;
+ u_int flags;
+{
+ GS *gp;
+ SMAP *smp, tmp;
+ VI_PRIVATE *vip;
+ recno_t lastline, lcnt;
+ size_t cwtotal, cnt, len, notused, off, y;
+ int ch, didpaint, isempty, leftright_warp;
+ char *p;
+
+#define LNO sp->lno /* Current file line. */
+#define OLNO vip->olno /* Remembered file line. */
+#define CNO sp->cno /* Current file column. */
+#define OCNO vip->ocno /* Remembered file column. */
+#define SCNO vip->sc_col /* Current screen column. */
+
+ gp = sp->gp;
+ vip = VIP(sp);
+ didpaint = leftright_warp = 0;
+
+ /*
+ * 5: 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, SC_SCR_REFORMAT)) {
+ /* Invalidate the line size cache. */
+ VI_SCR_CFLUSH(vip);
+
+ /* Toss vs_line() cached information. */
+ if (F_ISSET(sp, SC_SCR_TOP)) {
+ if (vs_sm_fill(sp, LNO, P_TOP))
+ return (1);
+ }
+ else if (F_ISSET(sp, SC_SCR_CENTER)) {
+ if (vs_sm_fill(sp, LNO, P_MIDDLE))
+ return (1);
+ } else
+ if (vs_sm_fill(sp, OOBLNO, P_TOP))
+ return (1);
+ F_SET(sp, SC_SCR_REDRAW);
+ }
+
+ /*
+ * 6: Line movement.
+ *
+ * Line changes can cause the top line to change as well. As
+ * before, if the movement is large, the screen is repainted.
+ *
+ * 6a: 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 (IS_SMALL(sp))
+ if (LNO < HMAP->lno) {
+ lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ ++TMAP;
+ if (vs_sm_1down(sp))
+ return (1);
+ }
+ else
+ goto small_fill;
+ } else if (LNO > TMAP->lno) {
+ lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ if (vs_sm_next(sp, TMAP, TMAP + 1))
+ return (1);
+ ++TMAP;
+ if (vs_line(sp, TMAP, NULL, NULL))
+ return (1);
+ }
+ else {
+small_fill: (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+ for (; sp->t_rows > sp->t_minrows;
+ --sp->t_rows, --TMAP) {
+ (void)gp->scr_move(sp, TMAP - HMAP, 0);
+ (void)gp->scr_clrtoeol(sp);
+ }
+ if (vs_sm_fill(sp, LNO, P_FILL))
+ return (1);
+ F_SET(sp, SC_SCR_REDRAW);
+ goto adjust;
+ }
+ }
+
+ /*
+ * 6b: Line down, or current screen.
+ */
+ if (LNO >= HMAP->lno) {
+ /* Current screen. */
+ if (LNO <= TMAP->lno)
+ goto adjust;
+ if (F_ISSET(sp, SC_SCR_TOP))
+ goto top;
+ if (F_ISSET(sp, SC_SCR_CENTER))
+ goto middle;
+
+ /*
+ * If less than half a screen above the line, scroll down
+ * until the line is on the screen.
+ */
+ lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ while (lcnt--)
+ if (vs_sm_1up(sp))
+ return (1);
+ goto adjust;
+ }
+ goto bottom;
+ }
+
+ /*
+ * 6c: If not on the current screen, may request center or top.
+ */
+ if (F_ISSET(sp, SC_SCR_TOP))
+ goto top;
+ if (F_ISSET(sp, SC_SCR_CENTER))
+ goto middle;
+
+ /*
+ * 6d: Line up.
+ */
+ lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ /*
+ * If less than half a screen below the line, scroll up until
+ * the line is the first line on the screen. Special check so
+ * that if the screen has been emptied, we refill it.
+ */
+ if (db_exist(sp, HMAP->lno)) {
+ while (lcnt--)
+ if (vs_sm_1down(sp))
+ return (1);
+ goto adjust;
+ }
+
+ /*
+ * If less than a half screen from the bottom of the file,
+ * put the last line of the file on the bottom of the screen.
+ */
+bottom: if (db_last(sp, &lastline))
+ return (1);
+ tmp.lno = LNO;
+ tmp.coff = HMAP->coff;
+ tmp.soff = 1;
+ lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows);
+ if (lcnt < HALFTEXT(sp)) {
+ if (vs_sm_fill(sp, lastline, P_BOTTOM))
+ return (1);
+ F_SET(sp, SC_SCR_REDRAW);
+ goto adjust;
+ }
+ /* It's not close, just put the line in the middle. */
+ goto middle;
+ }
+
+ /*
+ * 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.coff = HMAP->coff;
+ tmp.soff = 1;
+ lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ if (vs_sm_fill(sp, 1, P_TOP))
+ return (1);
+ } else
+middle: if (vs_sm_fill(sp, LNO, P_MIDDLE))
+ return (1);
+ if (0) {
+top: if (vs_sm_fill(sp, LNO, P_TOP))
+ return (1);
+ }
+ F_SET(sp, SC_SCR_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 = vs_screens(sp, LNO, &CNO);
+ if (LNO == HMAP->lno && cnt < HMAP->soff)
+ if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
+ HMAP->soff = cnt;
+ vs_sm_fill(sp, OOBLNO, P_TOP);
+ F_SET(sp, SC_SCR_REDRAW);
+ } else
+ while (cnt < HMAP->soff)
+ if (vs_sm_1down(sp))
+ return (1);
+ if (LNO == TMAP->lno && cnt > TMAP->soff)
+ if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
+ TMAP->soff = cnt;
+ vs_sm_fill(sp, OOBLNO, P_BOTTOM);
+ F_SET(sp, SC_SCR_REDRAW);
+ } else
+ while (cnt > TMAP->soff)
+ if (vs_sm_1up(sp))
+ return (1);
+ }
+
+ /*
+ * If the screen needs to be repainted, skip cursor optimization.
+ * However, in the code above we skipped leftright scrolling on
+ * the grounds that the cursor code would handle it. Make sure
+ * the right screen is up.
+ */
+ if (F_ISSET(sp, SC_SCR_REDRAW)) {
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ goto slow;
+ goto paint;
+ }
+
+ /*
+ * 7: Cursor movements (current screen only).
+ */
+ if (!LF_ISSET(UPDATE_CURSOR))
+ goto number;
+
+ /*
+ * 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. 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, reread it..
+ */
+ if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
+ goto slow;
+
+ /* Otherwise, if nothing's changed, ignore the cursor. */
+ 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 (db_eget(sp, LNO, &p, &len, &isempty)) {
+ if (isempty)
+ goto slow;
+ return (1);
+ }
+
+#ifdef DEBUG
+ /* Sanity checking. */
+ 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.
+ */
+ if (CNO < OCNO) {
+ /*
+ * 7a: 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;
+
+ /*
+ * Quick 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 slow;
+
+ /*
+ * 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 += KEY_LEN(sp, ch))
+ 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 (KEY_LEN(sp, ch) > 1)
+ cwtotal -= KEY_LEN(sp, ch) - 1;
+
+ /*
+ * If the new column moved us off of the current logical line,
+ * calculate a new one. If doing leftright scrolling, we've
+ * moved off of the current screen, as well.
+ */
+ if (SCNO < cwtotal)
+ goto slow;
+ SCNO -= cwtotal;
+ } else {
+ /*
+ * 7b: 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 += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp))
+ break;
+ }
+
+ /*
+ * Increment the screen cursor by the total width of the
+ * characters.
+ */
+ SCNO = cwtotal;
+
+ /* See screen change comment in section 6a. */
+ if (SCNO >= SCREEN_COLS(sp))
+ goto slow;
+ }
+
+ /*
+ * 7c: Fast cursor update.
+ *
+ * We have the current column, retrieve the current row.
+ */
+fast: (void)gp->scr_cursor(sp, &y, &notused);
+ goto done_cursor;
+
+ /*
+ * 7d: Slow cursor update.
+ *
+ * Walk through the map and find the current line.
+ */
+slow: for (smp = HMAP; smp->lno != LNO; ++smp);
+
+ /*
+ * 7e: Leftright scrolling adjustment.
+ *
+ * If doing left-right scrolling and the cursor movement has changed
+ * the displayed screen, scroll the screen left or right, unless we're
+ * updating the info line in which case we just scroll that one line.
+ * We adjust the offset up or down until we have a window that covers
+ * the current column, making sure that we adjust differently for the
+ * first screen as compared to subsequent ones.
+ */
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ /*
+ * Get the screen column for this character, and correct
+ * for the number option offset.
+ */
+ cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
+ if (O_ISSET(sp, O_NUMBER))
+ cnt -= O_NUMBER_LENGTH;
+
+ /* Adjust the window towards the beginning of the line. */
+ off = smp->coff;
+ if (off >= cnt) {
+ do {
+ if (off >= O_VAL(sp, O_SIDESCROLL))
+ off -= O_VAL(sp, O_SIDESCROLL);
+ else {
+ off = 0;
+ break;
+ }
+ } while (off >= cnt);
+ goto shifted;
+ }
+
+ /* Adjust the window towards the end of the line. */
+ if (off == 0 && off + SCREEN_COLS(sp) < cnt ||
+ off != 0 && off + sp->cols < cnt) {
+ do {
+ off += O_VAL(sp, O_SIDESCROLL);
+ } while (off + sp->cols < cnt);
+
+shifted: /* Fill in screen map with the new offset. */
+ if (F_ISSET(sp, SC_TINPUT_INFO))
+ smp->coff = off;
+ else {
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->coff = off;
+ leftright_warp = 1;
+ }
+ goto paint;
+ }
+
+ /*
+ * We may have jumped here to adjust a leftright screen because
+ * redraw was set. If so, we have to paint the entire screen.
+ */
+ if (F_ISSET(sp, SC_SCR_REDRAW))
+ goto paint;
+ }
+
+ /*
+ * Update the screen lines for this particular file line until we
+ * have a new screen cursor position.
+ */
+ for (y = -1,
+ vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
+ if (vs_line(sp, smp, &y, &SCNO))
+ return (1);
+ if (y != -1) {
+ vip->sc_smap = smp;
+ break;
+ }
+ }
+ goto done_cursor;
+
+ /*
+ * 8: Repaint the entire screen.
+ *
+ * Lost big, do what you have to do. We flush the cache, since
+ * SC_SCR_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 (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
+ if (vs_line(sp, smp, &y, &SCNO))
+ return (1);
+ if (y != -1 && vip->sc_smap == NULL)
+ vip->sc_smap = smp;
+ }
+ /*
+ * If it's a small screen and we're redrawing, clear the unused lines,
+ * ex may have overwritten them.
+ */
+ if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ (void)gp->scr_move(sp, cnt, 0);
+ (void)gp->scr_clrtoeol(sp);
+ }
+
+ didpaint = 1;
+
+done_cursor:
+ /*
+ * Sanity checking. When the repainting code messes up, the usual
+ * result is we don't repaint the cursor and so sc_smap will be
+ * NULL. If we're debugging, die, otherwise restart from scratch.
+ */
+#ifdef DEBUG
+ if (vip->sc_smap == NULL)
+ abort();
+#else
+ if (vip->sc_smap == NULL) {
+ F_SET(sp, SC_SCR_REFORMAT);
+ return (vs_paint(sp, flags));
+ }
+#endif
+
+ /*
+ * 9: Set the remembered cursor values.
+ */
+ OCNO = CNO;
+ OLNO = LNO;
+
+ /*
+ * 10: Repaint the line numbers.
+ *
+ * If O_NUMBER is set and the VIP_N_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(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
+ return (1);
+
+ /*
+ * 11: Update the mode line, position the cursor, and flush changes.
+ *
+ * If we warped the screen, we have to refresh everything.
+ */
+ if (leftright_warp)
+ LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
+
+ if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
+ !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
+ vs_modeline(sp);
+
+ if (LF_ISSET(UPDATE_CURSOR)) {
+ (void)gp->scr_move(sp, y, SCNO);
+
+ /*
+ * XXX
+ * If the screen shifted, we recalculate the "most favorite"
+ * cursor position. Vi won't know that we've warped the
+ * screen, so it's going to have a wrong idea about where the
+ * cursor should be. This is vi's problem, and fixing it here
+ * is a gross layering violation.
+ */
+ if (leftright_warp)
+ (void)vs_column(sp, &sp->rcm);
+ }
+
+ if (LF_ISSET(UPDATE_SCREEN))
+ (void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
+
+ /* 12: Clear the flags that are handled by this routine. */
+ F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
+ F_CLR(vip, VIP_CUR_INVALID |
+ VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
+
+ return (0);
+
+#undef LNO
+#undef OLNO
+#undef CNO
+#undef OCNO
+#undef SCNO
+}
+
+/*
+ * vs_modeline --
+ * Update the mode line.
+ */
+static void
+vs_modeline(sp)
+ SCR *sp;
+{
+ static char * const modes[] = {
+ "215|Append", /* SM_APPEND */
+ "216|Change", /* SM_CHANGE */
+ "217|Command", /* SM_COMMAND */
+ "218|Insert", /* SM_INSERT */
+ "219|Replace", /* SM_REPLACE */
+ };
+ GS *gp;
+ size_t cols, curcol, curlen, endpoint, len, midpoint;
+ const char *t;
+ int ellipsis;
+ char *p, buf[20];
+
+ gp = sp->gp;
+
+ /*
+ * We put down the file name, the ruler, the mode and the dirty flag.
+ * If there's not enough room, there's not enough room, we don't play
+ * any special games. We try to put the ruler in the middle and the
+ * mode and dirty flag at the end.
+ *
+ * !!!
+ * Leave the last character blank, in case it's a really dumb terminal
+ * with hardware scroll. Second, don't paint the last character in the
+ * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
+ *
+ * Move to the last line on the screen.
+ */
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+
+ /* If more than one screen in the display, show the file name. */
+ curlen = 0;
+ if (IS_SPLIT(sp)) {
+ for (p = sp->frp->name; *p != '\0'; ++p);
+ for (ellipsis = 0, cols = sp->cols / 2; --p > sp->frp->name;) {
+ if (*p == '/') {
+ ++p;
+ break;
+ }
+ if ((curlen += KEY_LEN(sp, *p)) > cols) {
+ ellipsis = 3;
+ curlen +=
+ KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
+ while (curlen > cols) {
+ ++p;
+ curlen -= KEY_LEN(sp, *p);
+ }
+ break;
+ }
+ }
+ if (ellipsis) {
+ while (ellipsis--)
+ (void)gp->scr_addstr(sp,
+ KEY_NAME(sp, '.'), KEY_LEN(sp, '.'));
+ (void)gp->scr_addstr(sp,
+ KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
+ }
+ for (; *p != '\0'; ++p)
+ (void)gp->scr_addstr(sp,
+ KEY_NAME(sp, *p), KEY_LEN(sp, *p));
+ }
+
+ /* Clear the rest of the line. */
+ (void)gp->scr_clrtoeol(sp);
+
+ /*
+ * Display the ruler. If we're not at the midpoint yet, move there.
+ * Otherwise, add in two extra spaces.
+ *
+ * Adjust the current column for the fact that the editor uses it as
+ * a zero-based number.
+ *
+ * XXX
+ * Assume that numbers, commas, and spaces only take up a single
+ * column on the screen.
+ */
+ cols = sp->cols - 1;
+ if (O_ISSET(sp, O_RULER)) {
+ vs_column(sp, &curcol);
+ len =
+ snprintf(buf, sizeof(buf), "%lu,%lu", sp->lno, curcol + 1);
+
+ midpoint = (cols - ((len + 1) / 2)) / 2;
+ if (curlen < midpoint) {
+ (void)gp->scr_move(sp, LASTLINE(sp), midpoint);
+ curlen += len;
+ } else if (curlen + 2 + len < cols) {
+ (void)gp->scr_addstr(sp, " ", 2);
+ curlen += 2 + len;
+ }
+ (void)gp->scr_addstr(sp, buf, len);
+ }
+
+ /*
+ * Display the mode and the modified flag, as close to the end of the
+ * line as possible, but guaranteeing at least two spaces between the
+ * ruler and the modified flag.
+ */
+#define MODESIZE 9
+ endpoint = cols;
+ if (O_ISSET(sp, O_SHOWMODE)) {
+ if (F_ISSET(sp->ep, F_MODIFIED))
+ --endpoint;
+ t = msg_cat(sp, modes[sp->showmode], &len);
+ endpoint -= len;
+ }
+
+ if (endpoint > curlen + 2) {
+ (void)gp->scr_move(sp, LASTLINE(sp), endpoint);
+ if (O_ISSET(sp, O_SHOWMODE)) {
+ if (F_ISSET(sp->ep, F_MODIFIED))
+ (void)gp->scr_addstr(sp,
+ KEY_NAME(sp, '*'), KEY_LEN(sp, '*'));
+ (void)gp->scr_addstr(sp, t, len);
+ }
+ }
+}
diff --git a/contrib/nvi/vi/vs_relative.c b/contrib/nvi/vi/vs_relative.c
new file mode 100644
index 000000000000..c92c10c43e2e
--- /dev/null
+++ b/contrib/nvi/vi/vs_relative.c
@@ -0,0 +1,305 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_relative.c 10.11 (Berkeley) 5/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+/*
+ * vs_column --
+ * Return the logical column of the cursor in the line.
+ *
+ * PUBLIC: int vs_column __P((SCR *, size_t *));
+ */
+int
+vs_column(sp, colp)
+ SCR *sp;
+ size_t *colp;
+{
+ VI_PRIVATE *vip;
+
+ vip = VIP(sp);
+
+ *colp = (O_ISSET(sp, O_LEFTRIGHT) ?
+ vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) +
+ vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0);
+ return (0);
+}
+
+/*
+ * vs_screens --
+ * Return the screens necessary to display the line, or if specified,
+ * the physical character column within the line, including space
+ * required for the O_NUMBER and O_LIST options.
+ *
+ * PUBLIC: size_t vs_screens __P((SCR *, recno_t, size_t *));
+ */
+size_t
+vs_screens(sp, lno, cnop)
+ SCR *sp;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t cols, screens;
+
+ /* Left-right screens are simple, it's always 1. */
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ return (1);
+
+ /*
+ * Check for a cached value. We maintain a cache because, if the
+ * line is large, this routine gets called repeatedly. One other
+ * hack, lots of time the cursor is on column one, which is an easy
+ * one.
+ */
+ if (cnop == NULL) {
+ if (VIP(sp)->ss_lno == lno)
+ return (VIP(sp)->ss_screens);
+ } else if (*cnop == 0)
+ return (1);
+
+ /* Figure out how many columns the line/column needs. */
+ cols = vs_columns(sp, NULL, lno, cnop, NULL);
+
+ screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+ if (screens == 0)
+ screens = 1;
+
+ /* Cache the value. */
+ if (cnop == NULL) {
+ VIP(sp)->ss_lno = lno;
+ VIP(sp)->ss_screens = screens;
+ }
+ return (screens);
+}
+
+/*
+ * vs_columns --
+ * Return the screen columns necessary to display the line, or,
+ * if specified, the physical character column within the line.
+ *
+ * PUBLIC: size_t vs_columns __P((SCR *, char *, recno_t, size_t *, size_t *));
+ */
+size_t
+vs_columns(sp, lp, lno, cnop, diffp)
+ SCR *sp;
+ char *lp;
+ recno_t lno;
+ size_t *cnop, *diffp;
+{
+ size_t chlen, cno, curoff, last, len, scno;
+ int ch, leftright, listset;
+ char *p;
+
+ /* Need the line to go any further. */
+ if (lp == NULL) {
+ (void)db_get(sp, lno, 0, &lp, &len);
+ if (len == 0)
+ goto done;
+ }
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL) {
+done: if (diffp != NULL) /* XXX */
+ *diffp = 0;
+ return (0);
+ }
+
+ /* Store away the values of the list and leftright edit options. */
+ listset = O_ISSET(sp, O_LIST);
+ leftright = O_ISSET(sp, O_LEFTRIGHT);
+
+ /*
+ * Initialize the pointer into the buffer and screen and current
+ * offsets.
+ */
+ p = lp;
+ curoff = scno = 0;
+
+ /* Leading number if O_NUMBER option set. */
+ if (O_ISSET(sp, O_NUMBER))
+ scno += O_NUMBER_LENGTH;
+
+ /* Macro to return the display length of any signal character. */
+#define CHLEN(val) (ch = *(u_char *)p++) == '\t' && \
+ !listset ? TAB_OFF(val) : KEY_LEN(sp, ch);
+
+ /*
+ * If folding screens (the historic vi screen format), past the end
+ * of the current screen, and the character was a tab, reset the
+ * current screen column to 0, and the total screen columns to the
+ * last column of the screen. Otherwise, display the rest of the
+ * character in the next screen.
+ */
+#define TAB_RESET { \
+ curoff += chlen; \
+ if (!leftright && curoff >= sp->cols) \
+ if (ch == '\t') { \
+ curoff = 0; \
+ scno -= scno % sp->cols; \
+ } else \
+ curoff -= sp->cols; \
+}
+ if (cnop == NULL)
+ while (len--) {
+ chlen = CHLEN(curoff);
+ last = scno;
+ scno += chlen;
+ TAB_RESET;
+ }
+ else
+ for (cno = *cnop;; --cno) {
+ chlen = CHLEN(curoff);
+ last = scno;
+ scno += chlen;
+ TAB_RESET;
+ if (cno == 0)
+ break;
+ }
+
+ /* Add the trailing '$' if the O_LIST option set. */
+ if (listset && cnop == NULL)
+ scno += KEY_LEN(sp, '$');
+
+ /*
+ * The text input screen code needs to know how much additional
+ * room the last two characters required, so that it can handle
+ * tab character displays correctly.
+ */
+ if (diffp != NULL)
+ *diffp = scno - last;
+ return (scno);
+}
+
+/*
+ * vs_rcm --
+ * Return the physical column from the line that will display a
+ * character closest to the currently most attractive character
+ * position (which is stored as a screen column).
+ *
+ * PUBLIC: size_t vs_rcm __P((SCR *, recno_t, int));
+ */
+size_t
+vs_rcm(sp, lno, islast)
+ SCR *sp;
+ recno_t lno;
+ int islast;
+{
+ size_t len;
+
+ /* Last character is easy, and common. */
+ if (islast) {
+ if (db_get(sp, lno, 0, NULL, &len) || len == 0)
+ return (0);
+ return (len - 1);
+ }
+
+ /* First character is easy, and common. */
+ if (sp->rcm == 0)
+ return (0);
+
+ return (vs_colpos(sp, lno, sp->rcm));
+}
+
+/*
+ * vs_colpos --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column.
+ *
+ * PUBLIC: size_t vs_colpos __P((SCR *, recno_t, size_t));
+ */
+size_t
+vs_colpos(sp, lno, cno)
+ SCR *sp;
+ recno_t lno;
+ size_t cno;
+{
+ size_t chlen, curoff, len, llen, off, scno;
+ int ch, leftright, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ (void)db_get(sp, lno, 0, &lp, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ /* Store away the values of the list and leftright edit options. */
+ listset = O_ISSET(sp, O_LIST);
+ leftright = O_ISSET(sp, O_LEFTRIGHT);
+
+ /* Discard screen (logical) lines. */
+ off = cno / sp->cols;
+ cno %= sp->cols;
+ for (scno = 0, p = lp, len = llen; off--;) {
+ for (; len && scno < sp->cols; --len)
+ scno += CHLEN(scno);
+
+ /*
+ * If reached the end of the physical line, return the last
+ * physical character in the line.
+ */
+ if (len == 0)
+ return (llen - 1);
+
+ /*
+ * If folding screens (the historic vi screen format), past
+ * the end of the current screen, and the character was a tab,
+ * reset the current screen column to 0. Otherwise, the rest
+ * of the character is displayed in the next screen.
+ */
+ if (leftright && ch == '\t')
+ scno = 0;
+ else
+ scno -= sp->cols;
+ }
+
+ /* Step through the line until reach the right character or EOL. */
+ for (curoff = scno; len--;) {
+ chlen = CHLEN(curoff);
+
+ /*
+ * If we've reached the specific character, there are three
+ * cases.
+ *
+ * 1: scno == cno, i.e. the current character ends at the
+ * screen character we care about.
+ * a: off < llen - 1, i.e. not the last character in
+ * the line, return the offset of the next character.
+ * b: else return the offset of the last character.
+ * 2: scno != cno, i.e. this character overruns the character
+ * we care about, return the offset of this character.
+ */
+ if ((scno += chlen) >= cno) {
+ off = p - lp;
+ return (scno == cno ?
+ (off < llen - 1 ? off : llen - 1) : off - 1);
+ }
+
+ TAB_RESET;
+ }
+
+ /* No such character; return the start of the last character. */
+ return (llen - 1);
+}
diff --git a/contrib/nvi/vi/vs_smap.c b/contrib/nvi/vi/vs_smap.c
new file mode 100644
index 000000000000..af38057e815f
--- /dev/null
+++ b/contrib/nvi/vi/vs_smap.c
@@ -0,0 +1,1260 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_smap.c 10.25 (Berkeley) 7/12/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static int vs_deleteln __P((SCR *, int));
+static int vs_insertln __P((SCR *, int));
+static int vs_sm_delete __P((SCR *, recno_t));
+static int vs_sm_down __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
+static int vs_sm_erase __P((SCR *));
+static int vs_sm_insert __P((SCR *, recno_t));
+static int vs_sm_reset __P((SCR *, recno_t));
+static int vs_sm_up __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
+
+/*
+ * vs_change --
+ * Make a change to the screen.
+ *
+ * PUBLIC: int vs_change __P((SCR *, recno_t, lnop_t));
+ */
+int
+vs_change(sp, lno, op)
+ SCR *sp;
+ recno_t lno;
+ lnop_t op;
+{
+ VI_PRIVATE *vip;
+ SMAP *p;
+ size_t cnt, oldy, oldx;
+
+ vip = VIP(sp);
+
+ /*
+ * XXX
+ * 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 screen.
+ * The vi text input functions detect it explicitly and don't insert
+ * a new line.
+ *
+ * Check for line #2 before going to the end of the file.
+ */
+ if ((op == LINE_APPEND && lno == 0 || op == LINE_INSERT && lno == 1) &&
+ !db_exist(sp, 2)) {
+ lno = 1;
+ op = LINE_RESET;
+ }
+
+ /* 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, cnt = sp->t_rows; cnt--; ++p)
+ --p->lno;
+ if (sp->lno >= lno)
+ --sp->lno;
+ F_SET(vip, VIP_N_RENUMBER);
+ break;
+ case LINE_INSERT:
+ for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
+ ++p->lno;
+ if (sp->lno >= lno)
+ ++sp->lno;
+ F_SET(vip, VIP_N_RENUMBER);
+ break;
+ case LINE_RESET:
+ break;
+ }
+ return (0);
+ }
+
+ F_SET(vip, VIP_N_REFRESH);
+
+ /*
+ * Invalidate the line size cache, and invalidate the cursor if it's
+ * on this line,
+ */
+ VI_SCR_CFLUSH(vip);
+ if (sp->lno == lno)
+ F_SET(vip, VIP_CUR_INVALID);
+
+ /*
+ * If ex modifies the screen after ex output is already on the screen
+ * or if we've switched into ex canonical mode, don't touch it -- we'll
+ * get scrolling wrong, at best.
+ */
+ if (!F_ISSET(sp, SC_TINPUT_INFO) &&
+ (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
+ F_SET(vip, VIP_N_EX_REDRAW);
+ return (0);
+ }
+
+ /* Save and restore the cursor for these routines. */
+ (void)sp->gp->scr_cursor(sp, &oldy, &oldx);
+
+ switch (op) {
+ case LINE_DELETE:
+ if (vs_sm_delete(sp, lno))
+ return (1);
+ F_SET(vip, VIP_N_RENUMBER);
+ break;
+ case LINE_INSERT:
+ if (vs_sm_insert(sp, lno))
+ return (1);
+ F_SET(vip, VIP_N_RENUMBER);
+ break;
+ case LINE_RESET:
+ if (vs_sm_reset(sp, lno))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ (void)sp->gp->scr_move(sp, oldy, oldx);
+ return (0);
+}
+
+/*
+ * vs_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.
+ *
+ * PUBLIC: int vs_sm_fill __P((SCR *, recno_t, pos_t));
+ */
+int
+vs_sm_fill(sp, lno, pos)
+ SCR *sp;
+ recno_t lno;
+ pos_t pos;
+{
+ SMAP *p, tmp;
+ size_t cnt;
+
+ /* Flush all cached information from the SMAP. */
+ for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
+ SMAP_FLUSH(p);
+
+ /*
+ * If the map is filled, the screen must be redrawn.
+ *
+ * XXX
+ * This is a bug. We should try and figure out if the desired line
+ * is already in the map or close by -- scrolling the screen would
+ * be a lot better than redrawing.
+ */
+ F_SET(sp, SC_SCR_REDRAW);
+
+ switch (pos) {
+ case P_FILL:
+ tmp.lno = 1;
+ tmp.coff = 0;
+ tmp.soff = 1;
+
+ /* See if less than half a screen from the top. */
+ if (vs_sm_nlines(sp,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* See if less than half a screen from the bottom. */
+ if (db_last(sp, &tmp.lno))
+ return (1);
+ tmp.coff = 0;
+ tmp.soff = vs_screens(sp, tmp.lno, NULL);
+ if (vs_sm_nlines(sp,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ TMAP->lno = tmp.lno;
+ TMAP->coff = tmp.coff;
+ TMAP->soff = tmp.soff;
+ goto bottom;
+ }
+ goto middle;
+ case P_TOP:
+ if (lno != OOBLNO) {
+top: HMAP->lno = lno;
+ HMAP->coff = 0;
+ HMAP->soff = 1;
+ }
+ /* If we fail, just punt. */
+ for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
+ if (vs_sm_next(sp, p, p + 1))
+ goto err;
+ break;
+ case P_MIDDLE:
+ /* If we fail, guess that the file is too small. */
+middle: p = HMAP + sp->t_rows / 2;
+ p->lno = lno;
+ p->coff = 0;
+ p->soff = 1;
+ for (; p > HMAP; --p)
+ if (vs_sm_prev(sp, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* If we fail, just punt. */
+ p = HMAP + sp->t_rows / 2;
+ for (; p < TMAP; ++p)
+ if (vs_sm_next(sp, p, p + 1))
+ goto err;
+ break;
+ case P_BOTTOM:
+ if (lno != OOBLNO) {
+ TMAP->lno = lno;
+ TMAP->coff = 0;
+ TMAP->soff = vs_screens(sp, lno, NULL);
+ }
+ /* If we fail, guess that the file is too small. */
+bottom: for (p = TMAP; p > HMAP; --p)
+ if (vs_sm_prev(sp, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+
+ /*
+ * Try and put *something* on the screen. If this fails, we have a
+ * serious hard error.
+ */
+err: HMAP->lno = 1;
+ HMAP->coff = 0;
+ HMAP->soff = 1;
+ for (p = HMAP; p < TMAP; ++p)
+ if (vs_sm_next(sp, p, p + 1))
+ return (1);
+ return (0);
+}
+
+/*
+ * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
+ * screen contains only a single line (whether because the screen is small
+ * or the line large), it gets fairly exciting. Skip the fun, set a flag
+ * so the screen map is refilled and the screen redrawn, and return. This
+ * is amazingly slow, but it's not clear that anyone will care.
+ */
+#define HANDLE_WEIRDNESS(cnt) { \
+ if (cnt >= sp->t_rows) { \
+ F_SET(sp, SC_SCR_REFORMAT); \
+ return (0); \
+ } \
+}
+
+/*
+ * vs_sm_delete --
+ * Delete a line out of the SMAP.
+ */
+static int
+vs_sm_delete(sp, lno)
+ SCR *sp;
+ 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);
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ cnt_orig = 1;
+ else
+ for (cnt_orig = 1, t = p + 1;
+ t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+
+ HANDLE_WEIRDNESS(cnt_orig);
+
+ /* Delete that many lines from the screen. */
+ (void)sp->gp->scr_move(sp, p - HMAP, 0);
+ if (vs_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 && vs_sm_next(sp, p, p + 1))
+ return (1);
+ /* vs_sm_next() flushed the cache. */
+ if (vs_line(sp, ++p, NULL, NULL))
+ return (1);
+ if (p == TMAP)
+ break;
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_insert --
+ * Insert a line into the SMAP.
+ */
+static int
+vs_sm_insert(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig, cnt, coff;
+
+ /* Save the offset. */
+ coff = HMAP->coff;
+
+ /*
+ * 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 = vs_screens(sp, lno, NULL);
+ HANDLE_WEIRDNESS(cnt_orig);
+
+ /*
+ * 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. */
+ (void)sp->gp->scr_move(sp, p - HMAP, 0);
+ if (vs_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->coff = coff;
+ t->soff = cnt;
+ SMAP_FLUSH(t);
+ if (vs_line(sp, t, NULL, NULL))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_reset --
+ * Reset a line in the SMAP.
+ */
+static int
+vs_sm_reset(sp, lno)
+ SCR *sp;
+ 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);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t = p;
+ cnt_orig = cnt_new = 1;
+ } else {
+ for (cnt_orig = 0,
+ t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+ cnt_new = vs_screens(sp, lno, NULL);
+ }
+
+ HANDLE_WEIRDNESS(cnt_orig);
+
+ if (cnt_orig == cnt_new) {
+ do {
+ SMAP_FLUSH(p);
+ if (vs_line(sp, 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;
+
+ /* If there are any following lines, push them down. */
+ if (cnt > 1) {
+ (void)sp->gp->scr_move(sp, p - HMAP, 0);
+ if (vs_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->soff = cnt;
+ SMAP_FLUSH(t);
+ if (vs_line(sp, t, NULL, NULL))
+ return (1);
+ }
+ } else {
+ /* Get the difference. */
+ diff = cnt_orig - cnt_new;
+
+ /* Delete that many lines from the screen. */
+ (void)sp->gp->scr_move(sp, p - HMAP, 0);
+ if (vs_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->soff = cnt;
+ SMAP_FLUSH(t);
+ if (vs_line(sp, t, NULL, NULL))
+ return (1);
+ }
+
+ /* Display the new lines at the bottom of the screen. */
+ for (t = TMAP - diff;;) {
+ if (t < TMAP && vs_sm_next(sp, t, t + 1))
+ return (1);
+ /* vs_sm_next() flushed the cache. */
+ if (vs_line(sp, ++t, NULL, NULL))
+ return (1);
+ if (t == TMAP)
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_scroll
+ * Scroll the SMAP up/down count logical lines. Different
+ * semantics based on the vi command, *sigh*.
+ *
+ * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t));
+ */
+int
+vs_sm_scroll(sp, rp, count, scmd)
+ SCR *sp;
+ MARK *rp;
+ recno_t count;
+ scroll_t scmd;
+{
+ SMAP *smp;
+
+ /*
+ * Invalidate the cursor. The line is probably going to change,
+ * (although for ^E and ^Y it may not). In any case, the scroll
+ * routines move the cursor to draw things.
+ */
+ F_SET(VIP(sp), VIP_CUR_INVALID);
+
+ /* Find the cursor in the screen. */
+ if (vs_sm_cursor(sp, &smp))
+ return (1);
+
+ switch (scmd) {
+ case CNTRL_B:
+ case CNTRL_U:
+ case CNTRL_Y:
+ case Z_CARAT:
+ if (vs_sm_down(sp, rp, count, scmd, smp))
+ return (1);
+ break;
+ case CNTRL_D:
+ case CNTRL_E:
+ case CNTRL_F:
+ case Z_PLUS:
+ if (vs_sm_up(sp, rp, count, scmd, smp))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * !!!
+ * If we're at the start of a line, go for the first non-blank.
+ * This makes it look like the old vi, even though we're moving
+ * around by logical lines, not physical ones.
+ *
+ * XXX
+ * In the presence of a long line, which has more than a screen
+ * width of leading spaces, this code can cause a cursor warp.
+ * Live with it.
+ */
+ if (scmd != CNTRL_E && scmd != CNTRL_Y &&
+ rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * vs_sm_up --
+ * Scroll the SMAP up count logical lines.
+ */
+static int
+vs_sm_up(sp, rp, count, scmd, smp)
+ SCR *sp;
+ MARK *rp;
+ scroll_t scmd;
+ recno_t count;
+ SMAP *smp;
+{
+ int cursor_set, echanged, zset;
+ SMAP *ssmp, s1, s2;
+
+ /*
+ * Check to see if movement is possible.
+ *
+ * Get the line after the map. If that line is a new one (and if
+ * O_LEFTRIGHT option is set, this has to be true), and the next
+ * line doesn't exist, and the cursor doesn't move, or the cursor
+ * isn't even on the screen, or the cursor is already at the last
+ * line in the map, it's an error. If that test succeeded because
+ * the cursor wasn't at the end of the map, test to see if the map
+ * is mostly empty.
+ */
+ if (vs_sm_next(sp, TMAP, &s1))
+ return (1);
+ if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
+ if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
+ v_eof(sp, NULL);
+ return (1);
+ }
+ if (vs_sm_next(sp, smp, &s1))
+ return (1);
+ if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
+ v_eof(sp, NULL);
+ return (1);
+ }
+ }
+
+ /*
+ * Small screens: see vs_refresh.c section 6a.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves down one
+ * line for each line displayed. Otherwise, erase/compress and
+ * repaint, and move the cursor to the first line in the screen.
+ * Note, the ^F command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = 0;
+ if (IS_SMALL(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_F) {
+ s1 = TMAP[0];
+ if (vs_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (vs_sm_next(sp, &s1, &s2))
+ return (1);
+ if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
+ break;
+ }
+ TMAP[0] = s2;
+ if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
+ return (1);
+ return (vs_sm_position(sp, rp, 0, P_TOP));
+ }
+ cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (vs_sm_next(sp, TMAP, &s1))
+ return (1);
+ if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
+ break;
+ *++TMAP = s1;
+ /* vs_sm_next() flushed the cache. */
+ if (vs_line(sp, TMAP, NULL, NULL))
+ return (1);
+
+ if (!cursor_set)
+ ++ssmp;
+ }
+ if (!cursor_set) {
+ rp->lno = ssmp->lno;
+ rp->cno = ssmp->c_sboff;
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (echanged = zset = 0; count; --count) {
+ /* Decide what would show up on the screen. */
+ if (vs_sm_next(sp, TMAP, &s1))
+ return (1);
+
+ /* If the line doesn't exist, we're done. */
+ if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
+ break;
+
+ /* Scroll the screen cursor up one logical line. */
+ if (vs_sm_1up(sp))
+ return (1);
+ switch (scmd) {
+ case CNTRL_E:
+ if (smp > HMAP)
+ --smp;
+ else
+ echanged = 1;
+ break;
+ case Z_PLUS:
+ if (zset) {
+ if (smp > HMAP)
+ --smp;
+ } else {
+ smp = TMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_E:
+ /*
+ * On a ^E that was forced to change lines, 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 (echanged) {
+ rp->lno = smp->lno;
+ rp->cno = vs_colpos(sp, smp->lno,
+ (O_ISSET(sp, O_LEFTRIGHT) ?
+ smp->coff : (smp->soff - 1) * sp->cols) +
+ sp->rcm % sp->cols);
+ }
+ return (0);
+ case CNTRL_F:
+ /*
+ * If there are more lines, the ^F command is positioned at
+ * the first line of the screen.
+ */
+ if (!count) {
+ smp = HMAP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_D:
+ /*
+ * The ^D and ^F commands move the cursor towards EOF
+ * if there are more lines to move. Check to be sure
+ * the lines actually exist. (They may not if the
+ * file is smaller than the screen.)
+ */
+ for (; count; --count, ++smp)
+ if (smp == TMAP || !db_exist(sp, smp[1].lno))
+ break;
+ break;
+ case Z_PLUS:
+ /* The z+ command moves the cursor to the first new line. */
+ break;
+ default:
+ abort();
+ }
+
+ if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+ return (0);
+}
+
+/*
+ * vs_sm_1up --
+ * Scroll the SMAP up one.
+ *
+ * PUBLIC: int vs_sm_1up __P((SCR *));
+ */
+int
+vs_sm_1up(sp)
+ SCR *sp;
+{
+ /*
+ * Delete the top line of the screen. Shift the screen map
+ * up and display a new line at the bottom of the screen.
+ */
+ (void)sp->gp->scr_move(sp, 0, 0);
+ if (vs_deleteln(sp, 1))
+ return (1);
+
+ /* One-line screens can fail. */
+ if (IS_ONELINE(sp)) {
+ if (vs_sm_next(sp, TMAP, TMAP))
+ return (1);
+ } else {
+ memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
+ if (vs_sm_next(sp, TMAP - 1, TMAP))
+ return (1);
+ }
+ /* vs_sm_next() flushed the cache. */
+ return (vs_line(sp, TMAP, NULL, NULL));
+}
+
+/*
+ * vs_deleteln --
+ * Delete a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+vs_deleteln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ GS *gp;
+ size_t oldy, oldx;
+
+ gp = sp->gp;
+ if (IS_ONELINE(sp))
+ (void)gp->scr_clrtoeol(sp);
+ else {
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+ while (cnt--) {
+ (void)gp->scr_deleteln(sp);
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_insertln(sp);
+ (void)gp->scr_move(sp, oldy, oldx);
+ }
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_down --
+ * Scroll the SMAP down count logical lines.
+ */
+static int
+vs_sm_down(sp, rp, count, scmd, smp)
+ SCR *sp;
+ MARK *rp;
+ recno_t count;
+ SMAP *smp;
+ scroll_t scmd;
+{
+ SMAP *ssmp, s1, s2;
+ int cursor_set, ychanged, zset;
+
+ /* Check to see if movement is possible. */
+ if (HMAP->lno == 1 &&
+ (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
+ (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /*
+ * Small screens: see vs_refresh.c section 6a.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves up one
+ * line for each line displayed. Otherwise, erase/compress and
+ * repaint, and move the cursor to the first line in the screen.
+ * Note, the ^B command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = scmd == CNTRL_Y;
+ if (IS_SMALL(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_B) {
+ s1 = HMAP[0];
+ if (vs_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (vs_sm_prev(sp, &s1, &s2))
+ return (1);
+ if (s2.lno == 1 &&
+ (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
+ break;
+ }
+ HMAP[0] = s2;
+ if (vs_sm_fill(sp, OOBLNO, P_TOP))
+ return (1);
+ return (vs_sm_position(sp, rp, 0, P_BOTTOM));
+ }
+ cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (HMAP->lno == 1 &&
+ (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
+ break;
+ ++TMAP;
+ if (vs_sm_1down(sp))
+ return (1);
+ }
+ if (!cursor_set) {
+ rp->lno = ssmp->lno;
+ rp->cno = ssmp->c_sboff;
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (ychanged = zset = 0; count; --count) {
+ /* If the line doesn't exist, we're done. */
+ if (HMAP->lno == 1 &&
+ (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
+ break;
+
+ /* Scroll the screen and cursor down one logical line. */
+ if (vs_sm_1down(sp))
+ return (1);
+ switch (scmd) {
+ case CNTRL_Y:
+ if (smp < TMAP)
+ ++smp;
+ else
+ ychanged = 1;
+ break;
+ case Z_CARAT:
+ if (zset) {
+ if (smp < TMAP)
+ ++smp;
+ } else {
+ smp = HMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (scmd != CNTRL_Y && cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_B:
+ /*
+ * If there are more lines, the ^B command is positioned at
+ * the last line of the screen. However, the line may not
+ * exist.
+ */
+ if (!count) {
+ for (smp = TMAP; smp > HMAP; --smp)
+ if (db_exist(sp, smp->lno))
+ break;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_U:
+ /*
+ * The ^B and ^U commands move the cursor towards SOF
+ * if there are more lines to move.
+ */
+ if (count < smp - HMAP)
+ smp -= count;
+ else
+ smp = HMAP;
+ break;
+ case CNTRL_Y:
+ /*
+ * On a ^Y that was forced to change lines, 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 (ychanged) {
+ rp->lno = smp->lno;
+ rp->cno = vs_colpos(sp, smp->lno,
+ (O_ISSET(sp, O_LEFTRIGHT) ?
+ smp->coff : (smp->soff - 1) * sp->cols) +
+ sp->rcm % sp->cols);
+ }
+ return (0);
+ case Z_CARAT:
+ /* The z^ command moves the cursor to the first new line. */
+ break;
+ default:
+ abort();
+ }
+
+ if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+ return (0);
+}
+
+/*
+ * vs_sm_erase --
+ * Erase the small screen area for the scrolling functions.
+ */
+static int
+vs_sm_erase(sp)
+ SCR *sp;
+{
+ GS *gp;
+
+ gp = sp->gp;
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+ for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+ (void)gp->scr_move(sp, TMAP - HMAP, 0);
+ (void)gp->scr_clrtoeol(sp);
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_1down --
+ * Scroll the SMAP down one.
+ *
+ * PUBLIC: int vs_sm_1down __P((SCR *));
+ */
+int
+vs_sm_1down(sp)
+ SCR *sp;
+{
+ /*
+ * Insert a line at the top of the screen. Shift the screen map
+ * down and display a new line at the top of the screen.
+ */
+ (void)sp->gp->scr_move(sp, 0, 0);
+ if (vs_insertln(sp, 1))
+ return (1);
+
+ /* One-line screens can fail. */
+ if (IS_ONELINE(sp)) {
+ if (vs_sm_prev(sp, HMAP, HMAP))
+ return (1);
+ } else {
+ memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
+ if (vs_sm_prev(sp, HMAP + 1, HMAP))
+ return (1);
+ }
+ /* vs_sm_prev() flushed the cache. */
+ return (vs_line(sp, HMAP, NULL, NULL));
+}
+
+/*
+ * vs_insertln --
+ * Insert a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+vs_insertln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ GS *gp;
+ size_t oldy, oldx;
+
+ gp = sp->gp;
+ if (IS_ONELINE(sp)) {
+ (void)gp->scr_move(sp, LASTLINE(sp), 0);
+ (void)gp->scr_clrtoeol(sp);
+ } else {
+ (void)gp->scr_cursor(sp, &oldy, &oldx);
+ while (cnt--) {
+ (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
+ (void)gp->scr_deleteln(sp);
+ (void)gp->scr_move(sp, oldy, oldx);
+ (void)gp->scr_insertln(sp);
+ }
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_next --
+ * Fill in the next entry in the SMAP.
+ *
+ * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *));
+ */
+int
+vs_sm_next(sp, p, t)
+ SCR *sp;
+ SMAP *p, *t;
+{
+ size_t lcnt;
+
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno + 1;
+ t->coff = p->coff;
+ } else {
+ lcnt = vs_screens(sp, p->lno, NULL);
+ if (lcnt == p->soff) {
+ t->lno = p->lno + 1;
+ t->soff = 1;
+ } else {
+ t->lno = p->lno;
+ t->soff = p->soff + 1;
+ }
+ }
+ return (0);
+}
+
+/*
+ * vs_sm_prev --
+ * Fill in the previous entry in the SMAP.
+ *
+ * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
+ */
+int
+vs_sm_prev(sp, p, t)
+ SCR *sp;
+ SMAP *p, *t;
+{
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno - 1;
+ t->coff = p->coff;
+ } else {
+ if (p->soff != 1) {
+ t->lno = p->lno;
+ t->soff = p->soff - 1;
+ } else {
+ t->lno = p->lno - 1;
+ t->soff = vs_screens(sp, t->lno, NULL);
+ }
+ }
+ return (t->lno == 0);
+}
+
+/*
+ * vs_sm_cursor --
+ * Return the SMAP entry referenced by the cursor.
+ *
+ * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **));
+ */
+int
+vs_sm_cursor(sp, smpp)
+ SCR *sp;
+ SMAP **smpp;
+{
+ SMAP *p;
+
+ /* See if the cursor is not in the map. */
+ if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
+ return (1);
+
+ /* 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) {
+ *smpp = p;
+ return (0);
+ }
+ if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
+ return (1);
+ if (p->c_eboff >= sp->cno) {
+ *smpp = p;
+ return (0);
+ }
+ }
+
+ /* It was past the end of the map after all. */
+ return (1);
+}
+
+/*
+ * vs_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.
+ *
+ * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
+ */
+int
+vs_sm_position(sp, rp, cnt, pos)
+ SCR *sp;
+ MARK *rp;
+ u_long cnt;
+ pos_t pos;
+{
+ SMAP *smp;
+ recno_t last;
+
+ switch (pos) {
+ case P_TOP:
+ /*
+ * !!!
+ * Historically, an invalid count to the H command failed.
+ * We do nothing special here, just making sure that H in
+ * an empty screen works.
+ */
+ if (cnt > TMAP - HMAP)
+ goto sof;
+ smp = HMAP + cnt;
+ if (cnt && !db_exist(sp, smp->lno)) {
+sof: msgq(sp, M_BERR, "220|Movement past the end-of-screen");
+ return (1);
+ }
+ break;
+ case P_MIDDLE:
+ /*
+ * !!!
+ * Historically, a count to the M command was ignored.
+ * If the screen isn't filled, find the middle of what's
+ * real and move there.
+ */
+ if (!db_exist(sp, TMAP->lno)) {
+ if (db_last(sp, &last))
+ return (1);
+ for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
+ if (smp > HMAP)
+ smp -= (smp - HMAP) / 2;
+ } else
+ smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
+ break;
+ case P_BOTTOM:
+ /*
+ * !!!
+ * Historically, an invalid count to the L command failed.
+ * If the screen isn't filled, find the bottom of what's
+ * real and try to offset from there.
+ */
+ if (cnt > TMAP - HMAP)
+ goto eof;
+ smp = TMAP - cnt;
+ if (!db_exist(sp, smp->lno)) {
+ if (db_last(sp, &last))
+ return (1);
+ for (; smp->lno > last && smp > HMAP; --smp);
+ if (cnt > smp - HMAP) {
+eof: msgq(sp, M_BERR,
+ "221|Movement past the beginning-of-screen");
+ return (1);
+ }
+ smp -= cnt;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Make sure that the cached information is valid. */
+ if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+
+ return (0);
+}
+
+/*
+ * vs_sm_nlines --
+ * Return the number of screen lines from an SMAP entry to the
+ * start of some file line, less than a maximum value.
+ *
+ * PUBLIC: recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t));
+ */
+recno_t
+vs_sm_nlines(sp, from_sp, to_lno, max)
+ SCR *sp;
+ 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->soff - 1);
+
+ if (from_sp->lno > to_lno) {
+ lcnt = from_sp->soff - 1; /* Correct for off-by-one. */
+ for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
+ lcnt += vs_screens(sp, lno, NULL);
+ } else {
+ lno = from_sp->lno;
+ lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
+ for (; ++lno < to_lno && lcnt <= max;)
+ lcnt += vs_screens(sp, lno, NULL);
+ }
+ return (lcnt);
+}
diff --git a/contrib/nvi/vi/vs_split.c b/contrib/nvi/vi/vs_split.c
new file mode 100644
index 000000000000..d01735490e69
--- /dev/null
+++ b/contrib/nvi/vi/vs_split.c
@@ -0,0 +1,607 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * Keith Bostic. All rights reserved.
+ *
+ * See the LICENSE file for redistribution information.
+ */
+
+#include "config.h"
+
+#ifndef lint
+static const char sccsid[] = "@(#)vs_split.c 10.31 (Berkeley) 10/13/96";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/common.h"
+#include "vi.h"
+
+static SCR *vs_getbg __P((SCR *, char *));
+
+/*
+ * vs_split --
+ * Create a new screen.
+ *
+ * PUBLIC: int vs_split __P((SCR *, SCR *, int));
+ */
+int
+vs_split(sp, new, ccl)
+ SCR *sp, *new;
+ int ccl; /* Colon-command line split. */
+{
+ GS *gp;
+ SMAP *smp;
+ size_t half;
+ int issmallscreen, splitup;
+
+ gp = sp->gp;
+
+ /* Check to see if it's possible. */
+ /* XXX: The IS_ONELINE fix will change this, too. */
+ if (sp->rows < 4) {
+ msgq(sp, M_ERR,
+ "222|Screen must be larger than %d lines to split", 4 - 1);
+ return (1);
+ }
+
+ /* Wait for any messages in the screen. */
+ vs_resolve(sp, NULL, 1);
+
+ half = sp->rows / 2;
+ if (ccl && half > 6)
+ half = 6;
+
+ /* Get a new screen map. */
+ CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ if (_HMAP(new) == NULL)
+ return (1);
+ _HMAP(new)->lno = sp->lno;
+ _HMAP(new)->coff = 0;
+ _HMAP(new)->soff = 1;
+
+ /*
+ * Small screens: see vs_refresh.c section 6a. Set a flag so
+ * we know to fix the screen up later.
+ */
+ issmallscreen = IS_SMALL(sp);
+
+ /* The columns in the screen don't change. */
+ new->cols = sp->cols;
+
+ /*
+ * Split the screen, and link the screens together. If creating a
+ * screen to edit the colon command line or 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.
+ *
+ * Recalculate current cursor position based on sp->lno, we're called
+ * with the cursor on the colon command line. Then split the screen
+ * in half and update the shared information.
+ */
+ splitup =
+ !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (smp - HMAP) + 1) >= half;
+ if (splitup) { /* Old is bottom half. */
+ new->rows = sp->rows - half; /* New. */
+ new->woff = sp->woff;
+ sp->rows = half; /* Old. */
+ sp->woff += new->rows;
+ /* Link in before old. */
+ CIRCLEQ_INSERT_BEFORE(&gp->dq, sp, new, q);
+
+ /*
+ * If the parent is the bottom half of the screen, shift
+ * the map down to match on-screen text.
+ */
+ memmove(_HMAP(sp), _HMAP(sp) + new->rows,
+ (sp->t_maxrows - new->rows) * sizeof(SMAP));
+ } else { /* Old is top half. */
+ new->rows = half; /* New. */
+ sp->rows -= half; /* Old. */
+ new->woff = sp->woff + sp->rows;
+ /* Link in after old. */
+ CIRCLEQ_INSERT_AFTER(&gp->dq, sp, new, q);
+ }
+
+ /* Adjust maximum text count. */
+ sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
+ new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
+
+ /*
+ * Small screens: see vs_refresh.c, section 6a.
+ *
+ * The child may have different screen options sizes than the parent,
+ * so use them. Guarantee that 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 -= new->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. */
+ new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
+ if (new->t_rows > new->t_maxrows)
+ new->t_rows = new->t_maxrows;
+ if (new->t_minrows > new->t_maxrows)
+ new->t_minrows = new->t_maxrows;
+ } else {
+ sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
+
+ /*
+ * The new screen may be a small screen, even if 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.
+ */
+ new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
+ if (new->t_rows > new->rows - 1)
+ new->t_minrows = new->t_rows =
+ IS_ONELINE(new) ? 1 : new->rows - 1;
+ }
+
+ /* Adjust the ends of the new and old maps. */
+ _TMAP(sp) = IS_ONELINE(sp) ?
+ _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
+ _TMAP(new) = IS_ONELINE(new) ?
+ _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
+
+ /* Reset the length of the default scroll. */
+ if ((sp->defscroll = sp->t_maxrows / 2) == 0)
+ sp->defscroll = 1;
+ if ((new->defscroll = new->t_maxrows / 2) == 0)
+ new->defscroll = 1;
+
+ /*
+ * Initialize the screen flags:
+ *
+ * If we're in vi mode in one screen, we don't have to reinitialize.
+ * This isn't just a cosmetic fix. The path goes like this:
+ *
+ * return into vi(), SC_SSWITCH set
+ * call vs_refresh() with SC_STATUS set
+ * call vs_resolve to display the status message
+ * call vs_refresh() because the SC_SCR_VI bit isn't set
+ *
+ * Things go downhill at this point.
+ *
+ * Draw the new screen from scratch, and add a status line.
+ */
+ F_SET(new,
+ SC_SCR_REFORMAT | SC_STATUS |
+ F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
+ return (0);
+}
+
+/*
+ * vs_discard --
+ * Discard the screen, folding the real-estate into a related screen,
+ * if one exists, and return that screen.
+ *
+ * PUBLIC: int vs_discard __P((SCR *, SCR **));
+ */
+int
+vs_discard(sp, spp)
+ SCR *sp, **spp;
+{
+ SCR *nsp;
+ dir_t dir;
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), and the underlying file was a tmp
+ * file, it may have gone away.
+ */
+ if (sp->frp != NULL) {
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+
+ /*
+ * Add into a previous screen and then into a subsequent screen, as
+ * they're the closest to the current screen. If that doesn't work,
+ * there was no screen to join.
+ */
+ if ((nsp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
+ nsp->rows += sp->rows;
+ sp = nsp;
+ dir = FORWARD;
+ } else if ((nsp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
+ nsp->woff = sp->woff;
+ nsp->rows += sp->rows;
+ sp = nsp;
+ dir = BACKWARD;
+ } else
+ sp = NULL;
+
+ if (spp != NULL)
+ *spp = sp;
+ if (sp == NULL)
+ return (0);
+
+ /*
+ * Make no effort to clean up the discarded screen's information. If
+ * it's not exiting, we'll do the work when the user redisplays it.
+ *
+ * Small screens: see vs_refresh.c section 6a. Adjust text line info,
+ * unless it's a small screen.
+ *
+ * Reset the length of the default scroll.
+ */
+ if (!IS_SMALL(sp))
+ sp->t_rows = sp->t_minrows = sp->rows - 1;
+ sp->t_maxrows = sp->rows - 1;
+ sp->defscroll = sp->t_maxrows / 2;
+ *(HMAP + (sp->t_rows - 1)) = *TMAP;
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ /*
+ * Draw the new screen from scratch, and add a status line.
+ *
+ * XXX
+ * We could play games with the map, if this were ever to be a
+ * performance problem, but I wrote the code a few times and it
+ * was never clean or easy.
+ */
+ switch (dir) {
+ case FORWARD:
+ vs_sm_fill(sp, OOBLNO, P_TOP);
+ break;
+ case BACKWARD:
+ vs_sm_fill(sp, OOBLNO, P_BOTTOM);
+ break;
+ default:
+ abort();
+ }
+
+ F_SET(sp, SC_STATUS);
+ return (0);
+}
+
+/*
+ * vs_fg --
+ * Background the current screen, and foreground a new one.
+ *
+ * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
+ */
+int
+vs_fg(sp, nspp, name, newscreen)
+ SCR *sp, **nspp;
+ CHAR_T *name;
+ int newscreen;
+{
+ GS *gp;
+ SCR *nsp;
+
+ gp = sp->gp;
+
+ if (newscreen)
+ /* Get the specified background screen. */
+ nsp = vs_getbg(sp, name);
+ else
+ /* Swap screens. */
+ if (vs_swap(sp, &nsp, name))
+ return (1);
+
+ if ((*nspp = nsp) == NULL) {
+ msgq_str(sp, M_ERR, name,
+ name == NULL ?
+ "223|There are no background screens" :
+ "224|There's no background screen editing a file named %s");
+ return (1);
+ }
+
+ if (newscreen) {
+ /* Remove the new screen from the background queue. */
+ CIRCLEQ_REMOVE(&gp->hq, nsp, q);
+
+ /* Split the screen; if we fail, hook the screen back in. */
+ if (vs_split(sp, nsp, 0)) {
+ CIRCLEQ_INSERT_TAIL(&gp->hq, nsp, q);
+ return (1);
+ }
+ } else {
+ /* Move the old screen to the background queue. */
+ CIRCLEQ_REMOVE(&gp->dq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&gp->hq, sp, q);
+ }
+ return (0);
+}
+
+/*
+ * vs_bg --
+ * Background the screen, and switch to the next one.
+ *
+ * PUBLIC: int vs_bg __P((SCR *));
+ */
+int
+vs_bg(sp)
+ SCR *sp;
+{
+ GS *gp;
+ SCR *nsp;
+
+ gp = sp->gp;
+
+ /* Try and join with another screen. */
+ if (vs_discard(sp, &nsp))
+ return (1);
+ if (nsp == NULL) {
+ msgq(sp, M_ERR,
+ "225|You may not background your only displayed screen");
+ return (1);
+ }
+
+ /* Move the old screen to the background queue. */
+ CIRCLEQ_REMOVE(&gp->dq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&gp->hq, sp, q);
+
+ /* Toss the screen map. */
+ free(_HMAP(sp));
+ _HMAP(sp) = NULL;
+
+ /* Switch screens. */
+ sp->nextdisp = nsp;
+ F_SET(sp, SC_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * vs_swap --
+ * Swap the current screen with a backgrounded one.
+ *
+ * PUBLIC: int vs_swap __P((SCR *, SCR **, char *));
+ */
+int
+vs_swap(sp, nspp, name)
+ SCR *sp, **nspp;
+ char *name;
+{
+ GS *gp;
+ SCR *nsp;
+
+ gp = sp->gp;
+
+ /* Get the specified background screen. */
+ if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
+ return (0);
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), and the underlying file was a tmp
+ * file, it may have gone away.
+ */
+ if (sp->frp != NULL) {
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+
+ /* Switch screens. */
+ sp->nextdisp = nsp;
+ F_SET(sp, SC_SSWITCH);
+
+ /* Initialize terminal information. */
+ VIP(nsp)->srows = VIP(sp)->srows;
+
+ /* Initialize screen information. */
+ nsp->cols = sp->cols;
+ nsp->rows = sp->rows; /* XXX: Only place in vi that sets rows. */
+ nsp->woff = sp->woff;
+
+ /*
+ * Small screens: see vs_refresh.c, section 6a.
+ *
+ * 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 (IS_SMALL(nsp)) {
+ nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
+ if (nsp->t_rows > sp->t_maxrows)
+ nsp->t_rows = nsp->t_maxrows;
+ if (nsp->t_minrows > sp->t_maxrows)
+ nsp->t_minrows = nsp->t_maxrows;
+ } else
+ nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
+
+ /* Reset the length of the default scroll. */
+ nsp->defscroll = nsp->t_maxrows / 2;
+
+ /* Allocate a new screen map. */
+ CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP));
+ _TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
+
+ /* Fill the map. */
+ if (vs_sm_fill(nsp, nsp->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 background queue.
+ */
+ CIRCLEQ_REMOVE(&gp->hq, nsp, q);
+ CIRCLEQ_INSERT_AFTER(&gp->dq, sp, nsp, q);
+
+ /*
+ * Don't change the screen's cursor information other than to
+ * note that the cursor is wrong.
+ */
+ F_SET(VIP(nsp), VIP_CUR_INVALID);
+
+ /* Draw the new screen from scratch, and add a status line. */
+ F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
+ return (0);
+}
+
+/*
+ * vs_resize --
+ * Change the absolute size of the current screen.
+ *
+ * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
+ */
+int
+vs_resize(sp, count, adj)
+ SCR *sp;
+ long count;
+ adj_t adj;
+{
+ GS *gp;
+ SCR *g, *s;
+ size_t g_off, s_off;
+
+ gp = sp->gp;
+
+ /*
+ * Figure out which screens will grow, which will shrink, and
+ * make sure it's possible.
+ */
+ if (count == 0)
+ return (0);
+ if (adj == A_SET) {
+ if (sp->t_maxrows == count)
+ return (0);
+ if (sp->t_maxrows > count) {
+ adj = A_DECREASE;
+ count = sp->t_maxrows - count;
+ } else {
+ adj = A_INCREASE;
+ count = count - sp->t_maxrows;
+ }
+ }
+
+ g_off = s_off = 0;
+ if (adj == A_DECREASE) {
+ if (count < 0)
+ count = -count;
+ s = sp;
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ goto toosmall;
+ if ((g = sp->q.cqe_prev) == (void *)&gp->dq) {
+ if ((g = sp->q.cqe_next) == (void *)&gp->dq)
+ goto toobig;
+ g_off = -count;
+ } else
+ s_off = count;
+ } else {
+ g = sp;
+ if ((s = sp->q.cqe_next) != (void *)&gp->dq)
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ s = NULL;
+ else
+ s_off = count;
+ else
+ s = NULL;
+ if (s == NULL) {
+ if ((s = sp->q.cqe_prev) == (void *)&gp->dq) {
+toobig: msgq(sp, M_BERR, adj == A_DECREASE ?
+ "227|The screen cannot shrink" :
+ "228|The screen cannot grow");
+ return (1);
+ }
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
+toosmall: msgq(sp, M_BERR,
+ "226|The screen can only shrink to %d rows",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+ g_off = -count;
+ }
+ }
+
+ /*
+ * Fix up the screens; we could optimize the reformatting of the
+ * screen, but this isn't likely to be a common enough operation
+ * to make it worthwhile.
+ */
+ s->rows += -count;
+ s->woff += s_off;
+ g->rows += count;
+ g->woff += g_off;
+
+ g->t_rows += count;
+ if (g->t_minrows == g->t_maxrows)
+ g->t_minrows += count;
+ g->t_maxrows += count;
+ _TMAP(g) += count;
+ F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
+
+ s->t_rows -= count;
+ s->t_maxrows -= count;
+ if (s->t_minrows > s->t_maxrows)
+ s->t_minrows = s->t_maxrows;
+ _TMAP(s) -= count;
+ F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
+
+ return (0);
+}
+
+/*
+ * vs_getbg --
+ * Get the specified background screen, or, if name is NULL, the first
+ * background screen.
+ */
+static SCR *
+vs_getbg(sp, name)
+ SCR *sp;
+ char *name;
+{
+ GS *gp;
+ SCR *nsp;
+ char *p;
+
+ gp = sp->gp;
+
+ /* If name is NULL, return the first background screen on the list. */
+ if (name == NULL) {
+ nsp = gp->hq.cqh_first;
+ return (nsp == (void *)&gp->hq ? NULL : nsp);
+ }
+
+ /* Search for a full match. */
+ for (nsp = gp->hq.cqh_first;
+ nsp != (void *)&gp->hq; nsp = nsp->q.cqe_next)
+ if (!strcmp(nsp->frp->name, name))
+ break;
+ if (nsp != (void *)&gp->hq)
+ return (nsp);
+
+ /* Search for a last-component match. */
+ for (nsp = gp->hq.cqh_first;
+ nsp != (void *)&gp->hq; nsp = nsp->q.cqe_next) {
+ if ((p = strrchr(nsp->frp->name, '/')) == NULL)
+ p = nsp->frp->name;
+ else
+ ++p;
+ if (!strcmp(p, name))
+ break;
+ }
+ if (nsp != (void *)&gp->hq)
+ return (nsp);
+
+ return (NULL);
+}