From 59cc89c2c2e686da3bdab2d5cfac4f33462d29fe Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 1 Nov 1996 06:45:43 +0000 Subject: Import of nvi-1.79, minus a few bits that we dont need (eg: postscript files, curses, db, regex etc that we already have). The other glue will follow shortly. Obtained from: Keith Bostic --- contrib/nvi/FAQ | 160 + contrib/nvi/LAYOUT | 128 + contrib/nvi/LICENSE | 40 + contrib/nvi/README | 113 + contrib/nvi/build/ExtUtils/Embed.pm | 473 +++ contrib/nvi/build/Makefile.in | 630 ++++ contrib/nvi/build/README | 369 +++ contrib/nvi/build/README.LynxOS | 320 ++ contrib/nvi/build/acconfig.h | 82 + contrib/nvi/build/aclocal.m4 | 17 + contrib/nvi/build/config.guess | 571 ++++ contrib/nvi/build/config.h.in | 179 ++ contrib/nvi/build/config.sub | 872 +++++ contrib/nvi/build/configure | 4446 ++++++++++++++++++++++++++ contrib/nvi/build/configure.in | 725 +++++ contrib/nvi/build/distrib | 84 + contrib/nvi/build/install-sh | 238 ++ contrib/nvi/build/pathnames.h.in | 45 + contrib/nvi/build/port.h.in | 185 ++ contrib/nvi/build/recover.in | 49 + contrib/nvi/build/spell.ok | 58 + contrib/nvi/catalog/Makefile | 84 + contrib/nvi/catalog/README | 166 + contrib/nvi/catalog/dump.c | 114 + contrib/nvi/catalog/dutch | 317 ++ contrib/nvi/catalog/dutch.base | 307 ++ contrib/nvi/catalog/dutch.check | 37 + contrib/nvi/catalog/dutch.owner | 1 + contrib/nvi/catalog/english | 317 ++ contrib/nvi/catalog/english.base | 309 ++ contrib/nvi/catalog/english.check | 36 + contrib/nvi/catalog/english.owner | 1 + contrib/nvi/catalog/french | 317 ++ contrib/nvi/catalog/french.base | 309 ++ contrib/nvi/catalog/french.check | 34 + contrib/nvi/catalog/german | 317 ++ contrib/nvi/catalog/german.base | 307 ++ contrib/nvi/catalog/german.check | 36 + contrib/nvi/catalog/german.owner | 1 + contrib/nvi/catalog/ru_RU.KOI8-R | 267 ++ contrib/nvi/catalog/ru_RU.KOI8-R.base | 219 ++ contrib/nvi/catalog/ru_RU.KOI8-R.check | 169 + contrib/nvi/catalog/ru_RU.KOI8-R.owner | 1 + contrib/nvi/catalog/ru_SU.KOI8-R | 267 ++ contrib/nvi/catalog/ru_SU.KOI8-R.base | 219 ++ contrib/nvi/catalog/ru_SU.KOI8-R.check | 169 + contrib/nvi/catalog/ru_SU.KOI8-R.owner | 1 + contrib/nvi/catalog/spanish | 317 ++ contrib/nvi/catalog/spanish.base | 309 ++ contrib/nvi/catalog/spanish.check | 35 + contrib/nvi/catalog/spell.ok | 19 + contrib/nvi/catalog/swedish | 317 ++ contrib/nvi/catalog/swedish.base | 307 ++ contrib/nvi/catalog/swedish.check | 34 + contrib/nvi/catalog/swedish.owner | 1 + contrib/nvi/cl/README.signal | 174 + contrib/nvi/cl/cl.h | 78 + contrib/nvi/cl/cl_bsd.c | 345 ++ contrib/nvi/cl/cl_funcs.c | 704 ++++ contrib/nvi/cl/cl_main.c | 471 +++ contrib/nvi/cl/cl_read.c | 334 ++ contrib/nvi/cl/cl_screen.c | 581 ++++ contrib/nvi/cl/cl_term.c | 459 +++ contrib/nvi/clib/bsearch.c | 90 + contrib/nvi/clib/env.c | 160 + contrib/nvi/clib/gethostname.c | 22 + contrib/nvi/clib/getopt.c | 130 + contrib/nvi/clib/memchr.c | 65 + contrib/nvi/clib/memmove.c | 147 + contrib/nvi/clib/memset.c | 137 + contrib/nvi/clib/mkstemp.c | 133 + contrib/nvi/clib/mmap.c | 50 + contrib/nvi/clib/snprintf.c | 45 + contrib/nvi/clib/strdup.c | 63 + contrib/nvi/clib/strerror.c | 74 + contrib/nvi/clib/strpbrk.c | 62 + contrib/nvi/clib/strsep.c | 85 + contrib/nvi/clib/strtol.c | 134 + contrib/nvi/clib/strtoul.c | 113 + contrib/nvi/clib/vsnprintf.c | 31 + contrib/nvi/common/api.c | 525 +++ contrib/nvi/common/args.h | 29 + contrib/nvi/common/common.h | 96 + contrib/nvi/common/cut.c | 368 +++ contrib/nvi/common/cut.h | 77 + contrib/nvi/common/delete.c | 160 + contrib/nvi/common/exf.c | 1498 +++++++++ contrib/nvi/common/exf.h | 82 + contrib/nvi/common/gs.h | 210 ++ contrib/nvi/common/key.c | 865 +++++ contrib/nvi/common/key.h | 222 ++ contrib/nvi/common/line.c | 576 ++++ contrib/nvi/common/log.c | 717 +++++ contrib/nvi/common/log.h | 20 + contrib/nvi/common/main.c | 617 ++++ contrib/nvi/common/mark.c | 277 ++ contrib/nvi/common/mark.h | 42 + contrib/nvi/common/mem.h | 168 + contrib/nvi/common/msg.c | 895 ++++++ contrib/nvi/common/msg.h | 65 + contrib/nvi/common/options.awk | 9 + contrib/nvi/common/options.c | 1141 +++++++ contrib/nvi/common/options.h | 101 + contrib/nvi/common/options_f.c | 367 +++ contrib/nvi/common/put.c | 231 ++ contrib/nvi/common/recover.c | 878 +++++ contrib/nvi/common/screen.c | 233 ++ contrib/nvi/common/screen.h | 203 ++ contrib/nvi/common/search.c | 492 +++ contrib/nvi/common/seq.c | 395 +++ contrib/nvi/common/seq.h | 44 + contrib/nvi/common/util.c | 230 ++ contrib/nvi/common/util.h | 56 + contrib/nvi/docs/TODO | 147 + contrib/nvi/docs/USD.doc/edit/Makefile | 11 + contrib/nvi/docs/USD.doc/edit/edit.vindex | 115 + contrib/nvi/docs/USD.doc/edit/edittut.ms | 2280 +++++++++++++ contrib/nvi/docs/USD.doc/exref/Makefile | 17 + contrib/nvi/docs/USD.doc/exref/ex.rm | 2213 +++++++++++++ contrib/nvi/docs/USD.doc/exref/ex.summary | 730 +++++ contrib/nvi/docs/USD.doc/vi.man/Makefile | 16 + contrib/nvi/docs/USD.doc/vi.man/spell.ok | 179 ++ contrib/nvi/docs/USD.doc/vi.man/vi.1 | 1608 ++++++++++ contrib/nvi/docs/USD.doc/vi.ref/Makefile | 32 + contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff | 1924 +++++++++++ contrib/nvi/docs/USD.doc/vi.ref/index.so | 260 ++ contrib/nvi/docs/USD.doc/vi.ref/merge.awk | 16 + contrib/nvi/docs/USD.doc/vi.ref/ref.so | 103 + contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff | 1303 ++++++++ contrib/nvi/docs/USD.doc/vi.ref/spell.ok | 414 +++ contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff | 3085 ++++++++++++++++++ contrib/nvi/docs/USD.doc/vi.ref/vi.ref | 1840 +++++++++++ contrib/nvi/docs/USD.doc/vitut/Makefile | 22 + contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms | 1081 +++++++ contrib/nvi/docs/USD.doc/vitut/vi.chars | 645 ++++ contrib/nvi/docs/USD.doc/vitut/vi.in | 2074 ++++++++++++ contrib/nvi/docs/USD.doc/vitut/vi.summary | 468 +++ contrib/nvi/docs/changelog | 1102 +++++++ contrib/nvi/docs/ev | 55 + contrib/nvi/docs/features | 83 + contrib/nvi/docs/help | 229 ++ contrib/nvi/docs/internals/autowrite | 88 + contrib/nvi/docs/internals/context | 32 + contrib/nvi/docs/internals/cscope.NOTES | 142 + contrib/nvi/docs/internals/gdb.script | 76 + contrib/nvi/docs/internals/input | 350 ++ contrib/nvi/docs/internals/openmode | 36 + contrib/nvi/docs/internals/quoting | 208 ++ contrib/nvi/docs/internals/structures | 68 + contrib/nvi/docs/interp/interp | 190 ++ contrib/nvi/docs/interp/spell.ok | 46 + contrib/nvi/docs/spell.ok | 173 + contrib/nvi/docs/tutorial/vi.advanced | 1458 +++++++++ contrib/nvi/docs/tutorial/vi.beginner | 741 +++++ contrib/nvi/docs/tutorial/vi.tut.csh | 24 + contrib/nvi/ex/ex.awk | 6 + contrib/nvi/ex/ex.c | 2370 ++++++++++++++ contrib/nvi/ex/ex.h | 228 ++ contrib/nvi/ex/ex_abbrev.c | 117 + contrib/nvi/ex/ex_append.c | 277 ++ contrib/nvi/ex/ex_args.c | 327 ++ contrib/nvi/ex/ex_argv.c | 756 +++++ contrib/nvi/ex/ex_at.c | 126 + contrib/nvi/ex/ex_bang.c | 186 ++ contrib/nvi/ex/ex_cd.c | 129 + contrib/nvi/ex/ex_cmd.c | 457 +++ contrib/nvi/ex/ex_cscope.c | 1057 ++++++ contrib/nvi/ex/ex_delete.c | 65 + contrib/nvi/ex/ex_display.c | 145 + contrib/nvi/ex/ex_edit.c | 153 + contrib/nvi/ex/ex_equal.c | 59 + contrib/nvi/ex/ex_file.c | 80 + contrib/nvi/ex/ex_filter.c | 316 ++ contrib/nvi/ex/ex_global.c | 328 ++ contrib/nvi/ex/ex_init.c | 417 +++ contrib/nvi/ex/ex_join.c | 177 + contrib/nvi/ex/ex_map.c | 121 + contrib/nvi/ex/ex_mark.c | 45 + contrib/nvi/ex/ex_mkexrc.c | 101 + contrib/nvi/ex/ex_move.c | 198 ++ contrib/nvi/ex/ex_open.c | 46 + contrib/nvi/ex/ex_perl.c | 69 + contrib/nvi/ex/ex_preserve.c | 103 + contrib/nvi/ex/ex_print.c | 352 ++ contrib/nvi/ex/ex_put.c | 51 + contrib/nvi/ex/ex_quit.c | 46 + contrib/nvi/ex/ex_read.c | 360 +++ contrib/nvi/ex/ex_screen.c | 138 + contrib/nvi/ex/ex_script.c | 798 +++++ contrib/nvi/ex/ex_set.c | 46 + contrib/nvi/ex/ex_shell.c | 378 +++ contrib/nvi/ex/ex_shift.c | 191 ++ contrib/nvi/ex/ex_source.c | 85 + contrib/nvi/ex/ex_stop.c | 51 + contrib/nvi/ex/ex_subst.c | 1459 +++++++++ contrib/nvi/ex/ex_tag.c | 1324 ++++++++ contrib/nvi/ex/ex_tcl.c | 80 + contrib/nvi/ex/ex_txt.c | 430 +++ contrib/nvi/ex/ex_undo.c | 77 + contrib/nvi/ex/ex_usage.c | 196 ++ contrib/nvi/ex/ex_util.c | 234 ++ contrib/nvi/ex/ex_version.c | 39 + contrib/nvi/ex/ex_visual.c | 161 + contrib/nvi/ex/ex_write.c | 375 +++ contrib/nvi/ex/ex_yank.c | 46 + contrib/nvi/ex/ex_z.c | 150 + contrib/nvi/ex/script.h | 23 + contrib/nvi/ex/tag.h | 107 + contrib/nvi/ex/version.h | 2 + contrib/nvi/include/bitstring.h | 143 + contrib/nvi/include/cl_extern.h | 56 + contrib/nvi/include/com_extern.h | 199 ++ contrib/nvi/include/ex_def.h | 78 + contrib/nvi/include/ex_extern.h | 127 + contrib/nvi/include/ip_extern.h | 23 + contrib/nvi/include/options_def.h | 79 + contrib/nvi/include/perl_extern.h | 8 + contrib/nvi/include/sys/queue.h | 259 ++ contrib/nvi/include/tcl_extern.h | 1 + contrib/nvi/include/tk_extern.h | 29 + contrib/nvi/include/vi_extern.h | 142 + contrib/nvi/ip/IP_INSTRUCTIONS | 41 + contrib/nvi/ip/ip.h | 92 + contrib/nvi/ip/ip_funcs.c | 443 +++ contrib/nvi/ip/ip_main.c | 165 + contrib/nvi/ip/ip_read.c | 307 ++ contrib/nvi/ip/ip_screen.c | 87 + contrib/nvi/ip/ip_term.c | 108 + contrib/nvi/ip_cl/Makefile | 20 + contrib/nvi/ip_cl/ip_cl.c | 742 +++++ contrib/nvi/perl_api/VI.pod | 218 ++ contrib/nvi/perl_api/nviperl.pod | 43 + contrib/nvi/perl_api/perl.xs | 1115 +++++++ contrib/nvi/perl_api/perlsfio.c | 85 + contrib/nvi/perl_api/typemap | 42 + contrib/nvi/perl_scripts/forall.pl | 10 + contrib/nvi/perl_scripts/make.pl | 27 + contrib/nvi/perl_scripts/tk.pl | 20 + contrib/nvi/perl_scripts/wc.pl | 11 + contrib/nvi/tcl_api/tcl.c | 852 +++++ contrib/nvi/tcl_scripts/errors.tcl | 44 + contrib/nvi/tcl_scripts/gnats.tcl | 95 + contrib/nvi/tcl_scripts/mailprocs.tcl | 115 + contrib/nvi/tcl_scripts/wc.tcl | 16 + contrib/nvi/tk/init.tcl | 1096 +++++++ contrib/nvi/tk/tk_funcs.c | 346 ++ contrib/nvi/tk/tk_main.c | 423 +++ contrib/nvi/tk/tk_read.c | 207 ++ contrib/nvi/tk/tk_screen.c | 86 + contrib/nvi/tk/tk_term.c | 169 + contrib/nvi/tk/tk_util.c | 250 ++ contrib/nvi/tk/tki.h | 64 + contrib/nvi/vi/getc.c | 234 ++ contrib/nvi/vi/v_at.c | 109 + contrib/nvi/vi/v_ch.c | 295 ++ contrib/nvi/vi/v_cmd.c | 505 +++ contrib/nvi/vi/v_delete.c | 107 + contrib/nvi/vi/v_ex.c | 696 ++++ contrib/nvi/vi/v_increment.c | 267 ++ contrib/nvi/vi/v_init.c | 126 + contrib/nvi/vi/v_itxt.c | 537 ++++ contrib/nvi/vi/v_left.c | 293 ++ contrib/nvi/vi/v_mark.c | 234 ++ contrib/nvi/vi/v_match.c | 170 + contrib/nvi/vi/v_paragraph.c | 344 ++ contrib/nvi/vi/v_put.c | 146 + contrib/nvi/vi/v_redraw.c | 39 + contrib/nvi/vi/v_replace.c | 203 ++ contrib/nvi/vi/v_right.c | 145 + contrib/nvi/vi/v_screen.c | 65 + contrib/nvi/vi/v_scroll.c | 474 +++ contrib/nvi/vi/v_search.c | 515 +++ contrib/nvi/vi/v_section.c | 252 ++ contrib/nvi/vi/v_sentence.c | 359 +++ contrib/nvi/vi/v_status.c | 41 + contrib/nvi/vi/v_txt.c | 2950 +++++++++++++++++ contrib/nvi/vi/v_ulcase.c | 179 ++ contrib/nvi/vi/v_undo.c | 139 + contrib/nvi/vi/v_util.c | 180 ++ contrib/nvi/vi/v_word.c | 547 ++++ contrib/nvi/vi/v_xchar.c | 107 + contrib/nvi/vi/v_yank.c | 82 + contrib/nvi/vi/v_z.c | 149 + contrib/nvi/vi/v_zexit.c | 54 + contrib/nvi/vi/vi.c | 1251 ++++++++ contrib/nvi/vi/vi.h | 377 +++ contrib/nvi/vi/vs_line.c | 514 +++ contrib/nvi/vi/vs_msg.c | 927 ++++++ contrib/nvi/vi/vs_refresh.c | 885 +++++ contrib/nvi/vi/vs_relative.c | 305 ++ contrib/nvi/vi/vs_smap.c | 1260 ++++++++ contrib/nvi/vi/vs_split.c | 607 ++++ 292 files changed, 98515 insertions(+) create mode 100644 contrib/nvi/FAQ create mode 100644 contrib/nvi/LAYOUT create mode 100644 contrib/nvi/LICENSE create mode 100644 contrib/nvi/README create mode 100644 contrib/nvi/build/ExtUtils/Embed.pm create mode 100644 contrib/nvi/build/Makefile.in create mode 100644 contrib/nvi/build/README create mode 100644 contrib/nvi/build/README.LynxOS create mode 100644 contrib/nvi/build/acconfig.h create mode 100644 contrib/nvi/build/aclocal.m4 create mode 100755 contrib/nvi/build/config.guess create mode 100644 contrib/nvi/build/config.h.in create mode 100755 contrib/nvi/build/config.sub create mode 100755 contrib/nvi/build/configure create mode 100644 contrib/nvi/build/configure.in create mode 100644 contrib/nvi/build/distrib create mode 100755 contrib/nvi/build/install-sh create mode 100644 contrib/nvi/build/pathnames.h.in create mode 100644 contrib/nvi/build/port.h.in create mode 100644 contrib/nvi/build/recover.in create mode 100644 contrib/nvi/build/spell.ok create mode 100644 contrib/nvi/catalog/Makefile create mode 100644 contrib/nvi/catalog/README create mode 100644 contrib/nvi/catalog/dump.c create mode 100644 contrib/nvi/catalog/dutch create mode 100644 contrib/nvi/catalog/dutch.base create mode 100644 contrib/nvi/catalog/dutch.check create mode 100644 contrib/nvi/catalog/dutch.owner create mode 100644 contrib/nvi/catalog/english create mode 100644 contrib/nvi/catalog/english.base create mode 100644 contrib/nvi/catalog/english.check create mode 100644 contrib/nvi/catalog/english.owner create mode 100644 contrib/nvi/catalog/french create mode 100644 contrib/nvi/catalog/french.base create mode 100644 contrib/nvi/catalog/french.check create mode 100644 contrib/nvi/catalog/german create mode 100644 contrib/nvi/catalog/german.base create mode 100644 contrib/nvi/catalog/german.check create mode 100644 contrib/nvi/catalog/german.owner create mode 100644 contrib/nvi/catalog/ru_RU.KOI8-R create mode 100644 contrib/nvi/catalog/ru_RU.KOI8-R.base create mode 100644 contrib/nvi/catalog/ru_RU.KOI8-R.check create mode 100644 contrib/nvi/catalog/ru_RU.KOI8-R.owner create mode 100644 contrib/nvi/catalog/ru_SU.KOI8-R create mode 100644 contrib/nvi/catalog/ru_SU.KOI8-R.base create mode 100644 contrib/nvi/catalog/ru_SU.KOI8-R.check create mode 100644 contrib/nvi/catalog/ru_SU.KOI8-R.owner create mode 100644 contrib/nvi/catalog/spanish create mode 100644 contrib/nvi/catalog/spanish.base create mode 100644 contrib/nvi/catalog/spanish.check create mode 100644 contrib/nvi/catalog/spell.ok create mode 100644 contrib/nvi/catalog/swedish create mode 100644 contrib/nvi/catalog/swedish.base create mode 100644 contrib/nvi/catalog/swedish.check create mode 100644 contrib/nvi/catalog/swedish.owner create mode 100644 contrib/nvi/cl/README.signal create mode 100644 contrib/nvi/cl/cl.h create mode 100644 contrib/nvi/cl/cl_bsd.c create mode 100644 contrib/nvi/cl/cl_funcs.c create mode 100644 contrib/nvi/cl/cl_main.c create mode 100644 contrib/nvi/cl/cl_read.c create mode 100644 contrib/nvi/cl/cl_screen.c create mode 100644 contrib/nvi/cl/cl_term.c create mode 100644 contrib/nvi/clib/bsearch.c create mode 100644 contrib/nvi/clib/env.c create mode 100644 contrib/nvi/clib/gethostname.c create mode 100644 contrib/nvi/clib/getopt.c create mode 100644 contrib/nvi/clib/memchr.c create mode 100644 contrib/nvi/clib/memmove.c create mode 100644 contrib/nvi/clib/memset.c create mode 100644 contrib/nvi/clib/mkstemp.c create mode 100644 contrib/nvi/clib/mmap.c create mode 100644 contrib/nvi/clib/snprintf.c create mode 100644 contrib/nvi/clib/strdup.c create mode 100644 contrib/nvi/clib/strerror.c create mode 100644 contrib/nvi/clib/strpbrk.c create mode 100644 contrib/nvi/clib/strsep.c create mode 100644 contrib/nvi/clib/strtol.c create mode 100644 contrib/nvi/clib/strtoul.c create mode 100644 contrib/nvi/clib/vsnprintf.c create mode 100644 contrib/nvi/common/api.c create mode 100644 contrib/nvi/common/args.h create mode 100644 contrib/nvi/common/common.h create mode 100644 contrib/nvi/common/cut.c create mode 100644 contrib/nvi/common/cut.h create mode 100644 contrib/nvi/common/delete.c create mode 100644 contrib/nvi/common/exf.c create mode 100644 contrib/nvi/common/exf.h create mode 100644 contrib/nvi/common/gs.h create mode 100644 contrib/nvi/common/key.c create mode 100644 contrib/nvi/common/key.h create mode 100644 contrib/nvi/common/line.c create mode 100644 contrib/nvi/common/log.c create mode 100644 contrib/nvi/common/log.h create mode 100644 contrib/nvi/common/main.c create mode 100644 contrib/nvi/common/mark.c create mode 100644 contrib/nvi/common/mark.h create mode 100644 contrib/nvi/common/mem.h create mode 100644 contrib/nvi/common/msg.c create mode 100644 contrib/nvi/common/msg.h create mode 100644 contrib/nvi/common/options.awk create mode 100644 contrib/nvi/common/options.c create mode 100644 contrib/nvi/common/options.h create mode 100644 contrib/nvi/common/options_f.c create mode 100644 contrib/nvi/common/put.c create mode 100644 contrib/nvi/common/recover.c create mode 100644 contrib/nvi/common/screen.c create mode 100644 contrib/nvi/common/screen.h create mode 100644 contrib/nvi/common/search.c create mode 100644 contrib/nvi/common/seq.c create mode 100644 contrib/nvi/common/seq.h create mode 100644 contrib/nvi/common/util.c create mode 100644 contrib/nvi/common/util.h create mode 100644 contrib/nvi/docs/TODO create mode 100644 contrib/nvi/docs/USD.doc/edit/Makefile create mode 100644 contrib/nvi/docs/USD.doc/edit/edit.vindex create mode 100644 contrib/nvi/docs/USD.doc/edit/edittut.ms create mode 100644 contrib/nvi/docs/USD.doc/exref/Makefile create mode 100644 contrib/nvi/docs/USD.doc/exref/ex.rm create mode 100644 contrib/nvi/docs/USD.doc/exref/ex.summary create mode 100644 contrib/nvi/docs/USD.doc/vi.man/Makefile create mode 100644 contrib/nvi/docs/USD.doc/vi.man/spell.ok create mode 100644 contrib/nvi/docs/USD.doc/vi.man/vi.1 create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/Makefile create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/ex.cmd.roff create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/index.so create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/merge.awk create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/ref.so create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/set.opt.roff create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/spell.ok create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/vi.cmd.roff create mode 100644 contrib/nvi/docs/USD.doc/vi.ref/vi.ref create mode 100644 contrib/nvi/docs/USD.doc/vitut/Makefile create mode 100644 contrib/nvi/docs/USD.doc/vitut/vi.apwh.ms create mode 100644 contrib/nvi/docs/USD.doc/vitut/vi.chars create mode 100644 contrib/nvi/docs/USD.doc/vitut/vi.in create mode 100644 contrib/nvi/docs/USD.doc/vitut/vi.summary create mode 100644 contrib/nvi/docs/changelog create mode 100644 contrib/nvi/docs/ev create mode 100644 contrib/nvi/docs/features create mode 100644 contrib/nvi/docs/help create mode 100644 contrib/nvi/docs/internals/autowrite create mode 100644 contrib/nvi/docs/internals/context create mode 100644 contrib/nvi/docs/internals/cscope.NOTES create mode 100644 contrib/nvi/docs/internals/gdb.script create mode 100644 contrib/nvi/docs/internals/input create mode 100644 contrib/nvi/docs/internals/openmode create mode 100644 contrib/nvi/docs/internals/quoting create mode 100644 contrib/nvi/docs/internals/structures create mode 100644 contrib/nvi/docs/interp/interp create mode 100644 contrib/nvi/docs/interp/spell.ok create mode 100644 contrib/nvi/docs/spell.ok create mode 100644 contrib/nvi/docs/tutorial/vi.advanced create mode 100644 contrib/nvi/docs/tutorial/vi.beginner create mode 100755 contrib/nvi/docs/tutorial/vi.tut.csh create mode 100644 contrib/nvi/ex/ex.awk create mode 100644 contrib/nvi/ex/ex.c create mode 100644 contrib/nvi/ex/ex.h create mode 100644 contrib/nvi/ex/ex_abbrev.c create mode 100644 contrib/nvi/ex/ex_append.c create mode 100644 contrib/nvi/ex/ex_args.c create mode 100644 contrib/nvi/ex/ex_argv.c create mode 100644 contrib/nvi/ex/ex_at.c create mode 100644 contrib/nvi/ex/ex_bang.c create mode 100644 contrib/nvi/ex/ex_cd.c create mode 100644 contrib/nvi/ex/ex_cmd.c create mode 100644 contrib/nvi/ex/ex_cscope.c create mode 100644 contrib/nvi/ex/ex_delete.c create mode 100644 contrib/nvi/ex/ex_display.c create mode 100644 contrib/nvi/ex/ex_edit.c create mode 100644 contrib/nvi/ex/ex_equal.c create mode 100644 contrib/nvi/ex/ex_file.c create mode 100644 contrib/nvi/ex/ex_filter.c create mode 100644 contrib/nvi/ex/ex_global.c create mode 100644 contrib/nvi/ex/ex_init.c create mode 100644 contrib/nvi/ex/ex_join.c create mode 100644 contrib/nvi/ex/ex_map.c create mode 100644 contrib/nvi/ex/ex_mark.c create mode 100644 contrib/nvi/ex/ex_mkexrc.c create mode 100644 contrib/nvi/ex/ex_move.c create mode 100644 contrib/nvi/ex/ex_open.c create mode 100644 contrib/nvi/ex/ex_perl.c create mode 100644 contrib/nvi/ex/ex_preserve.c create mode 100644 contrib/nvi/ex/ex_print.c create mode 100644 contrib/nvi/ex/ex_put.c create mode 100644 contrib/nvi/ex/ex_quit.c create mode 100644 contrib/nvi/ex/ex_read.c create mode 100644 contrib/nvi/ex/ex_screen.c create mode 100644 contrib/nvi/ex/ex_script.c create mode 100644 contrib/nvi/ex/ex_set.c create mode 100644 contrib/nvi/ex/ex_shell.c create mode 100644 contrib/nvi/ex/ex_shift.c create mode 100644 contrib/nvi/ex/ex_source.c create mode 100644 contrib/nvi/ex/ex_stop.c create mode 100644 contrib/nvi/ex/ex_subst.c create mode 100644 contrib/nvi/ex/ex_tag.c create mode 100644 contrib/nvi/ex/ex_tcl.c create mode 100644 contrib/nvi/ex/ex_txt.c create mode 100644 contrib/nvi/ex/ex_undo.c create mode 100644 contrib/nvi/ex/ex_usage.c create mode 100644 contrib/nvi/ex/ex_util.c create mode 100644 contrib/nvi/ex/ex_version.c create mode 100644 contrib/nvi/ex/ex_visual.c create mode 100644 contrib/nvi/ex/ex_write.c create mode 100644 contrib/nvi/ex/ex_yank.c create mode 100644 contrib/nvi/ex/ex_z.c create mode 100644 contrib/nvi/ex/script.h create mode 100644 contrib/nvi/ex/tag.h create mode 100644 contrib/nvi/ex/version.h create mode 100644 contrib/nvi/include/bitstring.h create mode 100644 contrib/nvi/include/cl_extern.h create mode 100644 contrib/nvi/include/com_extern.h create mode 100644 contrib/nvi/include/ex_def.h create mode 100644 contrib/nvi/include/ex_extern.h create mode 100644 contrib/nvi/include/ip_extern.h create mode 100644 contrib/nvi/include/options_def.h create mode 100644 contrib/nvi/include/perl_extern.h create mode 100644 contrib/nvi/include/sys/queue.h create mode 100644 contrib/nvi/include/tcl_extern.h create mode 100644 contrib/nvi/include/tk_extern.h create mode 100644 contrib/nvi/include/vi_extern.h create mode 100644 contrib/nvi/ip/IP_INSTRUCTIONS create mode 100644 contrib/nvi/ip/ip.h create mode 100644 contrib/nvi/ip/ip_funcs.c create mode 100644 contrib/nvi/ip/ip_main.c create mode 100644 contrib/nvi/ip/ip_read.c create mode 100644 contrib/nvi/ip/ip_screen.c create mode 100644 contrib/nvi/ip/ip_term.c create mode 100644 contrib/nvi/ip_cl/Makefile create mode 100644 contrib/nvi/ip_cl/ip_cl.c create mode 100644 contrib/nvi/perl_api/VI.pod create mode 100644 contrib/nvi/perl_api/nviperl.pod create mode 100644 contrib/nvi/perl_api/perl.xs create mode 100644 contrib/nvi/perl_api/perlsfio.c create mode 100644 contrib/nvi/perl_api/typemap create mode 100644 contrib/nvi/perl_scripts/forall.pl create mode 100644 contrib/nvi/perl_scripts/make.pl create mode 100644 contrib/nvi/perl_scripts/tk.pl create mode 100644 contrib/nvi/perl_scripts/wc.pl create mode 100644 contrib/nvi/tcl_api/tcl.c create mode 100644 contrib/nvi/tcl_scripts/errors.tcl create mode 100644 contrib/nvi/tcl_scripts/gnats.tcl create mode 100644 contrib/nvi/tcl_scripts/mailprocs.tcl create mode 100644 contrib/nvi/tcl_scripts/wc.tcl create mode 100644 contrib/nvi/tk/init.tcl create mode 100644 contrib/nvi/tk/tk_funcs.c create mode 100644 contrib/nvi/tk/tk_main.c create mode 100644 contrib/nvi/tk/tk_read.c create mode 100644 contrib/nvi/tk/tk_screen.c create mode 100644 contrib/nvi/tk/tk_term.c create mode 100644 contrib/nvi/tk/tk_util.c create mode 100644 contrib/nvi/tk/tki.h create mode 100644 contrib/nvi/vi/getc.c create mode 100644 contrib/nvi/vi/v_at.c create mode 100644 contrib/nvi/vi/v_ch.c create mode 100644 contrib/nvi/vi/v_cmd.c create mode 100644 contrib/nvi/vi/v_delete.c create mode 100644 contrib/nvi/vi/v_ex.c create mode 100644 contrib/nvi/vi/v_increment.c create mode 100644 contrib/nvi/vi/v_init.c create mode 100644 contrib/nvi/vi/v_itxt.c create mode 100644 contrib/nvi/vi/v_left.c create mode 100644 contrib/nvi/vi/v_mark.c create mode 100644 contrib/nvi/vi/v_match.c create mode 100644 contrib/nvi/vi/v_paragraph.c create mode 100644 contrib/nvi/vi/v_put.c create mode 100644 contrib/nvi/vi/v_redraw.c create mode 100644 contrib/nvi/vi/v_replace.c create mode 100644 contrib/nvi/vi/v_right.c create mode 100644 contrib/nvi/vi/v_screen.c create mode 100644 contrib/nvi/vi/v_scroll.c create mode 100644 contrib/nvi/vi/v_search.c create mode 100644 contrib/nvi/vi/v_section.c create mode 100644 contrib/nvi/vi/v_sentence.c create mode 100644 contrib/nvi/vi/v_status.c create mode 100644 contrib/nvi/vi/v_txt.c create mode 100644 contrib/nvi/vi/v_ulcase.c create mode 100644 contrib/nvi/vi/v_undo.c create mode 100644 contrib/nvi/vi/v_util.c create mode 100644 contrib/nvi/vi/v_word.c create mode 100644 contrib/nvi/vi/v_xchar.c create mode 100644 contrib/nvi/vi/v_yank.c create mode 100644 contrib/nvi/vi/v_z.c create mode 100644 contrib/nvi/vi/v_zexit.c create mode 100644 contrib/nvi/vi/vi.c create mode 100644 contrib/nvi/vi/vi.h create mode 100644 contrib/nvi/vi/vs_line.c create mode 100644 contrib/nvi/vi/vs_msg.c create mode 100644 contrib/nvi/vi/vs_refresh.c create mode 100644 contrib/nvi/vi/vs_relative.c create mode 100644 contrib/nvi/vi/vs_smap.c create mode 100644 contrib/nvi/vi/vs_split.c (limited to 'contrib') diff --git a/contrib/nvi/FAQ b/contrib/nvi/FAQ new file mode 100644 index 0000000..357650a --- /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 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 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 0000000..e3a55eb --- /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 0000000..78e8f4a --- /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 0000000..33db36a --- /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 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 0000000..04525c1 --- /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 < +#include + +#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) = ; 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 will invoke ExtUtils::Embed +functions while building your application. + +=head1 @EXPORT + +ExtUtils::Embed exports the following functions: + +L, L, L, L, L, +L, L, L, L + +=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> (Defaults to B) + +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 + +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 function that glues the perl B function +to the C B function and writes it to a file named "xsinit.c". + +Note that B is a special case where it must call B directly. + + perl -MExtUtils::Embed -e xsinit + + +This will generate code for linking with B and +each static extension found in B<$Config{static_ext}>. +The code is written to the default file name B. + + + 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 and B. + +If you have a working B 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> + +Search path for ModuleName.a archives. +Default path is B<@INC>. +Library archives are expected to be found as +B +For example, when looking for B relative to a search path, +we should find B + +When looking for B relative to a search path, +we should find B + +Keep in mind, you can always supply B +as an additional linker argument. + +B<--> + +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 + +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, B 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 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 extension. + + + perl -MExtUtils::Embed -e ldopts -- DynaLoader + + +This will print arguments for linking with just the B extension +and B. + + + 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 macro as +B along with #including B and B. + +=item xsi_protos(@modules) + +This function returns a string of B prototypes for each @modules. + +=item xsi_body(@modules) + +This function returns a string of calls to B that glue the module B +function to B for each @modules. + +B uses the xsi_* functions to generate most of it's code. + +=head1 EXAMPLES + +For examples on how to use B for building C/C++ applications +with embedded perl, see the eg/ directory and the I man page. + +=head1 SEE ALSO + +the I man page + +=head1 AUTHOR + +Doug MacEachern + +Based on ideas from Tim Bunce and +B by Andreas Koenig and Tim Bunce. + +=cut + diff --git a/contrib/nvi/build/Makefile.in b/contrib/nvi/build/Makefile.in new file mode 100644 index 0000000..54025e7 --- /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 0000000..efbce2b --- /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 0000000..2cc68da --- /dev/null +++ b/contrib/nvi/build/README.LynxOS @@ -0,0 +1,320 @@ +README.LynxOS +============= + +Written by Ronald F. Guilmette + +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 +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. , , and ) +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 + +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 0000000..567f9ee --- /dev/null +++ b/contrib/nvi/build/acconfig.h @@ -0,0 +1,82 @@ +/* @(#)acconfig.h 8.18 (Berkeley) 7/2/96 */ + +/* Define to `int' if 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 */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have */ +#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 0000000..de7e57e --- /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 0000000..4c314d9 --- /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 . +# 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 + + 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 + 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 </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' /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 < +# include +#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 + 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 0000000..c87fcdd --- /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 . */ +#undef HAVE_VFORK_H + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your 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 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 */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have */ +#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 0000000..43f0867 --- /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 0000000..e2055d1 --- /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 <&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 </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 < conftest.$ac_ext </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 < +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 < +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 +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 <&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 <&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 <&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 <&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 +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 +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 < +#include +#include +#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* + 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 +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 +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 < +#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 < +#if STDC_HEADERS +#include +#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 < +#include +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 < +#include +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 </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 <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 < +#include +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 < +#if STDC_HEADERS +#include +#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 < +#if STDC_HEADERS +#include +#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 < +#if STDC_HEADERS +#include +#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 < +#if STDC_HEADERS +#include +#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 < +#include +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 < +/* 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 <&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 < +/* 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 < +/* 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 <&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 < +/* 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 < +/* 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 <&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 < +/* 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 < +/* 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 <&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 < +/* 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 <&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 < +/* 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 <&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 < +/* 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 <&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 < +/* 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 <&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 < +#include +#include + +#ifndef HAVE_GETPAGESIZE +# include +# 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 +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 < +/* 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 < +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_VFORK_H +#include +#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 , + but some compilers (e.g. gcc -O) don't grok . + 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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +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 < +#include +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 <> 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 <> 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 </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 < +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 < +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 < +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 < +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 < +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 </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 < +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 </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 </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 < +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 </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 </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 < +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 </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 </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 < +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 </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 </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 </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 < 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 <> $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 <> $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 0000000..cb8e463 --- /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== 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 ], + [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 ], + [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 ], [flock(0, 0);], + [vi_cv_lock=flock]);; + esac + if test "$vi_cv_lock" = none; then + AC_TRY_LINK([#include ], [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 ], [ftruncate(0, 0);], + [vi_cv_ftruncate=ftruncate], +AC_TRY_LINK([#include ], [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 ], [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 ], [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 ], [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 ], [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 ], [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 ], [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 ], [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 ], [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 +#include ], [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 ], 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 ], 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 ], 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 ], 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 ], 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 ], 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 ], 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 ], 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 ], 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 0000000..45dbe52 --- /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 0000000..ab74c88 --- /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 0000000..09cf974 --- /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 0000000..6696848 --- /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 + */ +#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 0000000..cfaf75f --- /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 0000000..fc103f4 --- /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 0000000..1044051 --- /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 0000000..15a7063 --- /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_.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 +.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 0000000..0b3cd26 --- /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 +#include + +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 0000000..de9fb60 --- /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 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 0000000..2d8667e --- /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 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 0000000..d651aec --- /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 0000000..27f47d3 --- /dev/null +++ b/contrib/nvi/catalog/dutch.owner @@ -0,0 +1 @@ +J.G. Vons diff --git a/contrib/nvi/catalog/english b/contrib/nvi/catalog/english new file mode 100644 index 0000000..f34451d --- /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 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 0000000..6d8d7bf --- /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 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 0000000..5109378 --- /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 0000000..034567ddd --- /dev/null +++ b/contrib/nvi/catalog/english.owner @@ -0,0 +1 @@ +Keith Bostic diff --git a/contrib/nvi/catalog/french b/contrib/nvi/catalog/french new file mode 100644 index 0000000..a75c486 --- /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 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 0000000..db2d45f --- /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 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 0000000..1e53d93 --- /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 0000000..0810ba6c --- /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 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 0000000..135b72d --- /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 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 0000000..4fd60c1 --- /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 0000000..e72e8ae --- /dev/null +++ b/contrib/nvi/catalog/german.owner @@ -0,0 +1 @@ +Bernhard Daeubler diff --git a/contrib/nvi/catalog/ru_RU.KOI8-R b/contrib/nvi/catalog/ru_RU.KOI8-R new file mode 100644 index 0000000..7eb35b9 --- /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 0000000..59efd3f --- /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 0000000..ca77b89 --- /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 0000000..bf4fa18 --- /dev/null +++ b/contrib/nvi/catalog/ru_RU.KOI8-R.owner @@ -0,0 +1 @@ +Dima Ruban diff --git a/contrib/nvi/catalog/ru_SU.KOI8-R b/contrib/nvi/catalog/ru_SU.KOI8-R new file mode 100644 index 0000000..7eb35b9 --- /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 0000000..59efd3f --- /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 0000000..ca77b89 --- /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 0000000..bf4fa18 --- /dev/null +++ b/contrib/nvi/catalog/ru_SU.KOI8-R.owner @@ -0,0 +1 @@ +Dima Ruban diff --git a/contrib/nvi/catalog/spanish b/contrib/nvi/catalog/spanish new file mode 100644 index 0000000..fb67ee9 --- /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 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 0000000..270199e --- /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 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 0000000..c04785e --- /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 0000000..00be471 --- /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 0000000..044b354 --- /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 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 0000000..43bf776 --- /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 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 0000000..c70b9bb --- /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 0000000..2b36f2d --- /dev/null +++ b/contrib/nvi/catalog/swedish.owner @@ -0,0 +1 @@ +Jan Djarv diff --git a/contrib/nvi/cl/README.signal b/contrib/nvi/cl/README.signal new file mode 100644 index 0000000..7faa456 --- /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^C, i.e. the sequence may end up being ^V^C. + 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 0000000..2ef2b8d --- /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 0000000..4a06a54 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * , 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 0000000..40315ee --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * 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 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 0000000..2889f70 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..8a95a77 --- /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 +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 character terminating a line of + * input is returned in the buffer, but a trailing character is + * not similarly included. As ex uses 0 and ^ as autoindent + * commands, it has to see the trailing characters to determine + * the difference between the user entering "0ab" and "0ab". We + * leave an extra slot in the buffer, so that we can add a trailing + * character if the buffer isn't terminated by a . We + * lose if the buffer is too small for the line and exactly N characters + * are entered followed by an 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 0000000..2ce58e8 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , or + * 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 to 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 0000000..e400740 --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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' keys send single + * 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 0000000..6b41ab5 --- /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 +#include + +#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 0000000..5a45dc1 --- /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 +#include + +/* + * __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 0000000..5b8e85a --- /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 +#include + +#include + +/* + * 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 0000000..b173017 --- /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 +#include +#include + +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 0000000..b6d4c31 --- /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 + +/* + * 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 0000000..7f43762 --- /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 + +/* + * 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 0000000..0013d66 --- /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 + +#include +#include + +/* + * 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 0000000..1faffddf --- /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 +#include +#include +#include +#include +#include + +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 0000000..7cea169 --- /dev/null +++ b/contrib/nvi/clib/mmap.c @@ -0,0 +1,50 @@ +#include "config.h" + +#include + +#include +#include + +/* + * 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 0000000..935522c --- /dev/null +++ b/contrib/nvi/clib/snprintf.c @@ -0,0 +1,45 @@ +#include "config.h" + +#include + +#include + +#ifdef __STDC__ +#include +#else +#include +#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 0000000..4bc2303 --- /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 + +#include +#include +#include + +/* + * 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 0000000..cf5910d --- /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 + +/* + * 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 0000000..22abbb0 --- /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 + +/* + * 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 0000000..33115f0 --- /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 +#include + +/* + * 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 0000000..50c724f --- /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 +#include +#include +#include + +/* + * 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 0000000..890bdbd --- /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 +#include +#include +#include + +/* + * 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 0000000..a1b013a --- /dev/null +++ b/contrib/nvi/clib/vsnprintf.c @@ -0,0 +1,31 @@ +#include "config.h" + +#include + +#include + +#ifdef __STDC__ +#include +#else +#include +#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 0000000..35d9f0c --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..e84dc2c --- /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 0000000..0e13fc8 --- /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 +#include + +/* + * 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 0000000..faceecd --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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`, + * 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 0000000..43f3ca8 --- /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 . + * TERM_CR: User entered ; no data. + * TERM_ESC: User entered ; 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 0000000..001788f --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..2993b0f --- /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 +#include /* XXX: param.h may not have included types.h */ +#include +#include + +/* + * We include , because the flock(2) and open(2) #defines + * were found there on historical systems. We also include + * because the open(2) #defines are found there on newer systems. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..cdfaa82 --- /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 0000000..e5a43a6 --- /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 +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 0000000..e1311ab --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "d21G", when the user probably wanted + * "d21G". So, if a map starts off with a digit we continue as + * before, otherwise, we pretend we haven't mapped the character, and return + * . + * + * 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. + * + * !!! + * characters are a problem. Cursor keys start with + * characters, so there's almost always a map in place that begins with + * an character. If we timeout keys in the same way + * that we timeout other keys, the user will get a noticeable pause as + * they enter 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 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 0000000..76fb64f --- /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 0000000..bcb9e0c --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#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 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 0000000..9a9fe79 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..df30731 --- /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 0000000..6fb2ed1 --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "+" into "-c". + * 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 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 0000000..0ac1fc2 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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'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 0000000..9c63e18 --- /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 0000000..af42e6b --- /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 0000000..2b18082 --- /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 +#include /* XXX: param.h may not have included types.h */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#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 0000000..b10f4cc --- /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 0000000..0c91f07 --- /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 0000000..973778c --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..2646dc3 --- /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 0000000..ea3c611 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..8c0ca4b --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..f3abaab --- /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 +#include /* XXX: param.h may not have included types.h */ +#include +#include + +/* + * We include , because the open #defines were found there + * on historical systems. We also include because the open(2) + * #defines are found there on newer systems. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * and . 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 . + */ +#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 . */ + 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 0000000..ba9e287 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..bb7254f --- /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 0000000..3fd2719 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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?$"). 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 0000000..e2be879 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..984bb6c --- /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 0000000..5a4422a --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#else +#include +#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 0000000..46edb4a --- /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 0000000..6fe8829 --- /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. + + moves to the next field + 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 0000000..0c59f6d --- /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 0000000..2098f14 --- /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 0000000..8a9d66e --- /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 0000000..11b5423 --- /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 0000000..217bad4 --- /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 0000000..83084a3 --- /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) 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 +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 0000000..5433864 --- /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 0000000..80ebcab --- /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 0000000..22aee3e --- /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 key. +(Key names are written using less-than and greater-than signs, e.g. + 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 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''. +The command you should enter as soon as you start editing is +``:set verbose showmode''. +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 +The cursor arrow keys should work, too. +.TP +.B /text +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 +Once you've entered input mode using the one of the +.BR \&a , +.BR \&i , +.BR \&O +or +.B \&o +commands, use +.B +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 +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" +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 +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! +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] " +Search forward +.I count +times for the current word. +.TP +.B "[count] " +Page backwards +.I count +screens. +.TP +.B "[count] " +Scroll forward +.I count +lines. +.TP +.B "[count] " +Scroll forward +.I count +lines, leaving the current line and column as is, if possible. +.TP +.B "[count] " +Page forward +.I count +screens. +.TP +.B "" +Display the file information. +.TP +.B "" +.TP +.B "[count] h" +Move the cursor back +.I count +characters in the current line. +.TP +.B "[count] " +.TP +.B "[count] " +.TP +.B "[count] j" +Move the cursor down +.I count +lines without changing the current column. +.TP +.B "" +.TP +.B "" +Repaint the screen. +.TP +.B "[count] " +.TP +.B "[count] +" +Move the cursor down +.I count +lines to the first nonblank character of that line. +.TP +.B "[count] " +.TP +.B "[count] k" +Move the cursor up +.I count +lines, without changing the current column. +.TP +.B "" +Return to the most recent tag context. +.TP +.B "" +Scroll backwards +.I count +lines. +.TP +.B "" +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 "" +Scroll backwards +.I count +lines, leaving the current line and column as is, if possible. +.TP +.B "" +Suspend the current editor session. +.TP +.B "" +Execute +.I \&ex +commands or cancel partial commands. +.TP +.B "" +Push a tag reference onto the tag stack. +.TP +.B "" +Switch to the most recently edited file. +.TP +.B "[count] " +.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 "'" +.TP +.B "`" +Return to a context marked by the character +.IR . +.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" +.TP +.B "/RE/ [offset]" +.TP +.B "?RE" +.TP +.B "?RE? [offset]" +.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 " +Search +.I count +times backward through the current line for +.IR . +.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 " +Search backwards, +.I count +times, +through the current line for the character +.I after +the specified +.IR . +.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" +Search forward, +.I count +times, through the rest of the current line for +.IR . +.TP +.B "[count] i" +Enter input mode, inserting the text before the cursor. +.TP +.B "m " +Save the current context (line and column) as +.IR . +.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 " +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 " +Search forward, +.I count +times, through the current line for the character immediately +.I before +.IR . +.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] -|.|+|^|" +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 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 "" +Replay the previous input. +.TP +.B "" +Erase to the previous +.B shiftwidth +column boundary. +.TP +.B "^" +Erase all of the autoindent characters, and reset the autoindent level. +.TP +.B "0" +Erase all of the autoindent characters. +.TP +.B "" +Insert sufficient +.I +and +.I +characters to move forward to the next +.B shiftwidth +column boundary. +.TP +.B " +.TP +.B "" +Erase the last character. +.TP +.B "" +Quote the next character. +.TP +.B " +Resolve all text input into the file, and return to command mode. +.TP +.B "" +Erase the current line. +.TP +.B "" +.TP +.B "" +Erase the last word. +The definition of word is dependent on the +.B altwerase +and +.B ttywerase +options. +.TP +.B "[0-9A-Fa-f]+" +Insert a character with the specified hexadecimal value into the text. +.TP +.B "" +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 "" +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] " +.TP +.B "[line] k " +Mark the line with the mark +.IR . +.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 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 0000000..0e1b634 --- /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 0000000..382e635 --- /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 "" +.IP "" +The end-of-file character is used to scroll the screen in the +.CO ex +editor. +This character is normally +.LI . +However, whatever character is set for the current terminal is supported +as well as +.LI . +.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 , +.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 "" +.KY "" +.IP "" +.IP "" +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 +key. +.sp +Historically, the +.CO eof +command ignored any preceding count, and the +.LI +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 +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 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 +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 +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 +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 +character. +.SS +.SP Line: +Unchanged. +.SP Options: +Affected by the +.OP remap +option. +.SE +.KY mark +.KY k +.IP "[line] ma[rk] " +.Ip "[line] k " +Mark the line with the mark +.LI . +The expressions +.QT ' +and +.QT ` +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, 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 +.Ip +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 0000000..4c3acb6 --- /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 +' 23 +( 23 +) 24 +* 43 ++ 19 +, 24 +. 24 +/RE/ 25 +0 25 +0 38 +: 26 +; 26 +< 26, 43 + 14 + 17 + 17 + 17, 38, 42 + 18 + 18 + 18 + 18, 39 + 19 + 19 + 19 + 19 + 19 + 19 + 19, 38 + 20 + 20, 39 + 39 + 20 + 20, 52 + 20 + 21 + 40, 42 + 39 + 20, 39 + 12, 37, 39 + 39 + 12, 39 + 14 + 38 + 21 + 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 +^ 38 +_ 32 +` 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 0000000..c65207c --- /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 0000000..a82c792 --- /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 0000000..8384128 --- /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 , +the indent character will be deleted and the line will be empty. +For example, if you enter +.LI +twice in succession, +the line created by the first +.LI +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 +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 +key would erase the indent characters. +Both the +.LI +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 +will erase all of the indent characters for the current line, +resetting the indentation level to 0. +Similarly, the keys +.QT ^ +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 +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 , +.LI , +and +.LI , +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 +to switch to an alternate screen. +Entering a +.CO +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 +character. +To set it to +.LI , +use +.QT "set cedit=" . +.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 +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 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 +and +.LI . +To set it to +.LI , +use +.QT "set filec=" . +To set it to +.LI , +use +.QT "set filec=\e" . +.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 +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 "." , +(where +.LI +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##" , +then all of the following additional paragraph boundaries would be +recognized: +.sp +.(l +.La +.A +.## +.)l +.KY path +.IP "path []" +The path option can be used to specify a -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 +and +.CO +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 "." , +(where +.LI +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 +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 +and +.CO , +and the default number of lines scrolled by the +.CO vi +commands +.CO +and +.CO . +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 +and +.CO +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 +or +.LI +character, and you then enter another +.LI +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 0000000..a7d95e3 --- /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 0000000..12030cd --- /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 +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 , +.CO , +.CO , +.CO , +.CO , +.CO , +.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. + + $ +% ' ( ) ++ , - / +0 ; ? B +E F G H +L M N T +W [[ ]] ^ +\&_ ` 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 +.IP "[count] " +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 +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. +.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 +.IP "[count] " +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 +.IP "[count] " +Scroll forward +.LI count +lines. +If +.LI count +is not specified, scroll forward the number of lines specified by the last +.CO +or +.CO +command. +If this is the first +.CO +or +.CO +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 +.IP "[count] " +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 +.IP "[count] " +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 +.IP "" +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 +.IP "[count] " +.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 +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 +.IP "[count] " +.KY +.Ip "[count] " +.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 , +.CO +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 +.IP "" +.KY +.Ip "" +Repaint the screen. +.SS +.SP Line: +Unchanged. +.SP Column: +Unchanged. +.SP Options: +None. +.SE +.KY +.IP "[count] " +.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 +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 +.IP "[count] " +.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 +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 +.IP "" +Return to the most recent tag context. +The +.CO +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 +.IP "[count] " +Scroll backward +.LI count +lines. +If +.LI count +is not specified, scroll backward the number of lines specified by the +last +.CO +or +.CO +command. +If this is the first +.CO +or +.CO +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 +.IP "" +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 +.IP "[count] " +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 +.IP "" +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 +.IP "" +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 +.IP "" +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 +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 +.IP "" +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 +.IP "[count] " +.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 +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)" +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 !! +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 +.IP \' +.KY ` +.Ip ` +Return to a context marked by the character +.LI . +If +.LI +is the +.QT ' +or +.QT ` +character, return to the previous context. +If +.LI +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" +.Ip "/RE/ [offset]" +.KY ?RE? +.Ip "?RE" +.Ip "?RE? [offset]" +.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 // , +.QT / , +.QT ?? , +or +.QT ? +search for the last search RE, in the indicated direction. +.sp +Searches may be interrupted using the +.LI +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 , +.LI +or +.LI +character; all of these characters may be escaped by using a +.LI "" +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 +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 +or +.LI +key to continue. +In some cases, a quit (normally a +.QQ q +character) or +.LI +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 +characters are logically appended to each line of the buffer. +If the buffer is character oriented, +.LI +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 +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 " +Search +.LI count +times backward through the current line for +.LI . +.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 " +Search backward, +.LI count +times, +through the current line for the character +.i after +the specified +.LI . +.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 " +Search forward, +.LI count +times, through the rest of the current line for +.LI . +.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 " +Save the current context (line and column) as +.LI . +The exact position is referred to by +.QT ` . +The line is referred to by +.QT ' . +.sp +Historically, +.LI +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 " +Replace characters. +The next +.LI count +characters in the line are replaced with +.LI . +Replacing characters with +.LI +characters results in creating new, empty lines into the file. +.sp +If +.LI +is +.LI , +the command is cancelled. +.SS +.SP Line: +Unchanged unless the replacement character is a +.LI , +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 , +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 " +Search forward, +.LI count +times, through the current line for the character immediately +.i before +.LI . +.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 +command. +.SP +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 +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 +.IP "" +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 +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 +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 +erase character and the +.QT 0 +and +.QT ^ +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 +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 +and +.LI +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 +.IP "" +If the first character of the input is a +.LI , +the previous input is replayed, as if just entered. +.KY +.IP "" +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 +and +.LI +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 +is ignored. +Otherwise, a literal +.LI +character is entered. +.KY ^ +.IP "^" +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 +.IP "0" +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 +.IP "" +Insert sufficient +.LI +and +.LI +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 +and +.LI +characters preceding the cursor being changed. +.sp +Historically, +.CO vi +did not permit the +.LI +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 +.IP +.KY +.Ip +Erase the last character. +.KY "" +.IP "" +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 +.IP +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 "" +.IP "" +Erase the current line. +.KY "" +.IP "" +.KY "" +.Ip "" +Erase the last word. +The definition of word is dependent on the +.OP altwerase +and +.OP ttywerase +options. +.KY "" +.IP "[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 +.IP "" +Interrupt text input mode, returning to command mode. +If the +.LI +character is used to interrupt inserting text into the file, +it is as if the +.LI +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 0000000..a880c16 --- /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 +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\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 +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 +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 +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 ^ , +where +.LI +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 ... 0x" , +or as octal values, in the form +.QT "\e ... \e" . +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 , +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 +command. +The +.CO +command returns the user to the previous context, i.e., +the last place from which a +.CO +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 +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 +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 +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 , +.CO tag +or +.CO cscope +commands to move to a new tag context, and then use the +.CO +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 or -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 """" ), +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 +into the replacement string will cause +the matched line to be split into two lines at that point. +(The +.LI +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 +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 +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 +.IP +The interrupt character is used to interrupt the current operation. +Normally +.LI , +whatever character is set for the current terminal is used. +.KY "" +.IP "" +The literal next character is used to escape the subsequent character +from any special meaning. +This character is always +.LI . +If the terminal is not set up to do XON/XOFF flow control, +then +.LI +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 """" +in +.CO vi +and +without the double quote, e.g. +.LI , +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 +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. + % ( ) +` / ? 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 and . +.KY "" +.IP "" +The character represented by an ASCII +.LI . +This character is almost always treated identically to a +.LI +character, but differs in that it can be escaped into the file text or +into a command. +.KY +.IP +The character represented by an ASCII +.LI . +This character is almost always treated identically to a +.LI +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 ' +or +.QT ` +refers to the line marked with the name +.LI . +(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 0000000..3d4aca0 --- /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 0000000..6b07630 --- /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\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\fR means +the \fBescape\fR key is to be typed. \fB\fR means that a +lower case letter is to be typed. \fB^\fR means that +the character is to be typed as a \fBcontrol\fR character, that is, +with the \fB\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 +^ and ^ 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 8 +escape, octal 033 +.IP 8 +carriage return, ^M, octal 015 +.IP 8 +linefeed ^J, octal 012 +.IP 8 +newline, ^J, octal 012 (same as linefeed) +.IP 8 +backspace, ^H, octal 010 +.IP 8 +tab, ^I, octal 011 +.IP 8 +bell, ^G, octal 07 +.IP 8 +formfeed, ^L, octal 014 +.IP 8 +space, octal 040 +.IP 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 + character. You will know you are back to command level +when you hear a . Usually typing an will produce the +same result. When \fBvi\fR gets an improperly formatted command +it rings the . +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 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] 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]" 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] 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]" 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" (,, or ). 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" (,, or ). +.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" (,, or ). +.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 . +.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 +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" 16 +This command does not move the cursor, but it \fBmarks\fR the place +in the file and the character "" 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" 16 +Move the cursor to the beginning of the line that is marked with the label +"". +.IP "\(ga" 16 +Move the cursor to the exact position on the line that was marked with +with the label "". +.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]/" 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]?" 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]" 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 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} 16 +Insert text immediately following the cursor position. +Mnemonic: \fBa\fRppend +.IP A{text} 16 +Insert text at the end of the current line. +Mnemonic: \fBA\fRppend +.IP i{text} 16 +Insert text immediately preceding the cursor position. +Mnemonic: \fBi\fRnsert +.IP I{text} 16 +Insert text at the beginning of the current line. +.IP o{text} 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} 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" 16 +Replaces the character at the current cursor position with . This +is a one character replacement. No is required for termination. +Mnemonic: \fBr\fReplace character +.IP "R{text}" 16 +Starts overlaying the characters on the screen with whatever you type. +It does not stop until an is typed. +.IP "[cnt]s{text}" 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}" 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}" 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"\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 ["][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 ["][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 ["]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\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 ["]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 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\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} 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}, see section 5. +.IP z{cnt} 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 +'s are typed. As with the , 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 16 +The backspace always serves as an erase during insert modes in addition +to your normal "erase" character. To insert a 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 , , +or . +.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\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 +.DE +and turned off with the command: +.DS +:set nooption +.DE +Commands requiring a value are set with a command of the form: +.DS +:set option=value +.DE +To display the value of a specific option type: +.DS +:set option? +.DE +To display only those that you have changed type: +.DS +:set +.DE +and to display the long table of all the settable parameters and +their current values type: +.DS +:set all +.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 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 , , and 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 . +.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 and characters displayed visually. +.IP magic 16 +Default: magic Type: toggle +.br +Enable the metacharacters for matching. These include \fB. * < > [string] +[^string]\fR and \fB[-]\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 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 +.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 +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 when it finds a natural +break point (usually a 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 or which it could +replace with a . 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 0000000..7941065 --- /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 0000000..c36ebe4 --- /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 +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 . +.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 0000000..8a09ce9 --- /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 0000000..1f2a8c6 --- /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 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. + + 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 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 -- 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 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 replaced a + previous 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 even though + 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 -matching pattern to use extended RE's, and bug + that kept cscope from finding patterns containing 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 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 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 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 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 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 + 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 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, not :w file. + + 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; 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 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, 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 . be equivalent to .+, 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 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 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 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 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 @ 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 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 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 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 , it's historic practice. + + Historic vi didn't eat 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- 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 "~" 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" 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 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 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 ` 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 . + + 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 to ^X + + 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/". + + 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 0000000..144295a --- /dev/null +++ b/contrib/nvi/docs/ev @@ -0,0 +1,55 @@ +# @(#)ev 8.4 (Berkeley) 4/29/94 + +Ev: Vi: Result: + (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' Copy to mark in line mode (or copy the current line). +^AC y` Copy to mark in character mode. +^Ad d' Delete to mark in line mode (or delete the current line). +^AD d` Delete to mark in character mode. +^Aj J Join lines. +^Am m 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 ^V 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 0000000..51650f9 --- /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 0000000..81df84a --- /dev/null +++ b/contrib/nvi/docs/help @@ -0,0 +1,229 @@ +MOVING THE CURSOR: + k - cursor up ^F - page forward / - search forward + j - cursor down ^B - page backward ? - 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 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 - write the file +:q - exit the file +:q! - exit without writing the file +:# - move to a line (e.g., :35 moves to line 35) + +MISCELLANEOUS: +^G - display the file name + J - join two lines (use i 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 + ^[ exit input mode, cancel partial commands + ^\ switch to ex mode + ^] tag push cursor word + ^^ switch to previous file + 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 0000000..dbad6c8 --- /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 0000000..8b1db32 --- /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, , , , + , + +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: ', G, H, L, M, z + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +In historic vi, the previous context mark was set if the +line or column changed: + +vi commands: `, /, ?, 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 0000000..e0e3483 --- /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 a "tye:csc find t"t + map c "tye:csc find c"t + map d "tye:csc find d"t + map e "tye:csc find e"t + map f "tye:csc find f"t + map g "tye:csc find g"t + map i "tye:csc find i"t + map s "tye:csc find s"t + map 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 -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 0000000..a112234 --- /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 0000000..9a7506e --- /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 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 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 0000000..c64b767 --- /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 0000000..a5fb892 --- /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 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 0000000..a25c780 --- /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 0000000..3da5a8f --- /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 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 "*" which will pipe the contents + of the named buffer to the interpreter, of the following form: + + [count]* + + 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 0000000..4ca990c --- /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 0000000..ec854ff --- /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 0000000..f757ad1 --- /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. {/\.*\/^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 0000000..3bf35ac --- /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 0000000..01554bc --- /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 0000000..3ee372e --- /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 0000000..f92d8f7 --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 characters + * were the same as a single , 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 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 + * wants a separator, but the command : wants to erase + * the command line.) If the line is empty except for s, + * or , we'll probably want to move up. I + * don't think there's any way to get 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 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 separated) + * command separators were very strange. For example, the command + * |||, 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. '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 characters) 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 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 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 , and + * the next non-blank character is a '!', it's a filter command + * and we want to eat everything up to the . 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 + * . 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 '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 s. + */ + if (*p == '!') { + if (ecp->clen > 0 && *ecp->cp == '!') { + ++ecp->cp; + --ecp->clen; + FL_SET(ecp->iflags, E_C_FORCE); + } + continue; + } + + /* Skip leading 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(<mp, 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 () or the scrolling commands (^D + * and ) as the first non- 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 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 + * + * + * 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 + * 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 + * 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, . was the same as .+, 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 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: + * + * [+-^]*[]*[0-9]* + * + * The rough translation is any number of signs, optionally + * followed by numbers, or a number by itself, all + * 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 0000000..5870990 --- /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 . */ +#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 0000000..231098c --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 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 0000000..8d89e12 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 + * 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 0000000..bc37109 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..cc5a201 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 0000000..e9c6c59 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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, [@*] 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@. + * + * 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 0000000..25f3f77 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..3307c7b --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..8f7fc8d --- /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 +#include + +#include +#include +#include + +#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 0000000..c2fa0a5 --- /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 +#include /* XXX: param.h may not have included types.h */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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: + * + * + * + * 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, "") == 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 0000000..58734bb --- /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 +#include + +#include +#include +#include + +#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 0000000..7531517 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..8b18e0f --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..565df66 --- /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 +#include + +#include +#include +#include + +#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 0000000..3492f9c --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..2e86e58 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..aba9dc5 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 0000000..6a78416 --- /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 +#include /* XXX: param.h may not have included types.h */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..c26c424 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..bc2cf08 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..08ad8c2 --- /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 +#include + +#include +#include +#include + +#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 0000000..0eb15d4 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..d6e45c3 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..afffaeb --- /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 +#include + +#include +#include +#include + +#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 0000000..e620352 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..5614c88 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..4218e08 --- /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 +#include + +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#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 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 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 0000000..2facb03 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..705fa1a --- /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 +#include + +#include +#include +#include + +#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 0000000..78296ff --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 , 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 0000000..9bc5bf0 --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#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 0000000..9ca6d60 --- /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 +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#ifdef HAVE_SYS5_PTY +#include +#endif +#include +#include + +#include +#include +#include +#include /* XXX: OSF/1 bug: include before */ +#include +#include +#include +#include +#include +#include + +#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 0000000..11e9297 --- /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 +#include + +#include +#include +#include + +#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 0000000..9516803 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..83bd36d --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..b52c527 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..bc55fd2 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..0ebb81d --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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- 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 \ 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 0000000..461b152 --- /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 +#include /* XXX: param.h may not have included types.h */ + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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: + * + * | + * + * 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 0000000..06736a7 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" + +#ifdef HAVE_TCL_INTERP +#include +#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 0000000..2f62ff5 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +#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\" would + * failed immediately with an error, as the 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 "\" 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, '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 /. We + * don't discard the backslash because we need it + * to get the 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 and + * . + */ +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 , which doesn't take up a full shiftwidth + * number of columns because it's preceded by 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 0000000..0b0b5b2 --- /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 +#include + +#include +#include +#include +#include + +#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 0000000..cddf7a6 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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\"\n"); + (void)ex_puts(sp, + "To see the list of ex commands, enter \":exusage\"\n"); + (void)ex_puts(sp, + "For an ex command usage statement enter \":exusage [cmd]\"\n"); + (void)ex_puts(sp, + "For a vi key usage statement enter \":viusage [key]\"\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 0000000..6c4772e --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..d7363c8 --- /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 +#include + +#include +#include +#include + +#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 0000000..82e503d --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..b3122e3 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..778dc7d --- /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 +#include + +#include +#include +#include + +#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 0000000..41b72ad --- /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 +#include + +#include +#include +#include +#include +#include + +#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 0000000..e29f633 --- /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 0000000..aee3dd2 --- /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 0000000..7d657b6 --- /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 0000000..88437e7 --- /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 0000000..bafeb94 --- /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 0000000..f140f9e --- /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 0000000..2e69b48 --- /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 0000000..ed01701 --- /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 0000000..b805343 --- /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 0000000..089fa7f --- /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 0000000..5d6dd0d --- /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 0000000..cbd7586 --- /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 0000000..ac68ae4 --- /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 0000000..20a4de4 --- /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 0000000..b3c335e --- /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 0000000..7745c88 --- /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 0000000..f7798c3 --- /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: + * + * + * + * 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 0000000..c92f1ed --- /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 +#include +#include + +#include +#include + +#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 0000000..7ca9f55 --- /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 +#include + +#include +#include +#include +#include + +#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 0000000..9fd9777 --- /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 +#include + +#include +#include +#include +#include + +#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 0000000..71578e0 --- /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 +#include + +#include +#include + +#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 0000000..28e686d --- /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 +#include + +#include +#include + +#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 0000000..9503c4d --- /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 0000000..5137b3f --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 +#else +#include +#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 0000000..a87e24d --- /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 or +from within an C block or a C 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 for perl regular expressions. + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Sven Verdoolaege diff --git a/contrib/nvi/perl_api/nviperl.pod b/contrib/nvi/perl_api/nviperl.pod new file mode 100644 index 0000000..43850d8 --- /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. + +=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 set the C variables. (If you think +this is a bad idea, tell me.) + +=back + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Sven Verdoolaege diff --git a/contrib/nvi/perl_api/perl.xs b/contrib/nvi/perl_api/perl.xs new file mode 100644 index 0000000..0b48cde --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" + +#include +#include +#include + +#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 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 0000000..20ff477 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" + +#include +#include +#include + +#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 0000000..0e38a9c --- /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 0000000..b9f8501 --- /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 0000000..118dd99 --- /dev/null +++ b/contrib/nvi/perl_scripts/make.pl @@ -0,0 +1,27 @@ +sub make { + open MAKE, "make 2>&1 1>/dev/null |"; + while() { + 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 0000000..f8d1bc0 --- /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 0000000..0a50159 --- /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 0000000..8f4a430 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 0000000..94a1e6a --- /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 0000000..61ecb6e --- /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 0000000..eccb9cd --- /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 0000000..25d0f62 --- /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 0000000..78c5a11 --- /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 . {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 {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 {tk_key_enter "\010"; break} + bind .t {tk_key_enter "\001"; break} + bind .t {tk_key_enter "\002"; break} + bind .t {tk_key_enter "\003"; break} + bind .t {tk_key_enter "\004"; break} + bind .t {tk_key_enter "\005"; break} + bind .t {tk_key_enter "\006"; break} + bind .t {tk_key_enter "\007"; break} + bind .t {tk_key_enter "\010"; break} + bind .t {tk_key_enter "\011"; break} + bind .t {tk_key_enter "\012"; break} + bind .t {tk_key_enter "\013"; break} + bind .t {tk_key_enter "\014"; break} + bind .t {tk_key_enter "\015"; break} + bind .t {tk_key_enter "\016"; break} + bind .t {tk_key_enter "\017"; break} + bind .t {tk_key_enter "\020"; break} + bind .t {tk_key_enter "\021"; break} + bind .t {tk_key_enter "\022"; break} + bind .t {tk_key_enter "\023"; break} + bind .t {tk_key_enter "\024"; break} + bind .t {tk_key_enter "\025"; break} + bind .t {tk_key_enter "\026"; break} + bind .t {tk_key_enter "\027"; break} + bind .t {tk_key_enter "\030"; break} + bind .t {tk_key_enter "\031"; break} + bind .t {tk_key_enter "\032"; break} + bind .t {tk_noop; break} + bind .t {tk_noop; break} + bind .t {tk_key_enter "x"; break} + bind .t {tk_key_enter "j"; break} + bind .t {tk_key_enter "G"; break} + bind .t {tk_key_enter "\033"; break} + bind .t {tk_key_enter "1G"; break} + bind .t {tk_key_enter "i"; break} + bind .t {tk_key_enter "h"; break} + bind .t {tk_key_enter "\006"; break} + bind .t {tk_key_enter "\002"; break} + bind .t {tk_key_enter "\015"; break} + bind .t {tk_key_enter "l"; break} + bind .t {tk_noop; break} + bind .t {tk_noop; break} + bind .t {tk_noop; break} + bind .t {tk_key_enter "\011"; break} + bind .t {tk_key_enter "k"; break} + bind .t {tk_key_enter "&"; break} + bind .t {tk_key_enter "^"; break} + bind .t {tk_key_enter "~"; break} + bind .t {tk_key_enter "*"; break} + bind .t {tk_key_enter "@"; break} + bind .t {tk_key_enter "\\"; break} + bind .t {tk_key_enter "|"; break} + bind .t {tk_key_enter "{"; break} + bind .t {tk_key_enter "; break}"} + bind .t {tk_key_enter "\["; break} + bind .t {tk_key_enter "]"; break} + bind .t {tk_key_enter ":"; break} + bind .t {tk_key_enter ","; break} + bind .t {tk_key_enter "$"; break} + bind .t {tk_key_enter "="; break} + bind .t {tk_key_enter "!"; break} + bind .t {tk_key_enter ">"; break} + bind .t {tk_key_enter "<"; break} + bind .t {tk_key_enter "-"; break} + bind .t {tk_key_enter "#"; break} + bind .t {tk_key_enter "("; break} + bind .t {tk_key_enter ")"; break} + bind .t {tk_key_enter "%"; break} + bind .t {tk_key_enter "."; break} + bind .t {tk_key_enter "+"; break} + bind .t {tk_key_enter "?"; break} + bind .t {tk_key_enter "\""; break} + bind .t {tk_key_enter "'"; break} + bind .t {tk_key_enter ";"; break} + bind .t {tk_key_enter "/"; break} + bind .t {tk_key_enter " "; break} + bind .t {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 ".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 0000000..be2b0d9 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..c2f34e7 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..b5cfab7 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..e109093 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..18299a2 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..096fa7b --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..206dacf --- /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 +#include + +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 0000000..5e60ade --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#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 0000000..d266f27 --- /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 +#include +#include + +#include +#include +#include +#include + +#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, [@*] 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, + * were appended to each line as it was pushed onto + * the stack. If the buffer was cut in character mode, + * 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 0000000..6a1b611 --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..cc9e30f --- /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 +#include +#include + +#include +#include +#include + +#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, + "^[ ", + "^[ 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]' '", + " 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][-|.|+|^|]", + " 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 0000000..70df78b --- /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 +#include +#include + +#include +#include +#include + +#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" + * 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 0000000..359080c --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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]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 !/; + * 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 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 , 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 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:!date: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 0000000..45e763f --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..ee58de3 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..6cf9377 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 .... + * :map K aABC DEF ^VKKKKK + * :map K 5aABC DEF ^VK + * + * 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 0000000..cfed80c --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..447430e --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..3996560 --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..762e38e --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..77220ea --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..4c965c7 --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..a4712b6 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" +#include "vi.h" + +/* + * v_replace -- [count]r + * + * !!! + * The r command in historic vi was almost beautiful in its badness. For + * example, "r" and "r" beeped the terminal and deleted + * a single character. "Nr", where N was greater than 1, + * inserted a single carriage return. "r" did cancel the command, + * but "r" erased a single character. To enter a literal + * character, it required three 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 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: + /* + * means escape the next character. + * 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: + /* means they changed their minds. */ + return (0); + case E_WRESIZE: + /* 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 or 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 0000000..c2f349e --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..85cd1e3 --- /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 +#include +#include + +#include +#include +#include + +#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 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 0000000..92def4b --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..4f7a267 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 or , 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 '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 '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 0000000..20e8ff2 --- /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 +#include +#include + +#include +#include +#include +#include + +#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-. + */ +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 0000000..a3d9376 --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..7095d78 --- /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 +#include +#include +#include + +#include +#include +#include + +#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 0000000..c47a485 --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * 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 character causes + * wrapmargin to split the line, the next user entered character is + * discarded if it's a 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., "Iab", + * 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, 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: + /* 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" + * 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 "[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 . + */ + 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 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 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 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 + * 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 + * 's, so, 6 words. The historic tty interface used + * '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 + * '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\" 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" 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 , and , as that's what the original + * 4BSD documentation said. This is obviously wrong, + * however, as 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 + * or . + */ + 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 "[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 . + */ + 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. + * 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", when the + * 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 + * 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 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 + * 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 , 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 characters + * in the line are resolved into the minimum number of characters. + * Historic practice. + */ + ts = O_VAL(sp, O_TABSTOP); + + /* Figure out the last 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^Tx^Tx^Tx^Dx^Dx^Dx + * 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 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 , which doesn't need a full shiftwidth number + * of columns because it's preceded by 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 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=4iai^T^D + * + * Otherwise, count up the total spaces/tabs needed to get from the + * beginning of the line (or the last non- 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 to replace N . + */ + 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 "iabcd0C", 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 + * "iabcd0R", the "bcd" disappears, and magically reappears + * on the second 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 /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 0000000..179beba --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..d04a8a1 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 0000000..c4c0daa --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000..e6e4a63 --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..15f155f --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..4708f19 --- /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 +#include +#include + +#include +#include +#include + +#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 0000000..5f02ddf --- /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 +#include +#include + +#include +#include +#include + +#include "../common/common.h" +#include "vi.h" + +/* + * v_z -- [count]z[count][-.+^] + * 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 . */ + 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 0000000..3e454ca --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..d20f7f2 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 characters at the beginning of + * a map wasn't considered an error -- in fact, users would + * put leading 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. + * + * 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 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 , 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, ¬used, 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" or "!" 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 0000000..bede3a6 --- /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 + * . + */ + 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 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 0000000..b439de9 --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..7ef8f53 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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, ¬used, &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 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 . + * 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 -- and all routines that care about + * do their own expansions. This catches + * 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, ¬used, &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 0000000..8158760 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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, ¬used); + 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 0000000..c92c10c --- /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 +#include +#include + +#include +#include +#include +#include + +#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 0000000..af38057 --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#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 0000000..d017354 --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.1