From 1e64b3baff009074655731838b99ed3a7b74bef4 Mon Sep 17 00:00:00 2001 From: "Jordan K. Hubbard" Date: Mon, 24 Jan 1994 01:14:16 +0000 Subject: [PATCH] This is `nvi' (tweaked to install as vi), version 1.01. --- usr.bin/vi/Makefile | 105 ++++ usr.bin/vi/args.h | 53 ++ usr.bin/vi/ascii.c | 115 ++++ usr.bin/vi/clib/Xaddnstr.c | 15 + usr.bin/vi/clib/addidlok.c | 13 + usr.bin/vi/clib/err.c | 151 +++++ usr.bin/vi/clib/fchmod.c | 17 + usr.bin/vi/clib/flock.c | 49 ++ usr.bin/vi/clib/fwopen.c | 157 +++++ usr.bin/vi/clib/lockf.c | 27 + usr.bin/vi/clib/memmove.c | 139 +++++ usr.bin/vi/clib/memset.c | 130 ++++ usr.bin/vi/clib/mktemp.c | 127 ++++ usr.bin/vi/clib/pty.c | 100 +++ usr.bin/vi/clib/pty_s5r4.c | 175 ++++++ usr.bin/vi/clib/realloc.c | 11 + usr.bin/vi/clib/siglist.c | 109 ++++ usr.bin/vi/clib/snprintf.c | 54 ++ usr.bin/vi/clib/strdup.c | 56 ++ usr.bin/vi/clib/strerror.c | 67 ++ usr.bin/vi/clib/strsep.c | 80 +++ usr.bin/vi/clib/strtoul.c | 109 ++++ usr.bin/vi/cut.c | 525 ++++++++++++++++ usr.bin/vi/cut.h | 90 +++ usr.bin/vi/delete.c | 189 ++++++ usr.bin/vi/exf.c | 690 +++++++++++++++++++++ usr.bin/vi/exf.h | 129 ++++ usr.bin/vi/gs.h | 91 +++ usr.bin/vi/include/bitstring.h | 143 +++++ usr.bin/vi/include/cdefs.h | 98 +++ usr.bin/vi/include/compat.h | 241 ++++++++ usr.bin/vi/include/err.h | 16 + usr.bin/vi/include/file.h | 15 + usr.bin/vi/include/glob.h | 92 +++ usr.bin/vi/include/mpool.h | 135 +++++ usr.bin/vi/include/ndbm.h | 77 +++ usr.bin/vi/include/queue.h | 245 ++++++++ usr.bin/vi/interrupt.h | 96 +++ usr.bin/vi/line.c | 464 ++++++++++++++ usr.bin/vi/log.c | 663 ++++++++++++++++++++ usr.bin/vi/log.h | 53 ++ usr.bin/vi/main.c | 745 +++++++++++++++++++++++ usr.bin/vi/mark.c | 257 ++++++++ usr.bin/vi/mark.h | 64 ++ usr.bin/vi/mem.h | 166 +++++ usr.bin/vi/msg.h | 68 +++ usr.bin/vi/options.c | 822 +++++++++++++++++++++++++ usr.bin/vi/options.h.stub | 110 ++++ usr.bin/vi/options_f.c | 547 +++++++++++++++++ usr.bin/vi/pathnames.h | 45 ++ usr.bin/vi/recover.c | 622 +++++++++++++++++++ usr.bin/vi/screen.c | 296 +++++++++ usr.bin/vi/screen.h | 314 ++++++++++ usr.bin/vi/search.c | 860 ++++++++++++++++++++++++++ usr.bin/vi/search.h | 55 ++ usr.bin/vi/seq.c | 299 +++++++++ usr.bin/vi/seq.h | 75 +++ usr.bin/vi/sex/sex_confirm.c | 74 +++ usr.bin/vi/sex/sex_get.c | 323 ++++++++++ usr.bin/vi/sex/sex_refresh.c | 72 +++ usr.bin/vi/sex/sex_screen.c | 214 +++++++ usr.bin/vi/sex/sex_screen.h | 66 ++ usr.bin/vi/sex/sex_term.c | 227 +++++++ usr.bin/vi/sex/sex_util.c | 97 +++ usr.bin/vi/svi/svi_confirm.c | 84 +++ usr.bin/vi/svi/svi_ex.c | 476 +++++++++++++++ usr.bin/vi/svi/svi_get.c | 150 +++++ usr.bin/vi/svi/svi_line.c | 425 +++++++++++++ usr.bin/vi/svi/svi_refresh.c | 839 +++++++++++++++++++++++++ usr.bin/vi/svi/svi_relative.c | 205 +++++++ usr.bin/vi/svi/svi_screen.c | 534 ++++++++++++++++ usr.bin/vi/svi/svi_screen.h | 238 ++++++++ usr.bin/vi/svi/svi_smap.c | 1041 ++++++++++++++++++++++++++++++++ usr.bin/vi/svi/svi_split.c | 581 ++++++++++++++++++ usr.bin/vi/svi/svi_util.c | 314 ++++++++++ usr.bin/vi/term.c | 687 +++++++++++++++++++++ usr.bin/vi/term.h | 184 ++++++ usr.bin/vi/timer.c | 166 +++++ usr.bin/vi/trace.c | 72 +++ usr.bin/vi/util.c | 570 +++++++++++++++++ usr.bin/vi/vi.h | 165 +++++ usr.bin/vi/xaw/xaw_screen.c | 85 +++ 82 files changed, 19145 insertions(+) create mode 100644 usr.bin/vi/Makefile create mode 100644 usr.bin/vi/args.h create mode 100644 usr.bin/vi/ascii.c create mode 100644 usr.bin/vi/clib/Xaddnstr.c create mode 100644 usr.bin/vi/clib/addidlok.c create mode 100644 usr.bin/vi/clib/err.c create mode 100644 usr.bin/vi/clib/fchmod.c create mode 100644 usr.bin/vi/clib/flock.c create mode 100644 usr.bin/vi/clib/fwopen.c create mode 100644 usr.bin/vi/clib/lockf.c create mode 100644 usr.bin/vi/clib/memmove.c create mode 100644 usr.bin/vi/clib/memset.c create mode 100644 usr.bin/vi/clib/mktemp.c create mode 100644 usr.bin/vi/clib/pty.c create mode 100644 usr.bin/vi/clib/pty_s5r4.c create mode 100644 usr.bin/vi/clib/realloc.c create mode 100644 usr.bin/vi/clib/siglist.c create mode 100644 usr.bin/vi/clib/snprintf.c create mode 100644 usr.bin/vi/clib/strdup.c create mode 100644 usr.bin/vi/clib/strerror.c create mode 100644 usr.bin/vi/clib/strsep.c create mode 100644 usr.bin/vi/clib/strtoul.c create mode 100644 usr.bin/vi/cut.c create mode 100644 usr.bin/vi/cut.h create mode 100644 usr.bin/vi/delete.c create mode 100644 usr.bin/vi/exf.c create mode 100644 usr.bin/vi/exf.h create mode 100644 usr.bin/vi/gs.h create mode 100644 usr.bin/vi/include/bitstring.h create mode 100644 usr.bin/vi/include/cdefs.h create mode 100644 usr.bin/vi/include/compat.h create mode 100644 usr.bin/vi/include/err.h create mode 100644 usr.bin/vi/include/file.h create mode 100644 usr.bin/vi/include/glob.h create mode 100644 usr.bin/vi/include/mpool.h create mode 100644 usr.bin/vi/include/ndbm.h create mode 100644 usr.bin/vi/include/queue.h create mode 100644 usr.bin/vi/interrupt.h create mode 100644 usr.bin/vi/line.c create mode 100644 usr.bin/vi/log.c create mode 100644 usr.bin/vi/log.h create mode 100644 usr.bin/vi/main.c create mode 100644 usr.bin/vi/mark.c create mode 100644 usr.bin/vi/mark.h create mode 100644 usr.bin/vi/mem.h create mode 100644 usr.bin/vi/msg.h create mode 100644 usr.bin/vi/options.c create mode 100644 usr.bin/vi/options.h.stub create mode 100644 usr.bin/vi/options_f.c create mode 100644 usr.bin/vi/pathnames.h create mode 100644 usr.bin/vi/recover.c create mode 100644 usr.bin/vi/screen.c create mode 100644 usr.bin/vi/screen.h create mode 100644 usr.bin/vi/search.c create mode 100644 usr.bin/vi/search.h create mode 100644 usr.bin/vi/seq.c create mode 100644 usr.bin/vi/seq.h create mode 100644 usr.bin/vi/sex/sex_confirm.c create mode 100644 usr.bin/vi/sex/sex_get.c create mode 100644 usr.bin/vi/sex/sex_refresh.c create mode 100644 usr.bin/vi/sex/sex_screen.c create mode 100644 usr.bin/vi/sex/sex_screen.h create mode 100644 usr.bin/vi/sex/sex_term.c create mode 100644 usr.bin/vi/sex/sex_util.c create mode 100644 usr.bin/vi/svi/svi_confirm.c create mode 100644 usr.bin/vi/svi/svi_ex.c create mode 100644 usr.bin/vi/svi/svi_get.c create mode 100644 usr.bin/vi/svi/svi_line.c create mode 100644 usr.bin/vi/svi/svi_refresh.c create mode 100644 usr.bin/vi/svi/svi_relative.c create mode 100644 usr.bin/vi/svi/svi_screen.c create mode 100644 usr.bin/vi/svi/svi_screen.h create mode 100644 usr.bin/vi/svi/svi_smap.c create mode 100644 usr.bin/vi/svi/svi_split.c create mode 100644 usr.bin/vi/svi/svi_util.c create mode 100644 usr.bin/vi/term.c create mode 100644 usr.bin/vi/term.h create mode 100644 usr.bin/vi/timer.c create mode 100644 usr.bin/vi/trace.c create mode 100644 usr.bin/vi/util.c create mode 100644 usr.bin/vi/vi.h create mode 100644 usr.bin/vi/xaw/xaw_screen.c diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile new file mode 100644 index 0000000000..57671e668e --- /dev/null +++ b/usr.bin/vi/Makefile @@ -0,0 +1,105 @@ +# @(#)Makefile 8.26 (Berkeley) 1/12/94 + +PROG= vi +MAN1= vi.1 +BINDIR?= /usr/bin + +#CFLAGS=-g -DDEBUG +#CFLAGS+=-pg +CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/../include -I${.CURDIR}/../curses -I${.CURDIR}/ex -I${.CURDIR}/vi -I${.CURDIR}/../regex +#STRIP= +.PATH: ${.CURDIR}/ex ${.CURDIR}/sex ${.CURDIR}/vi ${.CURDIR}/svi \ + ${.CURDIR}/xaw +CLEANFILES+=ex + +# General sources. +SRCS= ascii.c cut.c delete.c exf.c line.c log.c main.c mark.c \ + options.c options_f.c screen.c search.c seq.c recover.c \ + term.c timer.c trace.c util.c + +# Ex source. +SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c \ + ex_bang.c ex_cd.c ex_delete.c ex_digraph.c ex_display.c \ + ex_edit.c ex_equal.c ex_exit.c ex_file.c ex_global.c ex_init.c \ + ex_join.c ex_map.c ex_mark.c ex_mkexrc.c ex_move.c ex_open.c \ + ex_preserve.c ex_print.c ex_put.c ex_read.c ex_screen.c \ + ex_script.c ex_set.c ex_shell.c ex_shift.c ex_source.c ex_stop.c \ + ex_subst.c ex_tag.c ex_undo.c ex_usage.c ex_util.c ex_version.c \ + ex_visual.c ex_write.c ex_yank.c ex_z.c excmd.c filter.c + +# Ex screen source. +SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \ + sex_util.c + +# Vi source. +SRCS+= getc.c v_again.c v_at.c v_ch.c v_delete.c v_ex.c v_exit.c \ + v_exmode.c v_filter.c v_increment.c v_init.c v_join.c v_left.c \ + v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \ + v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \ + v_sentence.c v_shift.c v_status.c v_stop.c v_switch.c v_tag.c \ + v_text.c v_ulcase.c v_undo.c v_util.c v_word.c v_xchar.c v_yank.c \ + v_z.c vcmd.c vi.c + +# Vi curses screen source. +SRCS+= svi_confirm.c svi_ex.c svi_get.c svi_line.c svi_refresh.c \ + svi_relative.c svi_screen.c svi_smap.c svi_split.c svi_util.c + +# Athena widget set screen source. +SRCS+= xaw_screen.c + +#LDADD+=-pg +DPADD+= ${LIBCURSES} ${LIBTERM} +LDADD+= -L${.CURDIR}/../curses/obj -L${.CURDIR}/../curses \ + -L${.CURDIR}/../regex/obj -L${.CURDIR}/../regex \ + -lregex -lcurses -ltermlib -lutil +SPECHDR=excmd.h options.h +CLEANFILES+=${SPECHDR} +LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view + +all: .curses-stamp vi vi.1 + +.curses-stamp: + @(cd ${.CURDIR}/curses; make obj) + @(cd ${.CURDIR}/curses; make) + touch -f .curses-stamp + +warn:: ${SRCS} + -(cd ${.CURDIR} && \ + gcc -Wall -O -DDEBUG -Iobj -Ivi -Iex -I. ${.ALLSRC} \ + -lcurses -ltermlib 2>&1 | \ + sed -e "/warning: .*sccsid.*defined but not used/d" \ + -e "/warning: suggest parentheses around/d" \ + -e "/In function /d" \ + -e "/At top level:/d" \ + -e "/warning: .*inline call to/d" \ + -e "/warning: comparison is always 1 due /d") > \ + ${.CURDIR}/WARN.OUT + +options.h: options.h.stub options.c # Makefile + rm -f options.h + cp ${.CURDIR}/options.h.stub options.h + chmod 664 options.h + (echo '/^\/\* O_[0-9A-Z_]*/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}'; \ + echo 'END {'; \ + echo 'printf("#define O_OPTIONCOUNT %d\n", cnt)'; \ + echo '}') > /tmp/__vi.options.h + awk -f /tmp/__vi.options.h ${.CURDIR}/options.c >> options.h + rm -f /tmp/__vi.options.h + +excmd.h: excmd.h.stub excmd.c # Makefile + rm -f excmd.h + cp ${.CURDIR}/ex/excmd.h.stub excmd.h + chmod 664 excmd.h + (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}') > /tmp/__vi.excmd.h + awk -f /tmp/__vi.excmd.h ${.CURDIR}/ex/excmd.c >> excmd.h + rm -f /tmp/__vi.excmd.h + +.include + +.depend: ${SPECHDR} diff --git a/usr.bin/vi/args.h b/usr.bin/vi/args.h new file mode 100644 index 0000000000..4d437442d8 --- /dev/null +++ b/usr.bin/vi/args.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)args.h 8.3 (Berkeley) 12/19/93 + */ + +/* + * Structure for building "argc/argv" vector of arguments. + * + * !!! + * All arguments are nul terminated as well as having an associated length. + * The argument vector is NOT necessarily NULL terminated. The proper way + * to check the number of arguments is to use the argc value in the EXCMDARG + * structure or to walk the array until an ARGS structure with a length of 0 + * is found. + */ +typedef struct _args { + CHAR_T *bp; /* Argument. */ + size_t blen; /* Buffer length. */ + size_t len; /* Argument length. */ + +#define A_ALLOCATED 0x01 /* If allocated space. */ + u_char flags; +} ARGS; diff --git a/usr.bin/vi/ascii.c b/usr.bin/vi/ascii.c new file mode 100644 index 0000000000..61c79489a2 --- /dev/null +++ b/usr.bin/vi/ascii.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ascii.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include + +#include "vi.h" + +CHNAME const asciiname[UCHAR_MAX + 1] = { + {"^@", 2}, {"^A", 2}, {"^B", 2}, {"^C", 2}, + {"^D", 2}, {"^E", 2}, {"^F", 2}, {"^G", 2}, + {"^H", 2}, {"^I", 2}, {"^J", 2}, {"^K", 2}, + {"^L", 2}, {"^M", 2}, {"^N", 2}, {"^O", 2}, + {"^P", 2}, {"^Q", 2}, {"^R", 2}, {"^S", 2}, + {"^T", 2}, {"^U", 2}, {"^V", 2}, {"^W", 2}, + {"^X", 2}, {"^Y", 2}, {"^Z", 2}, {"^[", 2}, + {"^\\", 2}, {"^]", 2}, {"^^", 2}, {"^_", 2}, + {" ", 1}, {"!", 1}, {"\"", 1}, {"#", 1}, + {"$", 1}, {"%", 1}, {"&", 1}, {"'", 1}, + {"(", 1}, {")", 1}, {"*", 1}, {"+", 1}, + {",", 1}, {"-", 1}, {".", 1}, {"/", 1}, + {"0", 1}, {"1", 1}, {"2", 1}, {"3", 1}, + {"4", 1}, {"5", 1}, {"6", 1}, {"7", 1}, + {"8", 1}, {"9", 1}, {":", 1}, {";", 1}, + {"<", 1}, {"=", 1}, {">", 1}, {"?", 1}, + {"@", 1}, {"A", 1}, {"B", 1}, {"C", 1}, + {"D", 1}, {"E", 1}, {"F", 1}, {"G", 1}, + {"H", 1}, {"I", 1}, {"J", 1}, {"K", 1}, + {"L", 1}, {"M", 1}, {"N", 1}, {"O", 1}, + {"P", 1}, {"Q", 1}, {"R", 1}, {"S", 1}, + {"T", 1}, {"U", 1}, {"V", 1}, {"W", 1}, + {"X", 1}, {"Y", 1}, {"Z", 1}, {"[", 1}, + {"\\", 1}, {"]", 1}, {"^", 1}, {"_", 1}, + {"`", 1}, {"a", 1}, {"b", 1}, {"c", 1}, + {"d", 1}, {"e", 1}, {"f", 1}, {"g", 1}, + {"h", 1}, {"i", 1}, {"j", 1}, {"k", 1}, + {"l", 1}, {"m", 1}, {"n", 1}, {"o", 1}, + {"p", 1}, {"q", 1}, {"r", 1}, {"s", 1}, + {"t", 1}, {"u", 1}, {"v", 1}, {"w", 1}, + {"x", 1}, {"y", 1}, {"z", 1}, {"{", 1}, + {"|", 1}, {"}", 1}, {"~", 1}, {"^?", 2}, + {"0x80", 4}, {"0x81", 4}, {"0x82", 4}, {"0x83", 4}, + {"0x84", 4}, {"0x85", 4}, {"0x86", 4}, {"0x87", 4}, + {"0x88", 4}, {"0x89", 4}, {"0x8a", 4}, {"0x8b", 4}, + {"0x8c", 4}, {"0x8d", 4}, {"0x8e", 4}, {"0x8f", 4}, + {"0x90", 4}, {"0x91", 4}, {"0x92", 4}, {"0x93", 4}, + {"0x94", 4}, {"0x95", 4}, {"0x96", 4}, {"0x97", 4}, + {"0x98", 4}, {"0x99", 4}, {"0x9a", 4}, {"0x9b", 4}, + {"0x9c", 4}, {"0x9d", 4}, {"0x9e", 4}, {"0x9f", 4}, + {"0xa0", 4}, {"0xa1", 4}, {"0xa2", 4}, {"0xa3", 4}, + {"0xa4", 4}, {"0xa5", 4}, {"0xa6", 4}, {"0xa7", 4}, + {"0xa8", 4}, {"0xa9", 4}, {"0xaa", 4}, {"0xab", 4}, + {"0xac", 4}, {"0xad", 4}, {"0xae", 4}, {"0xaf", 4}, + {"0xb0", 4}, {"0xb1", 4}, {"0xb2", 4}, {"0xb3", 4}, + {"0xb4", 4}, {"0xb5", 4}, {"0xb6", 4}, {"0xb7", 4}, + {"0xb8", 4}, {"0xb9", 4}, {"0xba", 4}, {"0xbb", 4}, + {"0xbc", 4}, {"0xbd", 4}, {"0xbe", 4}, {"0xbf", 4}, + {"0xc0", 4}, {"0xc1", 4}, {"0xc2", 4}, {"0xc3", 4}, + {"0xc4", 4}, {"0xc5", 4}, {"0xc6", 4}, {"0xc7", 4}, + {"0xc8", 4}, {"0xc9", 4}, {"0xca", 4}, {"0xcb", 4}, + {"0xcc", 4}, {"0xcd", 4}, {"0xce", 4}, {"0xcf", 4}, + {"0xd0", 4}, {"0xd1", 4}, {"0xd2", 4}, {"0xd3", 4}, + {"0xd4", 4}, {"0xd5", 4}, {"0xd6", 4}, {"0xd7", 4}, + {"0xd8", 4}, {"0xd9", 4}, {"0xda", 4}, {"0xdb", 4}, + {"0xdc", 4}, {"0xdd", 4}, {"0xde", 4}, {"0xdf", 4}, + {"0xe0", 4}, {"0xe1", 4}, {"0xe2", 4}, {"0xe3", 4}, + {"0xe4", 4}, {"0xe5", 4}, {"0xe6", 4}, {"0xe7", 4}, + {"0xe8", 4}, {"0xe9", 4}, {"0xea", 4}, {"0xeb", 4}, + {"0xec", 4}, {"0xed", 4}, {"0xee", 4}, {"0xef", 4}, + {"0xf0", 4}, {"0xf1", 4}, {"0xf2", 4}, {"0xf3", 4}, + {"0xf4", 4}, {"0xf5", 4}, {"0xf6", 4}, {"0xf7", 4}, + {"0xf8", 4}, {"0xf9", 4}, {"0xfa", 4}, {"0xfb", 4}, + {"0xfc", 4}, {"0xfd", 4}, {"0xfe", 4}, {"0xff", 4}, +}; + +char * +charname(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + return (sp->gp->cname[ch & UCHAR_MAX].name); +} diff --git a/usr.bin/vi/clib/Xaddnstr.c b/usr.bin/vi/clib/Xaddnstr.c new file mode 100644 index 0000000000..9d270281bc --- /dev/null +++ b/usr.bin/vi/clib/Xaddnstr.c @@ -0,0 +1,15 @@ +#include + +#include + +int +addnstr(s, n) + const char *s; + int n; +{ + int ch; + + while (n-- && (ch = *s++)) + addch(ch); + return (OK); +} diff --git a/usr.bin/vi/clib/addidlok.c b/usr.bin/vi/clib/addidlok.c new file mode 100644 index 0000000000..906992d36b --- /dev/null +++ b/usr.bin/vi/clib/addidlok.c @@ -0,0 +1,13 @@ +#include + +/* + * idlok -- + * Fake idlok. + */ +void +idlok(win, bf) + WINDOW *win; + int bf; +{ + return; +} diff --git a/usr.bin/vi/clib/err.c b/usr.bin/vi/clib/err.c new file mode 100644 index 0000000000..7b3ca9b9f1 --- /dev/null +++ b/usr.bin/vi/clib/err.c @@ -0,0 +1,151 @@ +#include + +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +char *__progname = "nvi"; /* Program name, from crt0. */ + +void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verr(eval, fmt, ap); + va_end(ap); +} + +void +verr(eval, fmt, ap) + int eval; + const char *fmt; + va_list ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); + exit(eval); +} + +void +#ifdef __STDC__ +errx(int eval, const char *fmt, ...) +#else +errx(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verrx(eval, fmt, ap); + va_end(ap); +} + +void +verrx(eval, fmt, ap) + int eval; + const char *fmt; + va_list ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} + +void +#ifdef __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(fmt, ap) + const char *fmt; + va_list ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); +} + +void +#ifdef __STDC__ +warnx(const char *fmt, ...) +#else +warnx(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarnx(fmt, ap) + const char *fmt; + va_list ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); +} diff --git a/usr.bin/vi/clib/fchmod.c b/usr.bin/vi/clib/fchmod.c new file mode 100644 index 0000000000..887786c342 --- /dev/null +++ b/usr.bin/vi/clib/fchmod.c @@ -0,0 +1,17 @@ +#include +#include +#include + +int +fchmod(fd, mode) + int fd; + u_int mode; +{ + char *tty; + + /* Find the tty. */ + if ((tty = ttyname(fd)) == NULL) + return (-1); + + return (chmod(tty, mode)); +} diff --git a/usr.bin/vi/clib/flock.c b/usr.bin/vi/clib/flock.c new file mode 100644 index 0000000000..00273738d1 --- /dev/null +++ b/usr.bin/vi/clib/flock.c @@ -0,0 +1,49 @@ +#include +/* + * Include , not , the flock(2) + * #defines were found there on historical systems. + */ +#include + +#include +#include +#include + +#include + +/* + * Use fcntl(2) locking to fake flock(2) locking. + * + * DO NOT USE fcntl(2) UNLESS YOU HAVE TO, THERE ARE SOME SYSTEMS + * (I.E. ULTRIX) WHERE LOCKS AREN'T RELEASED WHEN PROCESSES DIE. + */ +int +flock(fd, operation) + int fd, operation; +{ + struct flock arg; + + switch (operation & ~LOCK_NB) { + case LOCK_EX: + arg.l_type = F_WRLCK; + break; + case LOCK_SH: + arg.l_type = F_RDLCK; + break; + case LOCK_UN: + arg.l_type = F_UNLCK; + break; + default: + abort(); + } + + arg.l_start = arg.l_len = 0; + arg.l_pid = 0; + arg.l_whence = 0; /* SEEK_SET */ + + if (!fcntl(fd, operation & LOCK_NB ? F_SETLK : F_SETLKW, &arg)) + return (0); + if (errno == EACCES || errno == EAGAIN) + errno = EWOULDBLOCK; + return (-1); +} diff --git a/usr.bin/vi/clib/fwopen.c b/usr.bin/vi/clib/fwopen.c new file mode 100644 index 0000000000..c6d4c67e9e --- /dev/null +++ b/usr.bin/vi/clib/fwopen.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)fwopen.c 8.5 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include +#include + +#include +#undef fwopen /* For testing. */ +#include + +#include "vi.h" +#include "pathnames.h" + +/* + * The major portability problem in nvi is that it uses new functionality + * from 4.4BSD to handle the interaction between vi and ex. + * + * Ex was written to use the stdio output routines because it's a lot easier + * that way. Since vi wants to take that output and put it up on the screen + * using curses, it needs to replace the underlying read/write routines in a + * stdio stream with its own. The way this works is that when vi wants to + * use the ex routines, it sets it up so that the output of ex goes to an nvi + * function which knows how to display the output on the screen. This way vi + * never has to leave curses (resulting in much nicer screen displays) and ex + * can use printf(3) without concern for what else is going on. 4.4BSD has a + * stdio function (fwopen(3)) which provides this functionality. Most other + * systems don't. + * + * Vi/ex uses the following key strings: + * + * ex_printf -- ex printf routine + * EXCOOKIE -- the cookie passed to the ex printf routine + * ex_fflush -- ex flush routine + * + * and there are #defines (based on FWOPEN_NOT_AVAILABLE) in vi.h to set them + * to the corresponding stdio(3) routines if fwopen(3) is available. If it's + * not available, this file contains the routines that fake it for you. + */ + +/* + * fwopen -- + * Return an open file descriptor. + */ +FILE * +fwopen(sp, func) + SCR *sp; + void *func; +{ + return (fopen(_PATH_DEVNULL, "w")); +} + +#ifdef __STDC__ +#include +#else +#include +#endif + +static size_t off; +static char buf[1024]; + +/* + * ex_printf -- + * Ex's version of printf. + * + * XXX + * Hard limits -- if we get more than 1024 of formatted + * input in a write, it will be discarded. + */ +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 +{ + va_list ap; + int n; + char b1[1024]; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (sp->stdfp == stdout) + return (vfprintf(stdout, fmt, ap)); + + n = vsnprintf(b1, sizeof(b1), fmt, ap); + va_end(ap); + + if (n > 512 || n + off > sizeof(buf)) { + (void)sp->s_ex_write(sp, buf, off); + off = 0; + if (n > 512) { + (void)sp->s_ex_write(sp, b1, n); + return (n); + } + } + memmove(buf + off, b1, n); + off += n; + return (n); +} + +/* + * ex_fflush -- + * Ex's version of fflush. + */ +int +ex_fflush(sp) + SCR *sp; +{ + if (sp->stdfp == stdout) + return (fflush(stdout)); + + if (off) { + (void)sp->s_ex_write(sp, buf, off); + off = 0; + } + return (0); +} diff --git a/usr.bin/vi/clib/lockf.c b/usr.bin/vi/clib/lockf.c new file mode 100644 index 0000000000..a7f6a55bf2 --- /dev/null +++ b/usr.bin/vi/clib/lockf.c @@ -0,0 +1,27 @@ +#include +/* + * Include , not , the flock(2) + * #defines were found there on historical systems. + */ +#include + +#include +#include + +#include + +/* + * Use lockf(2) locking to fake flock(2) locking. + */ +int +flock(fd, operation) + int fd, operation; +{ + if (!lockf(fd, + operation & LOCK_UN ? F_ULOCK : + operation & LOCK_NB ? F_TLOCK : F_LOCK, 0)) + return (0); + if (errno == EACCES || errno == EAGAIN) + errno = EWOULDBLOCK; + return (-1); +} diff --git a/usr.bin/vi/clib/memmove.c b/usr.bin/vi/clib/memmove.c new file mode 100644 index 0000000000..f90b09c9b7 --- /dev/null +++ b/usr.bin/vi/clib/memmove.c @@ -0,0 +1,139 @@ +/*- + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#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. + */ +#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/usr.bin/vi/clib/memset.c b/usr.bin/vi/clib/memset.c new file mode 100644 index 0000000000..9c28b7b81a --- /dev/null +++ b/usr.bin/vi/clib/memset.c @@ -0,0 +1,130 @@ +/*- + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)memset.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include + +#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/usr.bin/vi/clib/mktemp.c b/usr.bin/vi/clib/mktemp.c new file mode 100644 index 0000000000..157c36fa36 --- /dev/null +++ b/usr.bin/vi/clib/mktemp.c @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include + +static int _gettemp(); + +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/usr.bin/vi/clib/pty.c b/usr.bin/vi/clib/pty.c new file mode 100644 index 0000000000..98001b17db --- /dev/null +++ b/usr.bin/vi/clib/pty.c @@ -0,0 +1,100 @@ +/*- + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)pty.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +openpty(amaster, aslave, name, termp, winp) + int *amaster, *aslave; + char *name; + struct termios *termp; + struct winsize *winp; +{ + static char line[] = "/dev/ptyXX"; + register const 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[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 ONLY_44BSD_HAS_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); + if (winp) + (void) ioctl(slave, TIOCSWINSZ, + (char *)winp); + return (0); + } + (void) close(master); + line[5] = 'p'; + } + } + } + errno = ENOENT; /* out of ptys */ + return (-1); +} diff --git a/usr.bin/vi/clib/pty_s5r4.c b/usr.bin/vi/clib/pty_s5r4.c new file mode 100644 index 0000000000..7fc83fdcb4 --- /dev/null +++ b/usr.bin/vi/clib/pty_s5r4.c @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Brian Hirt. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pty_s5r4.c 8.1 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +/* + * 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; + + strcpy(pts_name,"/dev/ptmx"); + 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); +} + +int +openpty(amaster, aslave, name, termp, winp) + int *amaster, *aslave; + char *name; + struct termios *termp; +{ + register 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); + if (winp) + (void) ioctl(slave, TIOCSWINSZ, (char *)winp); + + return (0); +} diff --git a/usr.bin/vi/clib/realloc.c b/usr.bin/vi/clib/realloc.c new file mode 100644 index 0000000000..11e5cd28c5 --- /dev/null +++ b/usr.bin/vi/clib/realloc.c @@ -0,0 +1,11 @@ +#include + +#include + +void * +__fix_realloc(p, n) + void *p; + size_t n; +{ + return (p == 0 ? malloc(n) : realloc(p, n)); +} diff --git a/usr.bin/vi/clib/siglist.c b/usr.bin/vi/clib/siglist.c new file mode 100644 index 0000000000..be6334dd40 --- /dev/null +++ b/usr.bin/vi/clib/siglist.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)siglist.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +const char *const sys_signame[NSIG] = { + "Signal 0", + "hup", /* SIGHUP */ + "int", /* SIGINT */ + "quit", /* SIGQUIT */ + "ill", /* SIGILL */ + "trap", /* SIGTRAP */ + "abrt", /* SIGABRT */ + "emt", /* SIGEMT */ + "fpe", /* SIGFPE */ + "kill", /* SIGKILL */ + "bus", /* SIGBUS */ + "segv", /* SIGSEGV */ + "sys", /* SIGSYS */ + "pipe", /* SIGPIPE */ + "alrm", /* SIGALRM */ + "term", /* SIGTERM */ + "urg", /* SIGURG */ + "stop", /* SIGSTOP */ + "tstp", /* SIGTSTP */ + "cont", /* SIGCONT */ + "chld", /* SIGCHLD */ + "ttin", /* SIGTTIN */ + "ttou", /* SIGTTOU */ + "io", /* SIGIO */ + "xcpu", /* SIGXCPU */ + "xfsz", /* SIGXFSZ */ + "vtalrm", /* SIGVTALRM */ + "prof", /* SIGPROF */ + "winch", /* SIGWINCH */ + "info", /* SIGINFO */ + "usr1", /* SIGUSR1 */ + "usr2", /* SIGUSR2 */ +}; + +const char *const sys_siglist[NSIG] = { + "Signal 0", + "Hangup", /* SIGHUP */ + "Interrupt", /* SIGINT */ + "Quit", /* SIGQUIT */ + "Illegal instruction", /* SIGILL */ + "Trace/BPT trap", /* SIGTRAP */ + "Abort trap", /* SIGABRT */ + "EMT trap", /* SIGEMT */ + "Floating point exception", /* SIGFPE */ + "Killed", /* SIGKILL */ + "Bus error", /* SIGBUS */ + "Segmentation fault", /* SIGSEGV */ + "Bad system call", /* SIGSYS */ + "Broken pipe", /* SIGPIPE */ + "Alarm clock", /* SIGALRM */ + "Terminated", /* SIGTERM */ + "Urgent I/O condition", /* SIGURG */ + "Suspended (signal)", /* SIGSTOP */ + "Suspended", /* SIGTSTP */ + "Continued", /* SIGCONT */ + "Child exited", /* SIGCHLD */ + "Stopped (tty input)", /* SIGTTIN */ + "Stopped (tty output)", /* SIGTTOU */ + "I/O possible", /* SIGIO */ + "Cputime limit exceeded", /* SIGXCPU */ + "Filesize limit exceeded", /* SIGXFSZ */ + "Virtual timer expired", /* SIGVTALRM */ + "Profiling timer expired", /* SIGPROF */ + "Window size changes", /* SIGWINCH */ + "Information request", /* SIGINFO */ + "User defined signal 1", /* SIGUSR1 */ + "User defined signal 2" /* SIGUSR2 */ +}; diff --git a/usr.bin/vi/clib/snprintf.c b/usr.bin/vi/clib/snprintf.c new file mode 100644 index 0000000000..2863fa28d6 --- /dev/null +++ b/usr.bin/vi/clib/snprintf.c @@ -0,0 +1,54 @@ +#include +#include + +#include + +#ifdef __STDC__ +#include +#else +#include +#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; + char *rp; + int rval; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif +#ifdef VSPRINTF_CHARSTAR + rp = vsprintf(str, fmt, ap); + va_end(ap); + return (strlen(rp)); +#else + rval = vsprintf(str, fmt, ap); + va_end(ap); + return (rval); +#endif +} + +int +vsnprintf(str, n, fmt, ap) + char *str; + size_t n; + const char *fmt; + va_list ap; +{ +#ifdef VSPRINTF_CHARSTAR + return (strlen(vsprintf(str, fmt, ap))); +#else + return (vsprintf(str, fmt, ap)); +#endif +} diff --git a/usr.bin/vi/clib/strdup.c b/usr.bin/vi/clib/strdup.c new file mode 100644 index 0000000000..6fa50ceecf --- /dev/null +++ b/usr.bin/vi/clib/strdup.c @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +char * +strdup(str) + const char *str; +{ + size_t len; + char *copy; + + len = strlen(str) + 1; + if (!(copy = malloc((u_int)len))) + return (NULL); + bcopy(str, copy, len); + return (copy); +} diff --git a/usr.bin/vi/clib/strerror.c b/usr.bin/vi/clib/strerror.c new file mode 100644 index 0000000000..53f374bdf7 --- /dev/null +++ b/usr.bin/vi/clib/strerror.c @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +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/usr.bin/vi/clib/strsep.c b/usr.bin/vi/clib/strsep.c new file mode 100644 index 0000000000..18dc3987e6 --- /dev/null +++ b/usr.bin/vi/clib/strsep.c @@ -0,0 +1,80 @@ +/*- + * 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 +#include +#include + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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. + */ +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/usr.bin/vi/clib/strtoul.c b/usr.bin/vi/clib/strtoul.c new file mode 100644 index 0000000000..eed7cdccef --- /dev/null +++ b/usr.bin/vi/clib/strtoul.c @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#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. + */ +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/usr.bin/vi/cut.c b/usr.bin/vi/cut.c new file mode 100644 index 0000000000..1638296b54 --- /dev/null +++ b/usr.bin/vi/cut.c @@ -0,0 +1,525 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cut.c 8.19 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "vi.h" + +static int cb_line __P((SCR *, EXF *, recno_t, size_t, size_t, TEXT **)); +static int cb_rotate __P((SCR *)); + +/* + * cut -- + * Put a range of lines/columns into a buffer. + * + * There are two buffer areas, both found in the global structure. The first + * is the linked list of all the buffers the user has named, the second is the + * default buffer storage. There is a pointer, too, which is the current + * default buffer, i.e. it may point to the default buffer or a named buffer + * depending on into what buffer the last text was cut. In both delete and + * yank operations, text is cut into either the buffer named by the user, or + * the default buffer. If it's a delete of information on more than a single + * line, the contents of the numbered buffers are rotated up one, the contents + * of the buffer named '9' are discarded, and the text is also cut into the + * buffer named '1'. + * + * In all cases, upper-case buffer names are the same as lower-case names, + * with the exception that they cause the buffer to be appended to instead + * of replaced. + * + * !!! + * The contents of the default buffer would disappear after most operations in + * historic vi. It's unclear that this is useful, so we don't bother. + * + * When users explicitly cut text into the numeric buffers, historic vi became + * genuinely strange. I've never been able to figure out what was supposed to + * happen. It behaved differently if you deleted text than if you yanked text, + * and, in the latter case, the text was appended to the buffer instead of + * replacing the contents. Hopefully it's not worth getting right. + */ +int +cut(sp, ep, cbp, namep, fm, tm, flags) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + int flags; + MARK *fm, *tm; +{ + CHAR_T name; + TEXT *tp; + recno_t lno; + size_t len; + int append, namedbuffer, setdefcb; + +#if defined(DEBUG) && 0 + TRACE(sp, "cut: from {%lu, %d}, to {%lu, %d}%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, + LF_ISSET(CUT_LINEMODE) ? " LINE MODE" : ""); +#endif + if (cbp == NULL) { + if (LF_ISSET(CUT_DELETE) && fm->lno != tm->lno) { + (void)cb_rotate(sp); + name = '1'; + goto defcb; + } + if (namep == NULL) { + cbp = sp->gp->dcb_store; + append = namedbuffer = 0; + setdefcb = 1; + } else { + name = *namep; +defcb: CBNAME(sp, cbp, name); + append = isupper(name); + namedbuffer = setdefcb = 1; + } + } else + append = namedbuffer = setdefcb = 0; + + /* + * If this is a new buffer, create it and add it into the list. + * Otherwise, if it's not an append, free its current contents. + */ + if (cbp == NULL) { + CALLOC(sp, cbp, CB *, 1, sizeof(CB)); + cbp->name = name; + CIRCLEQ_INIT(&cbp->textq); + if (namedbuffer) { + LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q); + } else + sp->gp->dcb_store = cbp; + } else if (!append) { + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + } + + /* In line mode, it's pretty easy, just cut the lines. */ + if (LF_ISSET(CUT_LINEMODE)) { + for (lno = fm->lno; lno <= tm->lno; ++lno) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + cbp->flags |= CB_LMODE; + } else { + /* Get the first line. */ + len = fm->lno < tm->lno ? 0 : tm->cno - fm->cno; + if (cb_line(sp, ep, fm->lno, fm->cno, len, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + + /* Get the intermediate lines. */ + for (lno = fm->lno; ++lno < tm->lno;) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + + /* Get the last line. */ + if (tm->lno > fm->lno && tm->cno > 0) { + if (cb_line(sp, ep, lno, 0, tm->cno, &tp)) { +mem: if (append) + msgq(sp, M_ERR, + "Contents of %s buffer lost.", + charname(sp, name)); + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + return (1); + } + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + } + if (setdefcb) + sp->gp->dcbp = cbp; /* Repoint default buffer. */ + return (0); +} + +/* + * cb_rotate -- + * Rotate the numbered buffers up one. + */ +static int +cb_rotate(sp) + SCR *sp; +{ + CB *cbp, *del_cbp; + + del_cbp = NULL; + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) + switch(cbp->name) { + case '1': + cbp->name = '2'; + break; + case '2': + cbp->name = '3'; + break; + case '3': + cbp->name = '4'; + break; + case '4': + cbp->name = '5'; + break; + case '5': + cbp->name = '6'; + break; + case '6': + cbp->name = '7'; + break; + case '7': + cbp->name = '8'; + break; + case '8': + cbp->name = '9'; + break; + case '9': + del_cbp = cbp; + break; + } + if (del_cbp != NULL) { + LIST_REMOVE(del_cbp, q); + text_lfree(&del_cbp->textq); + FREE(del_cbp, sizeof(CB)); + } + return (0); +} + +/* + * cb_line -- + * Cut a portion of a single line. + */ +static int +cb_line(sp, ep, lno, fcno, clen, newp) + SCR *sp; + EXF *ep; + recno_t lno; + size_t fcno, clen; + TEXT **newp; +{ + TEXT *tp; + size_t len; + char *p; + + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + if ((*newp = tp = text_init(sp, NULL, 0, len)) == NULL) + return (1); + + /* + * A length of zero means to cut from the MARK to the end + * of the line. + */ + if (len != 0) { + if (clen == 0) + clen = len - fcno; + memmove(tp->lb, p + fcno, clen); + tp->len = clen; + } + return (0); +} + +/* + * text_init -- + * Allocate a new TEXT structure. + */ +TEXT * +text_init(sp, p, len, total_len) + SCR *sp; + const char *p; + size_t len, total_len; +{ + TEXT *tp; + + MALLOC(sp, tp, TEXT *, sizeof(TEXT)); + if (tp == NULL) + return (NULL); + /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */ + if (tp->lb_len = total_len) { + MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len); + if (tp->lb == NULL) { + free(tp); + return (NULL); + } + if (p != NULL && len != 0) + memmove(tp->lb, p, len); + } else + tp->lb = NULL; + tp->len = len; + tp->ai = tp->insert = tp->offset = tp->owrite = 0; + tp->wd = NULL; + tp->wd_len = 0; + return (tp); +} + +/* + * text_lfree -- + * Free a chain of text structures. + */ +void +text_lfree(headp) + TEXTH *headp; +{ + TEXT *tp; + + while ((tp = headp->cqh_first) != (void *)headp) { + CIRCLEQ_REMOVE(headp, tp, q); + text_free(tp); + } +} + +/* + * text_free -- + * Free a text structure. + */ +void +text_free(tp) + TEXT *tp; +{ + if (tp->lb != NULL) + FREE(tp->lb, tp->lb_len); + if (tp->wd != NULL) + FREE(tp->wd, tp->wd_len); + FREE(tp, sizeof(TEXT)); +} + +/* + * put -- + * Put text buffer contents into the file. + * + * !!! + * Historically, pasting into a file with no lines in vi would preserve + * the single blank line. This is almost certainly a result of the fact + * that historic vi couldn't deal with a file that had no lines in it. + * This implementation treats that as a bug, and does not retain the + * blank line. + */ +int +put(sp, ep, cbp, namep, cp, rp, append) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + MARK *cp, *rp; + int append; +{ + CHAR_T name; + TEXT *ltp, *tp; + recno_t lno; + size_t blen, clen, len; + char *bp, *p, *t; + + if (cbp == NULL) + if (namep == NULL) { + cbp = sp->gp->dcbp; + if (cbp == NULL) { + msgq(sp, M_ERR, "The default buffer is empty."); + return (1); + } + } else { + name = *namep; + CBNAME(sp, cbp, name); + if (cbp == NULL) { + msgq(sp, M_ERR, + "Buffer %s is empty.", charname(sp, name)); + return (1); + } + } + tp = cbp->textq.cqh_first; + + /* + * It's possible to do a put into an empty file, meaning that the + * cut buffer simply becomes the file. It's a special case so + * that we can ignore it in general. + * + * Historical practice is that the cursor ends up on the first + * non-blank character of the first line inserted. + */ + if (cp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) { + for (; tp != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->lno = 1; + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + } + + /* If a line mode buffer, append each new line into the file. */ + if (F_ISSET(cbp, CB_LMODE)) { + lno = append ? cp->lno : cp->lno - 1; + rp->lno = lno + 1; + for (; tp != (void *)&cbp->textq; ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + + /* + * If buffer was cut in character mode, replace the current line with + * one built from the portion of the first line to the left of the + * split plus the first line in the CB. Append each intermediate line + * in the CB. Append a line built from the portion of the first line + * to the right of the split plus the last line in the CB. + * + * Get the first line. + */ + lno = cp->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tp->len + len + 1); + t = bp; + + /* Original line, left of the split. */ + if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) { + memmove(bp, p, clen); + p += clen; + t += clen; + } + + /* First line from the CB. */ + memmove(t, tp->lb, tp->len); + t += tp->len; + + /* Calculate length left in original line. */ + clen = len ? len - cp->cno - (append ? 1 : 0) : 0; + + /* + * If no more lines in the CB, append the rest of the original + * line and quit. Otherwise, build the last line before doing + * the intermediate lines, because the line changes will lose + * the cached line. + */ + if (tp->q.cqe_next == (void *)&cbp->textq) { + /* + * Historical practice is that if a non-line mode put + * is inside a single line, the cursor ends up on the + * last character inserted. + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + if (clen > 0) { + memmove(t, p, clen); + t += clen; + } + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + } else { + /* + * Have to build both the first and last lines of the + * put before doing any sets or we'll lose the cached + * line. Build both the first and last lines in the + * same buffer, so we don't have to have another buffer + * floating around. + * + * Last part of original line; check for space, reset + * the pointer into the buffer. + */ + ltp = cbp->textq.cqh_last; + len = t - bp; + ADD_SPACE_RET(sp, bp, blen, ltp->len + clen); + t = bp + len; + + /* Add in last part of the CB. */ + memmove(t, ltp->lb, ltp->len); + if (clen) + memmove(t + ltp->len, p, clen); + clen += ltp->len; + + /* + * Now: bp points to the first character of the first + * line, t points to the last character of the last + * line, t - bp is the length of the first line, and + * clen is the length of the last. Just figured you'd + * want to know. + * + * Output the line replacing the original line. + */ + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + + /* + * Historical practice is that if a non-line mode put + * covers multiple lines, the cursor ends up on the + * first character inserted. (Of course.) + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + /* Output any intermediate lines in the CB. */ + for (tp = tp->q.cqe_next; + tp->q.cqe_next != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + goto mem; + + if (file_aline(sp, ep, 1, lno, t, clen)) { +mem: FREE_SPACE(sp, bp, blen); + return (1); + } + } + FREE_SPACE(sp, bp, blen); + + /* Reporting... */ +ret: sp->rptlines[L_PUT] += lno - cp->lno; + + return (0); +} diff --git a/usr.bin/vi/cut.h b/usr.bin/vi/cut.h new file mode 100644 index 0000000000..8ade88f056 --- /dev/null +++ b/usr.bin/vi/cut.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cut.h 8.11 (Berkeley) 1/11/94 + */ + +typedef struct _texth TEXTH; /* TEXT list head structure. */ +CIRCLEQ_HEAD(_texth, _text); + +/* Cut buffers. */ +struct _cb { + LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */ + TEXTH textq; /* Linked list of TEXT structures. */ + CHAR_T name; /* Cut buffer name. */ + size_t len; /* Total length of cut text. */ + +#define CB_LMODE 0x01 /* Cut was in line mode. */ + u_char flags; +}; + +/* Lines/blocks of text. */ +struct _text { /* Text: a linked list of lines. */ + CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */ + char *lb; /* Line buffer. */ + size_t lb_len; /* Line buffer length. */ + size_t len; /* Line length. */ + + /* These fields are used by the vi text input routine. */ + recno_t lno; /* 1-N: line number. */ + size_t ai; /* 0-N: autoindent bytes. */ + size_t insert; /* 0-N: bytes to insert (push). */ + size_t offset; /* 0-N: initial, unerasable bytes. */ + size_t owrite; /* 0-N: bytes to overwrite. */ + + /* These fields are used by the ex text input routine. */ + u_char *wd; /* Width buffer. */ + size_t wd_len; /* Width buffer length. */ +}; + +/* + * Get named buffer 'name'. + * Translate upper-case buffer names to lower-case buffer names. + */ +#define CBNAME(sp, cbp, name) { \ + CHAR_T __name; \ + __name = isupper(name) ? tolower(name) : (name); \ + for (cbp = sp->gp->cutq.lh_first; \ + cbp != NULL; cbp = cbp->q.le_next) \ + if (cbp->name == __name) \ + break; \ +} + +#define CUT_DELETE 0x01 /* Delete (rotate numeric buffers). */ +#define CUT_LINEMODE 0x02 /* Cut in line mode. */ +int cut __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); +int delete __P((SCR *, EXF *, MARK *, MARK *, int)); +int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); + +void text_free __P((TEXT *)); +TEXT *text_init __P((SCR *, const char *, size_t, size_t)); +void text_lfree __P((TEXTH *)); diff --git a/usr.bin/vi/delete.c b/usr.bin/vi/delete.c new file mode 100644 index 0000000000..e8c0a5d80e --- /dev/null +++ b/usr.bin/vi/delete.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)delete.c 8.7 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include + +#include +#include +#include + +#include "vi.h" + +/* + * delete -- + * Delete a range of text. + */ +int +delete(sp, ep, fm, tm, lmode) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + int lmode; +{ + recno_t lno; + size_t blen, len, tlen; + char *bp, *p; + int eof; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete: from %lu/%d to %lu/%d%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, lmode ? " (LINE MODE)" : ""); +#endif + bp = NULL; + + /* Case 1 -- delete in line mode. */ + if (lmode) { + for (lno = tm->lno; lno >= fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + goto vdone; + } + + /* + * Case 2 -- delete to EOF. This is a special case because it's + * easier to pick it off than try and find it in the other cases. + */ + if (file_lline(sp, ep, &lno)) + return (1); + if (tm->lno >= lno) { + if (tm->lno == lno) { + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + eof = tm->cno >= len ? 1 : 0; + } else + eof = 1; + if (eof) { + for (lno = tm->lno; lno > fm->lno; --lno) { + if (file_dline(sp, ep, lno)) + return (1); + ++sp->rptlines[L_DELETED]; + } + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, fm->cno); + memmove(bp, p, fm->cno); + if (file_sline(sp, ep, fm->lno, bp, fm->cno)) + return (1); + goto done; + } + } + + /* Case 3 -- delete within a single line. */ + if (tm->lno == fm->lno) { + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, fm->cno); + memmove(bp + fm->cno, p + tm->cno, len - tm->cno); + if (file_sline(sp, ep, fm->lno, bp, len - (tm->cno - fm->cno))) + goto err; + goto done; + } + + /* + * Case 4 -- delete over multiple lines. + * + * Figure out how big a buffer we need. + */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + tlen = len; + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + return (1); + } + + /* + * XXX + * We can overflow memory here, if (len + tlen) > SIZE_T_MAX. The + * only portable way I've found to test is to depend on the overflow + * being less than the value. + */ + tlen += len; + if (len > tlen) { + msgq(sp, M_ERR, "Error: line length overflow"); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tlen); + + /* Copy the start partial line into place. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + goto err; + } + memmove(bp, p, fm->cno); + tlen = fm->cno; + + /* Copy the end partial line into place. */ + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + goto err; + } + memmove(bp + tlen, p + tm->cno, len - tm->cno); + tlen += len - tm->cno; + + /* Set the current line. */ + if (file_sline(sp, ep, fm->lno, bp, tlen)) + goto err; + + /* Delete the last and intermediate lines. */ + for (lno = tm->lno; lno > fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + + /* Reporting. */ +vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1; + +done: if (bp != NULL) + FREE_SPACE(sp, bp, blen); + + return (0); + + /* Free memory. */ +err: FREE_SPACE(sp, bp, blen); + + return (1); +} diff --git a/usr.bin/vi/exf.c b/usr.bin/vi/exf.c new file mode 100644 index 0000000000..0f9ac4ba0e --- /dev/null +++ b/usr.bin/vi/exf.c @@ -0,0 +1,690 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exf.c 8.65 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include +#include + +/* + * We include , because the flock(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 "vi.h" +#include "excmd.h" +#include "pathnames.h" + +/* + * file_add -- + * Insert a file name into the FREF list, if it doesn't already + * appear in it. + * + * !!! + * The "if it doesn't already appear" changes vi's semantics slightly. If + * you do a "vi foo bar", and then execute "next bar baz", the edit of bar + * will reflect the line/column of the previous edit session. Historic nvi + * did not do this. The change is a logical extension of the change where + * vi now remembers the last location in any file that it has ever edited, + * not just the previously edited file. + */ +FREF * +file_add(sp, frp_append, name, ignore) + SCR *sp; + FREF *frp_append; + CHAR_T *name; + int ignore; +{ + FREF *frp; + char *p; + + /* + * Return it if it already exists. Note that we test against the + * user's current name, whatever that happens to be, including if + * it's a temporary file. If the user is trying to set an argument + * list, the ignore argument will be on -- if we're ignoring the + * file turn off the ignore bit, so it's back in the argument list. + */ + if (name != NULL) + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) { + if (!ignore) + F_CLR(frp, FR_IGNORE); + return (frp); + } + + /* Allocate and initialize the FREF structure. */ + CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); + if (frp == NULL) + return (NULL); + + /* + * If no file name specified, or if the file name is a request + * for something temporary, file_init() will allocate the file + * name. Temporary files are always ignored. + */ +#define TEMPORARY_FILE_STRING "/tmp" + if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && + (frp->name = strdup(name)) == NULL) { + FREE(frp, sizeof(FREF)); + msgq(sp, M_SYSERR, NULL); + return (NULL); + } + + /* Only the initial argument list is "remembered". */ + if (ignore) + F_SET(frp, FR_IGNORE); + + /* Append into the chain of file names. */ + if (frp_append != NULL) { + CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q); + } else + CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q); + + return (frp); +} + +/* + * file_first -- + * Return the first file name for editing, if any. + */ +FREF * +file_first(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the first file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_next -- + * Return the next file name, if any. + */ +FREF * +file_next(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_prev -- + * Return the previous file name, if any. + */ +FREF * +file_prev(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_unedited -- + * Return if there are files that aren't ignored and are unedited. + */ +FREF * +file_unedited(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the next file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_EDITED | FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_init -- + * Start editing a file, based on the FREF structure. If successsful, + * let go of any previous file. Don't release the previous file until + * absolutely sure we have the new one. + */ +int +file_init(sp, frp, rcv_name, force) + SCR *sp; + FREF *frp; + char *rcv_name; + int force; +{ + EXF *ep; + RECNOINFO oinfo; + struct stat sb; + size_t psize; + int fd; + char *p, *oname, tname[MAXPATHLEN]; + + /* + * Required ep initialization: + * Flush the line caches. + * Default recover mail file fd to -1. + * Set initial EXF flag bits. + */ + CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); + ep->c_lno = ep->c_nlines = OOBLNO; + ep->rcv_fd = -1; + LIST_INIT(&ep->marks); + F_SET(ep, F_FIRSTMODIFY); + + /* + * If no name or backing file, create a backing temporary file, saving + * the temp file name so can later unlink it. Repoint the name to the + * temporary name (we display it to the user until they rename it). + * There are some games we play with the FR_FREE_TNAME and FR_NONAME + * flags (see ex/ex_file.c) to make sure that the temporary memory gets + * free'd up. + */ + if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) { + (void)snprintf(tname, sizeof(tname), + "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY)); + if ((fd = mkstemp(tname)) == -1) { + msgq(sp, M_SYSERR, "Temporary file"); + goto err; + } + (void)close(fd); + if ((frp->tname = strdup(tname)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(tname); + goto err; + } + oname = frp->tname; + psize = 4 * 1024; + F_SET(frp, FR_NEWFILE); + } else { + /* Try to keep it at 10 pages or less per file. */ + if (sb.st_size < 40 * 1024) + psize = 4 * 1024; + else if (sb.st_size < 320 * 1024) + psize = 32 * 1024; + else + psize = 64 * 1024; + + frp->mtime = sb.st_mtime; + + if (!S_ISREG(sb.st_mode)) + msgq(sp, M_ERR, + "Warning: %s is not a regular file.", oname); + } + + /* Set up recovery. */ + memset(&oinfo, 0, sizeof(RECNOINFO)); + oinfo.bval = '\n'; /* Always set. */ + oinfo.psize = psize; + oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; + if (rcv_name == NULL) { + if (rcv_tmp(sp, ep, FILENAME(frp))) + msgq(sp, M_ERR, + "Modifications not recoverable if the system crashes."); + else + oinfo.bfname = ep->rcv_path; + } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err; + } else { + oinfo.bfname = ep->rcv_path; + F_SET(ep, F_MODIFIED | F_RCV_ON); + } + + /* Open a db structure. */ + if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, + O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) { + msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name); + goto err; + } + + /* Init file marks. */ + if (mark_init(sp, ep)) + goto err; + + /* Start logging. */ + if (log_init(sp, ep)) + goto err; + + /* + * The -R flag, or doing a "set readonly" during a session causes + * all files edited during the session (using an edit command, or + * even using tags) to be marked read-only. Changing the file name + * (see ex/ex_file.c), clears this flag. + * + * Otherwise, try and figure out if a file is readonly. This is a + * dangerous thing to do. The kernel is the only arbiter of whether + * or not a file is writeable, and the best that a user program can + * do is guess. Obvious loopholes are files that are on a file system + * mounted readonly (access catches this one on a few systems), or + * alternate protection mechanisms, ACL's for example, that we can't + * portably check. Lots of fun, and only here because users whined. + * + * !!! + * Historic vi displayed the readonly message if none of the file + * write bits were set, or if an an access(2) call on the path + * failed. This seems reasonable. If the file is mode 444, root + * users may want to know that the owner of the file did not expect + * it to be written. + * + * Historic vi set the readonly bit if no write bits were set for + * a file, even if the access call would have succeeded. This makes + * the superuser force the write even when vi expects that it will + * succeed. I'm less supportive of this semantic, but it's historic + * practice and the conservative approach to vi'ing files as root. + * + * It would be nice if there was some way to update this when the user + * does a "^Z; chmod ...". The problem is that we'd first have to + * distinguish between readonly bits set because of file permissions + * and those set for other reasons. That's not too hard, but deciding + * when to reevaluate the permissions is trickier. An alternative + * might be to turn off the readonly bit if the user forces a write + * and it succeeds. + * + * XXX + * Access(2) doesn't consider the effective uid/gid values. This + * probably isn't a problem for vi when it's running standalone. + */ + if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) && + (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || + access(FILENAME(frp), W_OK))) + F_SET(frp, FR_RDONLY); + else + F_CLR(frp, FR_RDONLY); + + /* + * Close the previous file; if that fails, close the new one + * and run for the border. + */ + if (sp->ep != NULL && file_end(sp, sp->ep, force)) { + (void)file_end(sp, ep, 1); + goto err; + } + + /* + * 4.4BSD supports locking in the open call, other systems don't. + * Since the user can't interrupt us between the open and here, + * it's a don't care. + * + * !!! + * We need to distinguish a lock not being available for the file + * from the file system not supporting locking. Assume that EAGAIN + * or EWOULDBLOCK is the former. There isn't a portable way to do + * this. + * + * XXX + * The locking is flock(2) style, not fcntl(2). The latter is known + * to fail badly on some systems, and its only advantage is that it + * occasionally works over NFS. + */ + if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB)) + if (errno == EAGAIN || errno == EWOULDBLOCK) { + msgq(sp, M_INFO, + "%s already locked, session is read-only", oname); + F_SET(frp, FR_RDONLY); + } else + msgq(sp, M_VINFO, "%s cannot be locked", oname); + + /* + * Set the previous file pointer and the alternate file name to be + * the file we're about to discard. + * + * !!! + * If the current file was a temporary file, the call to file_end() + * unlinked it and free'd the name. So, there is no previous file, + * and there is no alternate file name. This matches historical + * practice, although in historical vi it could only happen as the + * result of the initial command, i.e. if vi was execute without a + * file name. + */ + if (sp->frp != NULL) { + p = FILENAME(sp->frp); + if (p == NULL) + sp->p_frp = NULL; + else + sp->p_frp = sp->frp; + set_alt_name(sp, p); + } + + /* The new file has now been officially edited. */ + F_SET(frp, FR_EDITED); + + /* Switch... */ + ++ep->refcnt; + sp->ep = ep; + sp->frp = frp; + return (0); + +err: if (frp->tname != NULL) { + (void)unlink(frp->tname); + free(frp->tname); + frp->tname = NULL; + } + if (ep->rcv_path != NULL) { + free(ep->rcv_path); + ep->rcv_path = NULL; + } + FREE(ep, sizeof(EXF)); + return (1); +} + +/* + * file_end -- + * Stop editing a file. + */ +int +file_end(sp, ep, force) + SCR *sp; + EXF *ep; + int force; +{ + FREF *frp; + + /* + * + * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT! + * + * Save the cursor location. + * + * XXX + * It would be cleaner to do this somewhere else, but by the time + * ex or vi knows that we're changing files it's already happened. + */ + frp = sp->frp; + frp->lno = sp->lno; + frp->cno = sp->cno; + F_SET(frp, FR_CURSORSET); + + /* If multiply referenced, just decrement the count and return. */ + if (--ep->refcnt != 0) + return (0); + + /* Close the db structure. */ + if (ep->db->close != NULL && ep->db->close(ep->db) && !force) { + msgq(sp, M_ERR, + "%s: close: %s", FILENAME(frp), strerror(errno)); + ++ep->refcnt; + return (1); + } + + /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */ + + /* Stop logging. */ + (void)log_end(sp, ep); + + /* Free up any marks. */ + mark_end(sp, ep); + + /* + * Delete the recovery files, close the open descriptor, + * free recovery memory. + */ + if (!F_ISSET(ep, F_RCV_NORM)) { + if (ep->rcv_path != NULL && unlink(ep->rcv_path)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_path, strerror(errno)); + if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_mpath, strerror(errno)); + } + if (ep->rcv_fd != -1) + (void)close(ep->rcv_fd); + if (ep->rcv_path != NULL) + free(ep->rcv_path); + if (ep->rcv_mpath != NULL) + free(ep->rcv_mpath); + + /* + * Unlink any temporary file, file name. We also turn on the + * ignore bit at this point, because it was a "created" file, + * not an argument file. + */ + if (frp->tname != NULL) { + if (unlink(frp->tname)) + msgq(sp, M_ERR, + "%s: remove: %s", frp->tname, strerror(errno)); + free(frp->tname); + frp->tname = NULL; + + if (frp->name == NULL && frp->cname == NULL) + F_SET(frp, FR_IGNORE); + } + /* Free the EXF structure. */ + FREE(ep, sizeof(EXF)); + return (0); +} + +/* + * file_write -- + * Write the file to disk. Historic vi had fairly convoluted + * semantics for whether or not writes would happen. That's + * why all the flags. + */ +int +file_write(sp, ep, fm, tm, name, flags) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + char *name; + int flags; +{ + struct stat sb; + FILE *fp; + FREF *frp; + MARK from, to; + u_long nlno, nch; + int fd, oflags, rval; + char *msg; + + /* + * Don't permit writing to temporary files. The problem is that + * if it's a temp file, and the user does ":wq", we write and quit, + * unlinking the temporary file. Not what the user had in mind + * at all. This test cannot be forced. + */ + frp = sp->frp; + if (name == NULL && frp->cname == NULL && frp->name == NULL) { + msgq(sp, M_ERR, "No filename to which to write."); + return (1); + } + + /* Can't write files marked read-only, unless forced. */ + if (!LF_ISSET(FS_FORCE) && + name == NULL && F_ISSET(frp, FR_RDONLY)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Read-only file, not written; use ! to override."); + else + msgq(sp, M_ERR, + "Read-only file, not written."); + return (1); + } + + /* If not forced, not appending, and "writeany" not set ... */ + if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { + /* Don't overwrite anything but the original file. */ + if (name != NULL) { + if (!stat(name, &sb)) + goto exists; + } else if (frp->cname != NULL && + !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) { + name = frp->cname; +exists: if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "%s exists, not written; use ! to override.", name); + else + msgq(sp, M_ERR, + "%s exists, not written.", name); + return (1); + } + + /* + * Don't write part of any existing file. Only test for the + * original file, the previous test catches anything else. + */ + if (!LF_ISSET(FS_ALL) && name == NULL && + frp->cname == NULL && !stat(frp->name, &sb)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Use ! to write a partial file."); + else + msgq(sp, M_ERR, "Partial file, not written."); + return (1); + } + } + + /* + * Figure out if the file already exists -- if it doesn't, we display + * the "new file" message. The stat might not be necessary, but we + * just repeat it because it's easier than hacking the previous tests. + * The information is only used for the user message and modification + * time test, so we can ignore the obvious race condition. + * + * If the user is overwriting a file other than the original file, and + * O_WRITEANY was what got us here (neither force nor append was set), + * display the "existing file" messsage. Since the FR_CHANGEWRITE flag + * is set on a successful write, the message only appears once when the + * user changes a file name. This is historic practice. + * + * One final test. If we're not forcing or appending, and we have a + * saved modification time, stop the user if it's been written since + * we last edited or wrote it, and make them force it. + */ + if (stat(name == NULL ? FILENAME(frp) : name, &sb)) + msg = ": new file"; + else { + msg = ""; + if (!LF_ISSET(FS_FORCE | FS_APPEND)) { + if (frp->mtime && sb.st_mtime > frp->mtime) { + msgq(sp, M_ERR, + "%s: file modified more recently than this copy%s.", + name == NULL ? frp->name : name, + LF_ISSET(FS_POSSIBLE) ? + "; use ! to override" : ""); + return (1); + } + if (name != NULL || + !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL) + msg = ": existing file"; + } + } + + /* We no longer care where the name came from. */ + if (name == NULL) + name = FILENAME(frp); + + /* Set flags to either append or truncate. */ + oflags = O_CREAT | O_WRONLY; + if (LF_ISSET(FS_APPEND)) + oflags |= O_APPEND; + else + oflags |= O_TRUNC; + + /* Open the file. */ + if ((fd = open(name, oflags, DEFFILEMODE)) < 0) { + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Use stdio for buffering. */ + if ((fp = fdopen(fd, "w")) == NULL) { + (void)close(fd); + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Build fake addresses, if necessary. */ + if (fm == NULL) { + from.lno = 1; + from.cno = 0; + fm = &from; + if (file_lline(sp, ep, &to.lno)) + return (1); + to.cno = 0; + tm = &to; + } + + /* Write the file. */ + rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch); + + /* + * Save the new last modification time -- even if the write fails + * we re-init the time if we wrote anything. That way the user can + * clean up the disk and rewrite without having to force it. + */ + if (nlno || nch) + frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime; + + /* If the write failed, complain loudly. */ + if (rval) { + if (!LF_ISSET(FS_APPEND)) + msgq(sp, M_ERR, "%s: WARNING: file truncated!", name); + return (1); + } + + /* + * Once we've actually written the file, it doesn't matter that the + * file name was changed -- if it was, we've already whacked it. + */ + F_SET(frp, FR_CHANGEWRITE); + + /* If wrote the entire file, clear the modified bit. */ + if (LF_ISSET(FS_ALL)) + F_CLR(ep, F_MODIFIED); + + msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.", + name, msg, nlno, nlno == 1 ? "" : "s", nch); + + return (0); +} diff --git a/usr.bin/vi/exf.h b/usr.bin/vi/exf.h new file mode 100644 index 0000000000..5049f3ea24 --- /dev/null +++ b/usr.bin/vi/exf.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exf.h 8.20 (Berkeley) 12/28/93 + */ + /* Undo direction. */ +/* + * exf -- + * The file structure. + */ +struct _exf { + int refcnt; /* Reference count. */ + + /* Underlying database state. */ + DB *db; /* File db structure. */ + char *c_lp; /* Cached line. */ + size_t c_len; /* Cached line length. */ + recno_t c_lno; /* Cached line number. */ + recno_t c_nlines; /* Cached lines in the file. */ + + DB *log; /* Log db structure. */ + char *l_lp; /* Log buffer. */ + size_t l_len; /* Log buffer length. */ + recno_t l_high; /* Log last + 1 record number. */ + recno_t l_cur; /* Log current record number. */ + MARK l_cursor; /* Log cursor position. */ + enum direction lundo; /* Last undo direction. */ + + LIST_HEAD(_markh, _mark) marks; /* Linked list of file MARK's. */ + + /* + * Paths for the recovery mail file and the vi recovery file and + * a file descriptor for the former. We keep a file descriptor + * to the recovery file open and locked, while the file is in use. + * This allows the recovery option to distinguish between files + * that are live, and those that should be recovered. + * + * F_RCV_ON is set as long as we believe that the file is recoverable. + * This doesn't mean that any initialization has been done, however. + * If F_RCV_NORM is not set and rcv_path and rcv_mpath are not NULL, + * they are unlinked on file exit. If not NULL they are free'd on file + * exit. On file exit, if rcv_fd is not -1, it is closed. + */ +#define RCV_PERIOD 120 /* Sync every two minutes. */ + char *rcv_path; /* Recover file name. */ + char *rcv_mpath; /* Recover mail file name. */ + int rcv_fd; /* Locked mail file descriptor. */ + +#define F_FIRSTMODIFY 0x001 /* File not yet modified. */ +#define F_MODIFIED 0x002 /* File is currently dirty. */ +#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */ +#define F_NOLOG 0x008 /* Logging turned off. */ +#define F_RCV_ALRM 0x010 /* File needs to be synced. */ +#define F_RCV_NORM 0x020 /* Don't delete recovery files. */ +#define F_RCV_ON 0x040 /* Recovery is possible. */ +#define F_UNDO 0x080 /* No change since last undo. */ + u_int flags; +}; + +/* Flags to file_write(). */ +#define FS_ALL 0x01 /* Write the entire file. */ +#define FS_APPEND 0x02 /* Append to the file. */ +#define FS_FORCE 0x04 /* Force is set. */ +#define FS_POSSIBLE 0x08 /* Force could be set. */ + +#define GETLINE_ERR(sp, lno) { \ + msgq((sp), M_ERR, \ + "Error: %s/%d: unable to retrieve line %u.", \ + tail(__FILE__), __LINE__, (lno)); \ +} + +/* FREF routines. */ +FREF *file_add __P((SCR *, FREF *, CHAR_T *, int)); +FREF *file_first __P((SCR *)); +FREF *file_next __P((SCR *, FREF *)); +FREF *file_prev __P((SCR *, FREF *)); +FREF *file_unedited __P((SCR *)); + +/* EXF routines. */ +int file_end __P((SCR *, EXF *, int)); +int file_init __P((SCR *, FREF *, char *, int)); +int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int)); + +/* Recovery routines. */ +void rcv_hup __P((void)); +int rcv_init __P((SCR *, EXF *)); +int rcv_list __P((SCR *)); +int rcv_read __P((SCR *, char *)); +int rcv_sync __P((SCR *, EXF *)); +void rcv_term __P((void)); +int rcv_tmp __P((SCR *, EXF *, char *)); + +/* DB interface routines */ +int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t)); +int file_dline __P((SCR *, EXF *, recno_t)); +char *file_gline __P((SCR *, EXF *, recno_t, size_t *)); +int file_iline __P((SCR *, EXF *, recno_t, char *, size_t)); +int file_lline __P((SCR *, EXF *, recno_t *)); +char *file_rline __P((SCR *, EXF *, recno_t, size_t *)); +int file_sline __P((SCR *, EXF *, recno_t, char *, size_t)); diff --git a/usr.bin/vi/gs.h b/usr.bin/vi/gs.h new file mode 100644 index 0000000000..86af7299b3 --- /dev/null +++ b/usr.bin/vi/gs.h @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)gs.h 8.26 (Berkeley) 1/9/94 + */ + +struct _gs { + CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */ + CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */ + + mode_t origmode; /* Original terminal mode. */ + struct termios + original_termios; /* Original terminal values. */ + struct termios + s5_curses_botch; /* System V curses workaround. */ + + MSGH msgq; /* User message list. */ + + char *tmp_bp; /* Temporary buffer. */ + size_t tmp_blen; /* Size of temporary buffer. */ + +#ifdef DEBUG + FILE *tracefp; /* Trace file pointer. */ +#endif + +/* INFORMATION SHARED BY ALL SCREENS. */ + IBUF *tty; /* Key input buffer. */ + + CB *dcbp; /* Default cut buffer pointer. */ + CB *dcb_store; /* Default cut buffer storage. */ + LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */ + +#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */ + LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */ + bitstr_t bit_decl(seqb, MAX_BIT_SEQ); + +#define term_key_val(sp, ch) \ + ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \ + (ch) > sp->gp->max_special ? 0 : __term_key_val(sp, ch)) +#define MAX_FAST_KEY 255 /* Max + 1 fast check character.*/ + CHAR_T max_special; /* Max special character. */ + u_char *special_key; /* Fast lookup table. */ + CHNAME const *cname; /* Display names of ASCII characters. */ + +#define G_ABBREV 0x00001 /* If have abbreviations. */ +#define G_BELLSCHED 0x00002 /* Bell scheduled. */ +#define G_CURSES_INIT 0x00004 /* Curses: initialized. */ +#define G_CURSES_S5CB 0x00008 /* Curses: s5_curses_botch set. */ +#define G_ISFROMTTY 0x00010 /* Reading from a tty. */ +#define G_RECOVER_SET 0x00020 /* Recover system initialized. */ +#define G_SETMODE 0x00040 /* Tty mode changed. */ +#define G_SIGALRM 0x00080 /* SIGALRM arrived. */ +#define G_SIGHUP 0x00100 /* SIGHUP arrived. */ +#define G_SIGTERM 0x00200 /* SIGTERM arrived. */ +#define G_SIGWINCH 0x00400 /* SIGWINCH arrived. */ +#define G_SLEEPING 0x00800 /* Asleep (die on signal). */ +#define G_SNAPSHOT 0x01000 /* Always snapshot files. */ +#define G_TMP_INUSE 0x02000 /* Temporary buffer in use. */ + u_int flags; +}; + +extern GS *__global_list; /* List of screens. */ diff --git a/usr.bin/vi/include/bitstring.h b/usr.bin/vi/include/bitstring.h new file mode 100644 index 0000000000..88437e7fb9 --- /dev/null +++ b/usr.bin/vi/include/bitstring.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Vixie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 + */ + +#ifndef _BITSTRING_H_ +#define _BITSTRING_H_ + +typedef unsigned char bitstr_t; + +/* internal macros */ + /* byte of the bitstring bit is in */ +#define _bit_byte(bit) \ + ((bit) >> 3) + + /* mask for the bit within its byte */ +#define _bit_mask(bit) \ + (1 << ((bit)&0x7)) + +/* external macros */ + /* bytes in a bitstring of nbits bits */ +#define bitstr_size(nbits) \ + ((((nbits) - 1) >> 3) + 1) + + /* allocate a bitstring */ +#define bit_alloc(nbits) \ + (bitstr_t *)calloc(1, \ + (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t)) + + /* allocate a bitstring on the stack */ +#define bit_decl(name, nbits) \ + (name)[bitstr_size(nbits)] + + /* is bit N of bitstring name set? */ +#define bit_test(name, bit) \ + ((name)[_bit_byte(bit)] & _bit_mask(bit)) + + /* set bit N of bitstring name */ +#define bit_set(name, bit) \ + (name)[_bit_byte(bit)] |= _bit_mask(bit) + + /* clear bit N of bitstring name */ +#define bit_clear(name, bit) \ + (name)[_bit_byte(bit)] &= ~_bit_mask(bit) + + /* clear bits start ... stop in bitstring */ +#define bit_nclear(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \ + (0xff << ((_stop&0x7) + 1))); \ + } else { \ + _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0; \ + _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \ + } \ +} + + /* set bits start ... stop in bitstring */ +#define bit_nset(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] |= ((0xff << (_start&0x7)) & \ + (0xff >> (7 - (_stop&0x7)))); \ + } else { \ + _name[_startbyte] |= 0xff << ((_start)&0x7); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0xff; \ + _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \ + } \ +} + + /* find first bit clear in name */ +#define bit_ffc(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte] != 0xff) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + + /* find first bit set in name */ +#define bit_ffs(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte]) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + +#endif /* !_BITSTRING_H_ */ diff --git a/usr.bin/vi/include/cdefs.h b/usr.bin/vi/include/cdefs.h new file mode 100644 index 0000000000..c4157bcd1a --- /dev/null +++ b/usr.bin/vi/include/cdefs.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.2 (Berkeley) 10/4/93 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#if !defined(__GNUC__) && !defined(__cplusplus) +#define inline +#endif + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifdef __GNUC__ +#define const __const /* GCC: ANSI C with -traditional */ +#define inline __inline +#define signed __signed +#define volatile __volatile + +#else /* !__GNUC__ */ +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC has extensions for declaring functions as `pure' (always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and `dead' (nonreturning). These mainly affect + * optimization and warnings. Unfortunately, GCC complains if these are + * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to + * define them only if compiling without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/usr.bin/vi/include/compat.h b/usr.bin/vi/include/compat.h new file mode 100644 index 0000000000..1040699301 --- /dev/null +++ b/usr.bin/vi/include/compat.h @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)compat.h 8.10 (Berkeley) 1/11/94 + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#include +#include +#include +#include + +/* + * If your system doesn't typedef u_long, u_short, or u_char, change + * the 0 to a 1. + */ +#if 0 +typedef unsigned char u_char; /* 4.[34]BSD names. */ +typedef unsigned int u_int; +typedef unsigned long u_long; +typedef unsigned short u_short; +#endif + +/* If your system doesn't typedef size_t, change the 0 to a 1. */ +#if 0 +typedef unsigned int size_t; /* 4.[34]BSD names. */ +#endif + +/* + * If your system doesn't have the POSIX type for a signal mask, + * change the 0 to a 1. + */ +#if 0 /* POSIX 1003.1 signal mask type. */ +typedef unsigned int sigset_t; +#endif + +/* + * If your system's vsprintf returns a char *, not an int, + * change the 0 to a 1. + */ +#if 0 +#define VSPRINTF_CHARSTAR +#endif + +/* + * If you don't have POSIX 1003.1 signals, the signal code surrounding the + * temporary file creation is intended to block all of the possible signals + * long enough to create the file and unlink it. All of this stuff is + * intended to use old-style BSD calls to fake POSIX 1003.1 calls. + */ +#ifdef NO_POSIX_SIGNALS +#define sigemptyset(set) (*(set) = 0) +#define sigfillset(set) (*(set) = ~(sigset_t)0, 0) +#define sigaddset(set,signo) (*(set) |= sigmask(signo), 0) +#define sigdelset(set,signo) (*(set) &= ~sigmask(signo), 0) +#define sigismember(set,signo) ((*(set) & sigmask(signo)) != 0) + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +static int __sigtemp; /* For the use of sigprocmask */ + +/* Repeated test of oset != NULL is to avoid "*0". */ +#define sigprocmask(how, set, oset) \ + ((__sigtemp = \ + (((how) == SIG_BLOCK) ? \ + sigblock(0) | *(set) : \ + (((how) == SIG_UNBLOCK) ? \ + sigblock(0) & ~(*(set)) : \ + ((how) == SIG_SETMASK ? \ + *(set) : sigblock(0))))), \ + ((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) : \ + sigsetmask(__sigtemp)), 0) +#endif + +/* + * If realloc(3) of a NULL pointer on your system isn't the same as + * a malloc(3) call, change the 0 to a 1, and add realloc.o to the + * MISC line in your Makefile. + */ +#if 0 +#define realloc __fix_realloc +#endif + +/* + * If your system doesn't have an include file with the appropriate + * byte order set, make sure you specify the correct one. + */ +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#define BYTE_ORDER LITTLE_ENDIAN /* Set for your system. */ +#endif + +#if defined(SYSV) || defined(SYSTEM5) +#define index(a, b) strchr(a, b) +#define rindex(a, b) strrchr(a, b) +#define bzero(a, b) memset(a, 0, b) +#define bcmp(a, b, n) memcmp(a, b, n) +#define bcopy(a, b, n) memmove(b, a, n) +#endif + +#if defined(BSD) || defined(BSD4_3) +#define strchr(a, b) index(a, b) +#define strrchr(a, b) rindex(a, b) +#define memcmp(a, b, n) bcmp(a, b, n) +#define memmove(a, b, n) bcopy(b, a, n) +#endif + +/* + * 32-bit machine. The db routines are theoretically independent of + * the size of u_shorts and u_longs, but I don't know that anyone has + * ever actually tried it. At a minimum, change the following #define's + * if you are trying to compile on a different type of system. + */ +#ifndef USHRT_MAX +#define USHRT_MAX 0xFFFF +#define ULONG_MAX 0xFFFFFFFF +#endif + +#ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */ +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 RE limit. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +/* + * If you can't provide lock values in the open(2) call. Note, this + * allows races to happen. + */ +#ifndef O_EXLOCK /* 4.4BSD extension. */ +#define O_EXLOCK 0 +#endif + +#ifndef O_SHLOCK /* 4.4BSD extension. */ +#define O_SHLOCK 0 +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ +#endif + +#ifndef WCOREDUMP /* 4.4BSD extension */ +#define WCOREDUMP(a) 0 +#endif + +#ifndef STDERR_FILENO +#define STDIN_FILENO 0 /* ANSI C #defines */ +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifndef SEEK_END +#define SEEK_SET 0 /* POSIX 1003.1 seek values */ +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */ +#define _POSIX_VDISABLE 0 /* Some systems used 0. */ +#endif + +#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) +#define S_ISSOCK(m) ((m & 0170000) == 0140000) +#endif + +#ifndef TCSASOFT /* 4.4BSD extension. */ +#define TCSASOFT 0 +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 values. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +#ifndef NULL /* ANSI C #defines NULL everywhere. */ +#define NULL 0 +#endif + +#ifndef MAX /* Usually found in . */ +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN /* Usually found in . */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Default file permissions. */ +#ifndef DEFFILEMODE /* 4.4BSD extension. */ +#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) +#endif + +#ifndef S_ISDIR /* POSIX 1003.1 file type tests. */ +#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */ +#endif + +/* The type of a va_list. */ +#ifndef _BSD_VA_LIST_ /* 4.4BSD #define. */ +#define _BSD_VA_LIST_ char * +#endif + +#endif /* !_COMPAT_H_ */ diff --git a/usr.bin/vi/include/err.h b/usr.bin/vi/include/err.h new file mode 100644 index 0000000000..b6d7e5f94b --- /dev/null +++ b/usr.bin/vi/include/err.h @@ -0,0 +1,16 @@ +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +void err __P((int, const char *, ...)); +void verr __P((int, const char *, va_list)); +void errx __P((int, const char *, ...)); +void verrx __P((int, const char *, va_list)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, va_list)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, va_list)); diff --git a/usr.bin/vi/include/file.h b/usr.bin/vi/include/file.h new file mode 100644 index 0000000000..680797fa6a --- /dev/null +++ b/usr.bin/vi/include/file.h @@ -0,0 +1,15 @@ +/* + * If we're doing flock(2) emulation, we need to get the LOCK_* #defines. + * This stub includes the real one, and, if they're not in + * it, we #define them here. + */ + +#include + +#ifndef LOCK_SH +/* lock operations for flock(2) */ +#define LOCK_SH 0x01 /* shared file lock */ +#define LOCK_EX 0x02 /* exclusive file lock */ +#define LOCK_NB 0x04 /* don't block when locking */ +#define LOCK_UN 0x08 /* unlock file */ +#endif diff --git a/usr.bin/vi/include/glob.h b/usr.bin/vi/include/glob.h new file mode 100644 index 0000000000..b679f6aef6 --- /dev/null +++ b/usr.bin/vi/include/glob.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _GLOB_H_ +#define _GLOB_H_ + +#include + +#include "compat.h" + +struct stat; +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_matchc; /* Count of paths matching pattern. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ + /* Copy of errfunc parameter to glob. */ + int (*gl_errfunc) __P((const char *, int)); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir) __P((void *)); + struct dirent *(*gl_readdir) __P((void *)); + void *(*gl_opendir) __P((const char *)); + int (*gl_lstat) __P((const char *, struct stat *)); + int (*gl_stat) __P((const char *, struct stat *)); +} glob_t; + +#define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */ +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_MARK 0x0008 /* Append / to matching directories. */ +#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +#define GLOB_NOSORT 0x0020 /* Don't sort. */ + +#ifndef _POSIX_SOURCE +#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ +#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +#endif + +#define GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define GLOB_ABEND (-2) /* Unignored error. */ + +__BEGIN_DECLS +int glob __P((const char *, int, int (*)(const char *, int), glob_t *)); +void globfree __P((glob_t *)); +__END_DECLS + +#endif /* !_GLOB_H_ */ diff --git a/usr.bin/vi/include/mpool.h b/usr.bin/vi/include/mpool.h new file mode 100644 index 0000000000..910e0782aa --- /dev/null +++ b/usr.bin/vi/include/mpool.h @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mpool.h 8.1 (Berkeley) 6/2/93 + */ + +/* + * The memory pool scheme is a simple one. Each in memory page is referenced + * by a bucket which is threaded in three ways. All active pages are threaded + * on a hash chain (hashed by the page number) and an lru chain. Inactive + * pages are threaded on a free chain. Each reference to a memory pool is + * handed an MPOOL which is the opaque cookie passed to all of the memory + * routines. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the lists. */ +typedef struct BKT { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ + void *page; /* page */ + pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ + unsigned long flags; /* flags */ +} BKT; + +/* The BKTHDR structures are the heads of the lists. */ +typedef struct BKTHDR { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ +} BKTHDR; + +typedef struct MPOOL { + BKTHDR free; /* The free list. */ + BKTHDR lru; /* The LRU list. */ + BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */ + pgno_t curcache; /* Current number of cached pages. */ + pgno_t maxcache; /* Max number of cached pages. */ + pgno_t npages; /* Number of pages in the file. */ + u_long pagesize; /* File page size. */ + int fd; /* File descriptor. */ + /* Page in conversion routine. */ + void (*pgin) __P((void *, pgno_t, void *)); + /* Page out conversion routine. */ + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; /* Cookie for page in/out routines. */ +#ifdef STATISTICS + unsigned long cachehit; + unsigned long cachemiss; + unsigned long pagealloc; + unsigned long pageflush; + unsigned long pageget; + unsigned long pagenew; + unsigned long pageput; + unsigned long pageread; + unsigned long pagewrite; +#endif +} MPOOL; + +#ifdef __MPOOLINTERFACE_PRIVATE +/* Macros to insert/delete into/from hash chain. */ +#define rmhash(bp) { \ + (bp)->hprev->hnext = (bp)->hnext; \ + (bp)->hnext->hprev = (bp)->hprev; \ +} +#define inshash(bp, pg) { \ + hp = &mp->hashtable[HASHKEY(pg)]; \ + (bp)->hnext = hp->hnext; \ + (bp)->hprev = (struct BKT *)hp; \ + hp->hnext->hprev = (bp); \ + hp->hnext = (bp); \ +} + +/* Macros to insert/delete into/from lru and free chains. */ +#define rmchain(bp) { \ + (bp)->cprev->cnext = (bp)->cnext; \ + (bp)->cnext->cprev = (bp)->cprev; \ +} +#define inschain(bp, dp) { \ + (bp)->cnext = (dp)->cnext; \ + (bp)->cprev = (struct BKT *)(dp); \ + (dp)->cnext->cprev = (bp); \ + (dp)->cnext = (bp); \ +} +#endif + +__BEGIN_DECLS +MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), + void (*)(void *, pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, pgno_t *)); +void *mpool_get __P((MPOOL *, pgno_t, u_int)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS diff --git a/usr.bin/vi/include/ndbm.h b/usr.bin/vi/include/ndbm.h new file mode 100644 index 0000000000..a545bca132 --- /dev/null +++ b/usr.bin/vi/include/ndbm.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NDBM_H_ +#define _NDBM_H_ + +#include + +/* Map dbm interface onto db(3). */ +#define DBM_RDONLY O_RDONLY + +/* Flags to dbm_store(). */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * The db(3) support for ndbm(3) always appends this suffix to the + * file name to avoid overwriting the user's original database. + */ +#define DBM_SUFFIX ".db" + +typedef struct { + char *dptr; + int dsize; +} datum; + +typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE + +__BEGIN_DECLS +void dbm_close __P((DBM *)); +int dbm_delete __P((DBM *, datum)); +datum dbm_fetch __P((DBM *, datum)); +datum dbm_firstkey __P((DBM *)); +long dbm_forder __P((DBM *, datum)); +datum dbm_nextkey __P((DBM *)); +DBM *dbm_open __P((const char *, int, int)); +int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); +__END_DECLS + +#endif /* !_NDBM_H_ */ diff --git a/usr.bin/vi/include/queue.h b/usr.bin/vi/include/queue.h new file mode 100644 index 0000000000..40d32ccb6e --- /dev/null +++ b/usr.bin/vi/include/queue.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +/* + * This file defines three types of data structures: lists, tail queues, + * and circular queues. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element or at the head of the list. A list may only be + * traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) { \ + (head)->lh_first = NULL; \ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} + +#define LIST_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} + +#define LIST_REMOVE(elm, field) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} + +#define TAILQ_REMOVE(head, elm, field) { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} + +#define CIRCLEQ_REMOVE(head, elm, field) { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} +#endif /* !_QUEUE_H_ */ diff --git a/usr.bin/vi/interrupt.h b/usr.bin/vi/interrupt.h new file mode 100644 index 0000000000..baa75fef2c --- /dev/null +++ b/usr.bin/vi/interrupt.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)interrupt.h 8.1 (Berkeley) 1/9/94 + */ + +/* + * Macros to declare the variables and then turn on and off interrupts. + */ +#define DECLARE_INTERRUPTS \ + struct sigaction __act, __oact; \ + struct termios __nterm, __term; \ + u_int __istate; \ + int __isig, __termreset + +/* + * Search, global, and substitute interruptions. + * + * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the + * search, so we install a handler. VQUIT is ignored by main() because + * nvi never wants to catch it. A handler for VSUSP should have been + * installed by the screen code. + */ +#define SET_UP_INTERRUPTS(handler) { \ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { \ + __act.sa_handler = handler; \ + sigemptyset(&__act.sa_mask); \ + __act.sa_flags = 0; \ + if (__isig = !sigaction(SIGINT, &__act, &__oact)) { \ + __termreset = 0; \ + __istate = F_ISSET(sp, S_INTERRUPTIBLE); \ + F_CLR(sp, S_INTERRUPTED); \ + F_SET(sp, S_INTERRUPTIBLE); \ + if (tcgetattr(STDIN_FILENO, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcgetattr"); \ + goto interrupt_err; \ + } \ + __nterm = __term; \ + __nterm.c_lflag |= ISIG; \ + if (tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__nterm)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + goto interrupt_err; \ + } \ + __termreset = 1; \ + } \ + } \ +} + +#define TEAR_DOWN_INTERRUPTS { \ + if (F_ISSET(sp->gp, G_ISFROMTTY) && __isig) { \ + if (sigaction(SIGINT, &__oact, NULL)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "signal"); \ + } \ + if (__termreset && tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + } \ + F_CLR(sp, S_INTERRUPTED); \ + if (!__istate) \ + F_CLR(sp, S_INTERRUPTIBLE); \ + } \ +} diff --git a/usr.bin/vi/line.c b/usr.bin/vi/line.c new file mode 100644 index 0000000000..a7473491cf --- /dev/null +++ b/usr.bin/vi/line.c @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)line.c 8.20 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include + +#include +#include + +#include "vi.h" +#include "excmd.h" + +static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int)); + +/* + * file_gline -- + * Look in the text buffers for a line; if it's not there + * call file_rline to retrieve it from the database. + */ +char * +file_gline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + TEXT *tp; + recno_t l1, l2; + + /* + * The underlying recno stuff handles zero by returning NULL, but + * have to have an oob condition for the look-aside into the input + * buffer anyway. + */ + if (lno == 0) + return (NULL); + + /* + * Look-aside into the TEXT buffers and see if the line we want + * is there. + */ + if (F_ISSET(sp, S_INPUT)) { + l1 = ((TEXT *)sp->tiq.cqh_first)->lno; + l2 = ((TEXT *)sp->tiq.cqh_last)->lno; + if (l1 <= lno && l2 >= lno) { + for (tp = sp->tiq.cqh_first; + tp->lno != lno; tp = tp->q.cqe_next); + if (lenp) + *lenp = tp->len; + return (tp->lb); + } + /* + * Adjust the line number for the number of lines used + * by the text input buffers. + */ + if (lno > l2) + lno -= l2 - l1; + } + return (file_rline(sp, ep, lno, lenp)); +} + +/* + * file_rline -- + * Look in the cache for a line; if it's not there retrieve + * it from the file. + */ +char * +file_rline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + DBT data, key; + + /* Check the cache. */ + if (lno == ep->c_lno) { + if (lenp) + *lenp = ep->c_len; + return (ep->c_lp); + } + ep->c_lno = OOBLNO; + + /* Get the line from the underlying database. */ + key.data = &lno; + key.size = sizeof(lno); + switch (ep->db->get(ep->db, &key, &data, 0)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + /* FALLTHROUGH */ + case 1: + return (NULL); + /* NOTREACHED */ + } + if (lenp) + *lenp = data.size; + + /* Fill the cache. */ + ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + return (data.data); +} + +/* + * file_dline -- + * Delete a line from the file. + */ +int +file_dline(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + DBT key; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete line %lu\n", lno); +#endif + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_DELETE, lno); + global_insdel(sp, ep, LINE_DELETE, lno); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_DELETE); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + if (ep->db->del(ep->db, &key, 0) == 1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to delete line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno <= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + --ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_DELETE, 1)); +} + +/* + * file_aline -- + * Append a line into the file. + */ +int +file_aline(sp, ep, update, lno, p, len) + SCR *sp; + EXF *ep; + int update; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* + * Very nasty special case. The historic vi code displays a single + * space (or a '$' if the list option is set) for the first line in + * an "empty" file. If we "insert" a line, that line gets scrolled + * down, not repainted, so it's incorrect when we refresh the the + * screen. This is really hard to find and fix in the vi code -- the + * text input functions detect it explicitly and don't insert a new + * line. The hack here is to repaint the screen if we're appending + * to an empty file. + */ + if (lno == 0) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to append to line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno < ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno + 1, LOG_LINE_APPEND); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno + 1); + global_insdel(sp, ep, LINE_INSERT, lno + 1); + + /* + * Update screen. + * + * XXX + * Nasty hack. If multiple lines are input by the user, they aren't + * committed until an is entered. The problem is the screen was + * updated/scrolled as each line was entered. So, when this routine + * is called to copy the new lines from the cut buffer into the file, + * it has to know not to update the screen again. + */ + return (scr_update(sp, ep, lno, LINE_APPEND, update)); +} + +/* + * file_iline -- + * Insert a line into the file. + */ +int +file_iline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, + "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + + /* Very nasty special case. See comment in file_aline(). */ + if (lno == 1) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to insert at line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno >= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_INSERT); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno); + global_insdel(sp, ep, LINE_INSERT, lno); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_INSERT, 1)); +} + +/* + * file_sline -- + * Store a line in the file. + */ +int +file_sline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + +#if defined(DEBUG) && 0 + TRACE(sp, + "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* Log before change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_B); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, 0) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to store line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, before logging or screen update. */ + if (lno == ep->c_lno) + ep->c_lno = OOBLNO; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log after change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_F); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_RESET, 1)); +} + +/* + * file_lline -- + * Return the number of lines in the file. + */ +int +file_lline(sp, ep, lnop) + SCR *sp; + EXF *ep; + recno_t *lnop; +{ + DBT data, key; + recno_t lno; + + /* Check the cache. */ + if (ep->c_nlines != OOBLNO) { + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ? + ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines); + return (0); + } + + key.data = &lno; + key.size = sizeof(lno); + + switch (ep->db->seq(ep->db, &key, &data, R_LAST)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get last line: %s.", + tail(__FILE__), __LINE__, strerror(errno)); + *lnop = 0; + return (1); + case 1: + lno = 0; + break; + default: + memmove(&lno, key.data, sizeof(lno)); + break; + } + + /* Fill the cache. */ + ep->c_nlines = ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > lno ? + ((TEXT *)sp->tiq.cqh_last)->lno : lno); + return (0); +} + +/* + * scr_update -- + * Update all of the screens that are backed by the file that + * just changed. + */ +static inline int +scr_update(sp, ep, lno, op, current) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; + int current; +{ + SCR *tsp; + + if (ep->refcnt != 1) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (sp != tsp && tsp->ep == ep) + (void)sp->s_change(tsp, ep, lno, op); + return (current && sp->s_change(sp, ep, lno, op)); +} diff --git a/usr.bin/vi/log.c b/usr.bin/vi/log.c new file mode 100644 index 0000000000..fdf959f4d8 --- /dev/null +++ b/usr.bin/vi/log.c @@ -0,0 +1,663 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)log.c 8.9 (Berkeley) 12/28/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include + +#include "vi.h" + +/* + * The log consists of records, each containing a type byte and a variable + * length byte string, as follows: + * + * LOG_CURSOR_INIT MARK + * LOG_CURSOR_END MARK + * LOG_LINE_APPEND recno_t char * + * LOG_LINE_DELETE recno_t char * + * LOG_LINE_INSERT recno_t char * + * LOG_LINE_RESET_F recno_t char * + * LOG_LINE_RESET_B recno_t char * + * LOG_MARK MARK + * + * We do before image physical logging. This means that the editor layer + * MAY NOT modify records in place, even if simply deleting or overwriting + * characters. Since the smallest unit of logging is a line, we're using + * up lots of space. This may eventually have to be reduced, probably by + * doing logical logging, which is a much cooler database phrase. + * + * The implementation of the historic vi 'u' command, using roll-forward and + * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, + * followed by a number of other records, followed by a LOG_CURSOR_END record. + * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B + * record, and is the line before the change. The second is LOG_LINE_RESET_F, + * and is the line after the change. Roll-back is done by backing up to the + * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a + * similar fashion. + * + * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END + * record for a line different from the current one. It should be noted that + * this means that a subsequent 'u' command will make a change based on the + * new position of the log's cursor. This is okay, and, in fact, historic vi + * behaved that way. + */ + +static int log_cursor1 __P((SCR *, EXF *, int)); +#if defined(DEBUG) && 0 +static void log_trace __P((SCR *, char *, recno_t, u_char *)); +#endif + +/* Try and restart the log on failure, i.e. if we run out of memory. */ +#define LOG_ERR { \ + msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.", \ + tail(__FILE__), __LINE__, strerror(errno)); \ + (void)ep->log->close(ep->log); \ + if (!log_init(sp, ep)) \ + msgq(sp, M_ERR, "Log restarted."); \ + return (1); \ +} + +/* + * log_init -- + * Initialize the logging subsystem. + */ +int +log_init(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Initialize the buffer. The logging subsystem has its own + * buffers because the global ones are almost by definition + * going to be in use when the log runs. + */ + ep->l_lp = NULL; + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + + ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR, + S_IRUSR | S_IWUSR, DB_RECNO, NULL); + if (ep->log == NULL) { + msgq(sp, M_ERR, "log db: %s", strerror(errno)); + F_SET(ep, F_NOLOG); + return (1); + } + + return (0); +} + +/* + * log_end -- + * Close the logging subsystem. + */ +int +log_end(sp, ep) + SCR *sp; + EXF *ep; +{ + if (ep->log != NULL) { + (void)(ep->log->close)(ep->log); + ep->log = NULL; + } + if (ep->l_lp != NULL) { + free(ep->l_lp); + ep->l_lp = NULL; + } + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + return (0); +} + +/* + * log_cursor -- + * Log the current cursor position, starting an event. + */ +int +log_cursor(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * If any changes were made since the last cursor init, + * put out the ending cursor record. + */ + if (ep->l_cursor.lno == OOBLNO) { + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (log_cursor1(sp, ep, LOG_CURSOR_END)); + } + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (0); +} + +/* + * log_cursor1 -- + * Actually push a cursor record out. + */ +static int +log_cursor1(sp, ep, type) + SCR *sp; + EXF *ep; + int type; +{ + DBT data, key; + + BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = type; + memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, + type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", + sp->lno, sp->cno); +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_line -- + * Log a line change. + */ +int +log_line(sp, ep, lno, action) + SCR *sp; + EXF *ep; + recno_t lno; + u_int action; +{ + DBT data, key; + size_t len; + char *lp; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* + * XXX + * + * Kluge for vi. Clear the EXF undo flag so that the + * next 'u' command does a roll-back, regardless. + */ + F_CLR(ep, F_UNDO); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + /* + * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a + * special case, avoid the caches. Also, if it fails and it's + * line 1, it just means that the user started with an empty file, + * so fake an empty length line. + */ + if (action == LOG_LINE_RESET_B) { + if ((lp = file_rline(sp, ep, lno, &len)) == NULL) { + if (lno != 1) { + GETLINE_ERR(sp, lno); + return (1); + } + len = 0; + lp = ""; + } + } else + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + BINC_RET(sp, + ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t)); + ep->l_lp[0] = action; + memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); + memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = len + sizeof(u_char) + sizeof(recno_t); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + switch (action) { + case LOG_LINE_APPEND: + TRACE(sp, "%u: log_line: append: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_DELETE: + TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_INSERT: + TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_F: + TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_B: + TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + } +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_mark -- + * Log a mark position. For the log to work, we assume that there + * aren't any operations that just put out a log record -- this + * would mean that undo operations would only reset marks, and not + * cause any other change. + */ +int +log_mark(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + DBT data, key; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + BINC_RET(sp, ep->l_lp, + ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = LOG_MARK; + memmove(ep->l_lp + sizeof(u_char), mp, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + return (0); +} + +/* + * Log_backward -- + * Roll the log backward one operation. + */ +int +log_backward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) { + msgq(sp, M_BERR, "No changes to undo."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_backward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + if (didop) { + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_setline -- + * Reset the line to its original appearance. + * + * XXX + * There's a bug in this code due to our not logging cursor movements + * unless a change was made. If you do a change, move off the line, + * then move back on and do a 'U', the line will be restored to the way + * it was before the original change. + */ +int +log_setline(sp, ep) + SCR *sp; + EXF *ep; +{ + DBT key, data; + MARK m; + recno_t lno; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) + return (1); + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + + for (;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_setline", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno || ep->l_cur == 1) { + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno) { + ++ep->l_cur; + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + case LOG_LINE_DELETE: + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (lno == sp->lno && + file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_forward -- + * Roll the log forward one operation. + */ +int +log_forward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, roll-forward not possible."); + return (1); + } + + if (ep->l_cur == ep->l_high) { + msgq(sp, M_BERR, "No changes to re-do."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + ++ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_forward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_END: + if (didop) { + ++ep->l_cur; + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_INIT: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_RESET_B: + break; + case LOG_LINE_RESET_F: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +#if defined(DEBUG) && 0 +static void +log_trace(sp, msg, rno, p) + SCR *sp; + char *msg; + recno_t rno; + u_char *p; +{ + MARK m; + recno_t lno; + + switch (*p) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_LINE_APPEND: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); + break; + case LOG_LINE_INSERT: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); + break; + case LOG_LINE_DELETE: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_F: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); + break; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: MARK: %u/%u\n", rno, msg, m.lno, m.cno); + break; + default: + abort(); + } +} +#endif diff --git a/usr.bin/vi/log.h b/usr.bin/vi/log.h new file mode 100644 index 0000000000..840cf8532c --- /dev/null +++ b/usr.bin/vi/log.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)log.h 8.3 (Berkeley) 12/28/93 + */ + +#define LOG_NOTYPE 0 +#define LOG_CURSOR_INIT 1 +#define LOG_CURSOR_END 2 +#define LOG_LINE_APPEND 3 +#define LOG_LINE_DELETE 4 +#define LOG_LINE_INSERT 5 +#define LOG_LINE_RESET_F 6 +#define LOG_LINE_RESET_B 7 +#define LOG_MARK 8 + +int log_backward __P((SCR *, EXF *, MARK *)); +int log_cursor __P((SCR *, EXF *)); +int log_end __P((SCR *, EXF *)); +int log_forward __P((SCR *, EXF *, MARK *)); +int log_init __P((SCR *, EXF *)); +int log_line __P((SCR *, EXF *, recno_t, u_int)); +int log_mark __P((SCR *, EXF *, MARK *)); +int log_setline __P((SCR *, EXF *)); diff --git a/usr.bin/vi/main.c b/usr.bin/vi/main.c new file mode 100644 index 0000000000..26d9983ca4 --- /dev/null +++ b/usr.bin/vi/main.c @@ -0,0 +1,745 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.63 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" +#include "tag.h" + +static int exrc_isok __P((SCR *, char *, int)); +static void gs_end __P((GS *)); +static GS *gs_init __P((void)); +static void h_hup __P((int)); +static void h_term __P((int)); +static void h_winch __P((int)); +static void obsolete __P((char *[])); +static void usage __P((int)); + +GS *__global_list; /* GLOBAL: List of screens. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + static int reenter; /* STATIC: Re-entrancy check. */ + struct sigaction act; + GS *gp; + FREF *frp; + SCR *sp; + u_int flags, saved_vi_mode; + int ch, eval, flagchk, readonly, silent, snapshot; + char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg; + char path[MAXPATHLEN]; + + /* Stop if indirecting through a NULL pointer. */ + if (reenter++) + abort(); + + /* Set screen type and mode based on the program name. */ + readonly = 0; + if ((myname = strrchr(*argv, '/')) == NULL) + myname = *argv; + else + ++myname; + if (!strcmp(myname, "ex") || !strcmp(myname, "nex")) + LF_INIT(S_EX); + else { + /* View is readonly. */ + if (!strcmp(myname, "view")) + readonly = 1; + LF_INIT(S_VI_CURSES); + } + saved_vi_mode = S_VI_CURSES; + + /* Convert old-style arguments into new-style ones. */ + obsolete(argv); + + /* Parse the arguments. */ + flagchk = '\0'; + excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL; + silent = 0; + snapshot = 1; + while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF) + switch (ch) { + case 'c': /* Run the command. */ + excmdarg = optarg; + break; + case 'e': /* Ex mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_EX); + break; + case 'F': /* No snapshot. */ + snapshot = 0; + break; + case 'l': + if (flagchk != '\0' && flagchk != 'l') + errx(1, + "only one of -%c and -l may be specified.", + flagchk); + flagchk = 'l'; + break; + case 'R': /* Readonly. */ + readonly = 1; + break; + case 'r': /* Recover. */ + if (flagchk == 'r') + errx(1, + "only one recovery file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -r may be specified.", + flagchk); + flagchk = 'r'; + rec_f = optarg; + break; + case 's': + if (!LF_ISSET(S_EX)) + errx(1, "-s only applicable to ex."); + silent = 1; + break; + case 'T': /* Trace. */ + trace_f = optarg; + break; + case 't': /* Tag. */ + if (flagchk == 't') + errx(1, + "only one tag file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -t may be specified.", + flagchk); + flagchk = 't'; + tag_f = optarg; + break; + case 'v': /* Vi mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_VI_CURSES); + break; + case 'w': + wsizearg = optarg; + break; + case 'x': + if (!strcmp(optarg, "aw")) { + LF_CLR(S_SCREENS); + LF_SET(S_VI_XAW); + saved_vi_mode = S_VI_XAW; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(LF_ISSET(S_EX)); + } + argc -= optind; + argv += optind; + + /* Build and initialize the GS structure. */ + __global_list = gp = gs_init(); + + if (snapshot) + F_SET(gp, G_SNAPSHOT); + + /* Build and initialize the first/current screen. */ + if (screen_init(NULL, &sp, flags)) { + if (sp == NULL) + goto err2; + goto err1; + } + sp->saved_vi_mode = saved_vi_mode; + CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); + + if (trace_f != NULL) { +#ifdef DEBUG + if ((gp->tracefp = fopen(optarg, "w")) == NULL) + err(1, "%s", optarg); + (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg); +#else + msgq(sp, M_ERR, "-T support not compiled into this version."); +#endif + } + + if (set_window_size(sp, 0, 0)) /* Set the window size. */ + goto err1; + + if (opts_init(sp)) /* Options initialization. */ + goto err1; + if (readonly) + O_SET(sp, O_READONLY); + if (silent) { + O_CLR(sp, O_AUTOPRINT); + O_CLR(sp, O_PROMPT); + O_CLR(sp, O_VERBOSE); + O_CLR(sp, O_WARN); + F_SET(sp, S_EXSILENT); + } + if (wsizearg != NULL) { + ARGS *av[2], a, b; + if (strtol(optarg, &p, 10) < 0 || *p) + errx(1, "illegal window size -- %s", optarg); + (void)snprintf(path, sizeof(path), "window=%s", optarg); + a.bp = path; + a.len = strlen(path); + b.bp = NULL; + b.len = 0; + av[0] = &a; + av[1] = &b; + if (opts_set(sp, av)) + msgq(sp, M_ERR, + "Unable to set command line window option"); + } + + /* Keymaps, special keys, must follow option initializations. */ + if (term_init(sp)) + goto err1; + +#ifdef DIGRAPHS + if (digraph_init(sp)) /* Digraph initialization. */ + goto err1; +#endif + + /* + * Source the system, environment, ~user and local .exrc values. + * Vi historically didn't check ~user/.exrc if the environment + * variable EXINIT was set. This is all done before the file is + * read in because things in the .exrc information can set, for + * example, the recovery directory. + * + * !!! + * While nvi can handle any of the options settings of historic vi, + * the converse is not true. Since users are going to have to have + * files and environmental variables that work with both, we use nvi + * versions if they exist, otherwise the historic ones. + */ + if (!silent) { + if (exrc_isok(sp, _PATH_SYSEXRC, 1)) + (void)ex_cfile(sp, NULL, _PATH_SYSEXRC); + + /* Source the {N,}EXINIT environment variable. */ + if ((p = getenv("NEXINIT")) != NULL || + (p = getenv("EXINIT")) != NULL) + if ((p = strdup(p)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err1; + } else { + (void)ex_icmd(sp, NULL, p, strlen(p)); + free(p); + } + else if ((p = getenv("HOME")) != NULL && *p) { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_NEXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + else { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_EXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + } + } + /* + * !!! + * According to O'Reilly ("Learning the VI Editor", Fifth Ed., + * May 1992, page 106), System V release 3.2 and later, has an + * option "[no]exrc", causing vi to not "read .exrc files in + * the current directory unless you first set the exrc option + * in your home directory's .exrc file". Yeah, right. Did + * someone actually believe that users would change their home + * .exrc file based on whether or not they wanted to source the + * current local .exrc? Or that users would want ALL the local + * .exrc files on some systems, and none of them on others? + * I think not. + * + * Apply the same tests to local .exrc files that are applied + * to any other .exrc file. + */ + if (exrc_isok(sp, _PATH_EXRC, 0)) + (void)ex_cfile(sp, NULL, _PATH_EXRC); + } + + /* List recovery files if -l specified. */ + if (flagchk == 'l') + exit(rcv_list(sp)); + + /* Use a tag file or recovery file if specified. */ + if (tag_f != NULL && ex_tagfirst(sp, tag_f)) + goto err1; + else if (rec_f != NULL && rcv_read(sp, rec_f)) + goto err1; + + /* Append any remaining arguments as file names. */ + if (*argv != NULL) + for (; *argv != NULL; ++argv) + if (file_add(sp, NULL, *argv, 0) == NULL) + goto err1; + + /* + * If no recovery or tag file, get an EXF structure. + * If no argv file, use a temporary file. + */ + if (tag_f == NULL && rec_f == NULL) { + if ((frp = file_first(sp)) == NULL && + (frp = file_add(sp, NULL, NULL, 1)) == NULL) + goto err1; + if (file_init(sp, frp, NULL, 0)) + goto err1; + } + + /* Set up the argument pointer. */ + sp->a_frp = sp->frp; + + /* + * Initialize the signals. Use sigaction(2), not signal(3), because + * we don't want to always restart system calls on 4BSD systems. It + * would be nice in some cases to restart system calls, but SA_RESTART + * is a 4BSD extension so we can't use it. + * + * SIGWINCH, SIGHUP, SIGTERM: + * Catch and set a global bit. + */ + act.sa_handler = h_hup; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGHUP, &act, NULL); + act.sa_handler = h_term; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGTERM, &act, NULL); + act.sa_handler = h_winch; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGWINCH, &act, NULL); + + /* + * SIGQUIT: + * Always ignore. + */ + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGQUIT, &act, NULL); + + /* + * If there's an initial command, push it on the command stack. + * Historically, it was always an ex command, not vi in vi mode + * or ex in ex mode. So, make it look like an ex command to vi. + */ + if (excmdarg != NULL) + if (IN_EX_MODE(sp)) { + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err1; + } else if (IN_VI_MODE(sp)) { + if (term_push(sp, "\n", 1, 0, 0)) + goto err1; + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err1; + if (term_push(sp, ":", 1, 0, 0)) + goto err1; + } + + /* Vi reads from the terminal. */ + if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) { + msgq(sp, M_ERR, "Vi's standard input must be a terminal."); + goto err1; + } + + for (;;) { + if (sp->s_edit(sp, sp->ep)) + goto err2; + + /* + * Edit the next screen on the display queue, or, move + * a screen from the hidden queue to the display queue. + */ + if ((sp = __global_list->dq.cqh_first) == + (void *)&__global_list->dq) + if ((sp = __global_list->hq.cqh_first) != + (void *)&__global_list->hq) { + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + } else + break; + + /* + * The screen type may have changed -- reinitialize the + * functions in case it has. + */ + switch (F_ISSET(sp, S_SCREENS)) { + case S_EX: + if (sex_screen_init(sp)) + goto err2; + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + goto err2; + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + goto err2; + break; + default: + abort(); + } + } + + /* + * Two error paths. The first means that something failed before + * we called a screen routine. Swap the message pointers between + * the SCR and the GS, so messages get displayed. The second is + * something failed in a screen. NOTE: sp may be GONE when the + * screen returns, so only the gp can be trusted. + */ + eval = 0; + if (0) { +err1: gp->msgq.lh_first = sp->msgq.lh_first; +err2: eval = 1; + } + + gs_end(gp); + + /* + * XXX + * Make absolutely sure that the modes are restored correctly. + * + * This should no longer be needed, and it's here to handle what I + * believe are SunOS/Solaris curses problems. The problem is that + * for some unknown reason, when endwin() is called in the svi + * routines, it isn't resetting the terminal correctly. I have not + * been able to figure it out, so this resets the terminal to the + * right modes regardless. The problem is that, in most tty driver + * implementations, you can only reset the terminal modes once + * (changing from !ICANON to ICANON) without losing the re-parsing + * effect on the pending input. This means that this "fix" will make + * other systems mess up characters typed after the quit command to + * vi but before vi actually exits. + */ + if (F_ISSET(gp, G_ISFROMTTY) && + tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios)) + err(1, "tcsetattr"); + exit(eval); +} + +/* + * gs_init -- + * Build and initialize the GS structure. + */ +static GS * +gs_init() +{ + GS *gp; + int fd; + + CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS)); + if (gp == NULL) + err(1, NULL); + + CIRCLEQ_INIT(&gp->dq); + CIRCLEQ_INIT(&gp->hq); + LIST_INIT(&gp->msgq); + + /* Structures shared by screens so stored in the GS structure. */ + CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF)); + if (gp->tty == NULL) + err(1, NULL); + + LIST_INIT(&gp->cutq); + LIST_INIT(&gp->seqq); + + /* Set a flag if we're reading from the tty. */ + if (isatty(STDIN_FILENO)) + F_SET(gp, G_ISFROMTTY); + + /* + * XXX + * Set a flag and don't do terminal sets/resets if the input isn't + * from a tty. Under all circumstances put reasonable things into + * the original_termios field, as some routines (seq.c:seq_save() + * and term.c:term_init()) want values for special characters. + */ + if (F_ISSET(gp, G_ISFROMTTY)) { + if (tcgetattr(STDIN_FILENO, &gp->original_termios)) + err(1, "tcgetattr"); + } else { + if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1) + err(1, "%s", _PATH_TTY); + if (tcgetattr(fd, &gp->original_termios)) + err(1, "tcgetattr"); + (void)close(fd); + } + + return (gp); +} + + +/* + * gs_end -- + * End the GS structure. + */ +static void +gs_end(gp) + GS *gp; +{ + MSG *mp; + SCR *sp; + char *tty; + + /* Reset anything that needs resetting. */ + if (gp->flags & G_SETMODE) /* O_MESG */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) + warn("ttyname"); + else if (chmod(tty, gp->origmode) < 0) + warn("%s", tty); + + /* Ring the bell if scheduled. */ + if (F_ISSET(gp, G_BELLSCHED)) + (void)fprintf(stderr, "\07"); /* \a */ + + /* If there are any remaining screens, flush their messages. */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + /* Flush messages on the global queue. */ + for (mp = gp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + + if (gp->special_key != NULL) + FREE(gp->special_key, MAX_FAST_KEY); + + /* + * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN + * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED. + */ +} + +/* + * h_hup -- + * Handle SIGHUP. + */ +static void +h_hup(signo) + int signo; +{ + F_SET(__global_list, G_SIGHUP); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_hup(); +} + +/* + * h_term -- + * Handle SIGTERM. + */ +static void +h_term(signo) + int signo; +{ + F_SET(__global_list, G_SIGTERM); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_term(); +} + +/* + * h_winch -- + * Handle SIGWINCH. + */ +static void +h_winch(signo) + int signo; +{ + F_SET(__global_list, G_SIGWINCH); +} + +/* + * exrc_isok -- + * Check a .exrc for source-ability. + */ +static int +exrc_isok(sp, path, rootok) + SCR *sp; + char *path; + int rootok; +{ + struct stat sb; + uid_t uid; + char *emsg, buf[MAXPATHLEN]; + + /* Check for the file's existence. */ + if (stat(path, &sb)) + return (0); + + /* + * !!! + * Historically, vi did not read the .exrc files if they were owned + * by someone other than the user, unless the undocumented option + * sourceany was set. We don't support the sourceany option. We + * check that the user (or root, for system files) owns the file and + * require that it not be writeable by anyone other than the owner. + */ + + /* Owned by the user or root. */ + uid = getuid(); + if (rootok) { + if (sb.st_uid != uid && sb.st_uid != 0) { + emsg = "not owned by you or root"; + goto err; + } + } else + if (sb.st_uid != uid) { + emsg = "not owned by you"; + goto err; + } + + /* Not writeable by anyone but the owner. */ + if (sb.st_mode & (S_IWGRP | S_IWOTH)) { + emsg = "writeable by a user other than the owner"; +err: if (strchr(path, '/') == NULL && + getcwd(buf, sizeof(buf)) != NULL) + msgq(sp, M_ERR, + "%s/%s: not sourced: %s.", buf, path, emsg); + else + msgq(sp, M_ERR, + "%s: not sourced: %s.", path, emsg); + return (0); + } + return (1); +} + +static void +obsolete(argv) + char *argv[]; +{ + size_t len; + char *p, *myname; + + /* + * Translate old style arguments into something getopt will like. + * Make sure it's not text space memory, because ex changes the + * strings. + * Change "+" into "-c$". + * Change "+" into "-c". + * Change "-" into "-s" + * Change "-r" into "-l" + */ + for (myname = argv[0]; *++argv;) + if (argv[0][0] == '+') { + if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 4); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-c$"); + } else { + p = argv[0]; + len = strlen(argv[0]); + MALLOC_NOMSG(NULL, argv[0], char *, len + 2); + if (argv[0] == NULL) + err(1, NULL); + argv[0][0] = '-'; + argv[0][1] = 'c'; + (void)strcpy(argv[0] + 2, p + 1); + } + } else if (argv[0][0] == '-') { + if (argv[0][1] == 'r') { + if (argv[0][2] == '\0' && argv[1] == NULL) + argv[0][1] = 'l'; + } else if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 3); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-s"); + } + } +} + +static void +usage(is_ex) + int is_ex; +{ +#define EX_USAGE \ + "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" +#define VI_USAGE \ + "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" + + (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE); + exit(1); +} diff --git a/usr.bin/vi/mark.c b/usr.bin/vi/mark.c new file mode 100644 index 0000000000..c510a22002 --- /dev/null +++ b/usr.bin/vi/mark.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mark.c 8.12 (Berkeley) 12/27/93"; +#endif /* not lint */ + +#include + +#include +#include +#include + +#include "vi.h" + +static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T)); + +/* + * Marks are maintained in a key sorted doubly linked list. We can't + * use arrays because we have no idea how big an index key could be. + * The underlying assumption is that users don't have more than, say, + * 10 marks at any one time, so this will be is fast enough. + * + * Marks are fixed, and modifications to the line don't update the mark's + * position in the line. This can be hard. If you add text to the line, + * place a mark in that text, undo the addition and use ` to move to the + * mark, the location will have disappeared. It's tempting to try to adjust + * the mark with the changes in the line, but this is hard to do, especially + * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi + * would move to the first non-blank on the line when the mark location was + * past the end of the line. This can be complicated by deleting to a mark + * that has disappeared using the ` command. Historic vi vi treated this as + * a line-mode motion and deleted the line. This implementation complains to + * the user. + * + * In historic vi, marks returned if the operation was undone, unless the + * mark had been subsequently reset. Tricky. This is hard to start with, + * but in the presence of repeated undo it gets nasty. When a line is + * deleted, we delete (and log) any marks on that line. An undo will create + * the mark. Any mark creations are noted as to whether the user created + * it or if it was created by an undo. The former cannot be reset by another + * undo, but the latter may. + * + * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of + * the absolute mark locations sets both, so that "m'" and "m`" work like + * they, ah, for lack of a better word, "should". + */ + +/* + * mark_init -- + * Set up the marks. + */ +int +mark_init(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + /* + * Make sure the marks have been set up. If they + * haven't, do so, and create the absolute mark. + */ + MALLOC_RET(sp, mp, MARK *, sizeof(MARK)); + mp->lno = 1; + mp->cno = 0; + mp->name = ABSMARK1; + mp->flags = 0; + LIST_INSERT_HEAD(&ep->marks, mp, q); + return (0); +} + +/* + * mark_end -- + * Free up the marks. + */ +int +mark_end(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + while ((mp = ep->marks.lh_first) != NULL) { + LIST_REMOVE(mp, q); + FREE(mp, sizeof(MARK)); + } + return (0); +} + +/* + * mark_get -- + * Get the location referenced by a mark. + */ +MARK * +mark_get(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp; + size_t len; + char *p; + + if (key == ABSMARK2) + key = ABSMARK1; + + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key)); + return (NULL); + } + if (F_ISSET(mp, MARK_DELETED)) { + msgq(sp, M_BERR, + "Mark %s: the line was deleted.", charname(sp, key)); + return (NULL); + } + if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL || + mp->cno > len || mp->cno == len && len != 0) { + msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.", + charname(sp, key)); + return (NULL); + } + return (mp); +} + +/* + * mark_set -- + * Set the location referenced by a mark. + */ +int +mark_set(sp, ep, key, value, userset) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; + MARK *value; + int userset; +{ + MARK *mp, *mt; + + if (key == ABSMARK2) + key = ABSMARK1; + + /* + * The rules are simple. If the user is setting a mark (if it's a + * new mark this is always true), it always happens. If not, it's + * an undo, and we set it if it's not already set or if it was set + * by a previous undo. + */ + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + MALLOC_RET(sp, mt, MARK *, sizeof(MARK)); + if (mp == NULL) { + LIST_INSERT_HEAD(&ep->marks, mt, q); + } else + LIST_INSERT_AFTER(mp, mt, q); + mp = mt; + } else if (!userset && + !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET)) + return (0); + + mp->lno = value->lno; + mp->cno = value->cno; + mp->name = key; + mp->flags = userset ? MARK_USERSET : 0; + return (0); +} + +/* + * mark_find -- + * Find the requested mark, or, the slot immediately before + * where it would go. + */ +static MARK * +mark_find(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp, *lastmp; + + /* + * Return the requested mark or the slot immediately before + * where it should go. + */ + for (lastmp = NULL, mp = ep->marks.lh_first; + mp != NULL; lastmp = mp, mp = mp->q.le_next) + if (mp->name >= key) + return (mp->name == key ? mp : lastmp); + return (lastmp); +} + +/* + * mark_insdel -- + * Update the marks based on an insertion or deletion. + */ +void +mark_insdel(sp, ep, op, lno) + SCR *sp; + EXF *ep; + enum operation op; + recno_t lno; +{ + MARK *mp; + + switch (op) { + case LINE_APPEND: + return; + case LINE_DELETE: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + if (mp->lno == lno) { + F_SET(mp, MARK_DELETED); + (void)log_mark(sp, ep, mp); + } else + --mp->lno; + return; + case LINE_INSERT: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + ++mp->lno; + return; + case LINE_RESET: + return; + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/mark.h b/usr.bin/vi/mark.h new file mode 100644 index 0000000000..9c28151314 --- /dev/null +++ b/usr.bin/vi/mark.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mark.h 8.5 (Berkeley) 12/27/93 + */ + +/* + * The MARK structure defines a position in the file. Because of the different + * interfaces used by the db(3) package, curses, and users, the line number is + * 1 based, while the column number is 0 based. Additionally, it is known that + * the out-of-band line number is less than any legal line number. The line + * number is of type recno_t, as that's the underlying type of the database. + * The column number is of type size_t, guaranteeing that we can malloc a line. + */ +struct _mark { + LIST_ENTRY(_mark) q; /* Linked list of marks. */ +#define OOBLNO 0 /* Out-of-band line number. */ + recno_t lno; /* Line number. */ + size_t cno; /* Column number. */ + CHAR_T name; /* Mark name. */ + +#define MARK_DELETED 0x01 /* Mark was deleted. */ +#define MARK_USERSET 0x02 /* User set this mark. */ + u_char flags; +}; + +#define ABSMARK1 '\'' /* Absolute mark name. */ +#define ABSMARK2 '`' /* Absolute mark name. */ + +/* Mark routines. */ +int mark_end __P((SCR *, EXF *)); +MARK *mark_get __P((SCR *, EXF *, ARG_CHAR_T)); +int mark_init __P((SCR *, EXF *)); +void mark_insdel __P((SCR *, EXF *, enum operation, recno_t)); +int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int)); diff --git a/usr.bin/vi/mem.h b/usr.bin/vi/mem.h new file mode 100644 index 0000000000..0f8fac4b8f --- /dev/null +++ b/usr.bin/vi/mem.h @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mem.h 8.2 (Berkeley) 12/19/93 + */ + +/* Increase the size of a malloc'd buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define BINC_GOTO(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + goto binc_err; \ +} +#define BINC_RET(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + return (1); \ +} + +/* + * Get some temporary space, preferably from the global temporary buffer, + * from a malloc'd buffer otherwise. Two versions, one that returns, one + * that jumps to an error label. + */ +#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_GOTO(sp, bp, blen, nlen); \ + } else { \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} +#define GET_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_RET(sp, bp, blen, nlen); \ + } else { \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} + +/* + * Add space to a GET_SPACE returned buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_GOTO(sp, bp, blen, nlen); \ +} +#define ADD_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_RET(sp, bp, blen, nlen); \ +} + +/* Free memory, optionally making pointers unusable. */ +#ifdef DEBUG +#define FREE(p, sz) { \ + memset(p, 0xff, sz); \ + free(p); \ +} +#else +#define FREE(p, sz) free(p); +#endif + +/* Free a GET_SPACE returned buffer. */ +#define FREE_SPACE(sp, bp, blen) { \ + if (bp == sp->gp->tmp_bp) \ + F_CLR(sp->gp, G_TMP_INUSE); \ + else \ + FREE(bp, blen); \ +} + +/* + * Malloc a buffer, casting the return pointer. Various versions. + * + * !!! + * The cast should be unnecessary, malloc(3) and friends return void *'s, + * which is all we need. However, some systems that nvi needs to run on + * don't do it right yet, resulting in the compiler printing out roughly + * a million warnings. After awhile, it seemed easier to put the casts + * in instead of explaining it all the time. + */ +#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \ + p = (cast)calloc(nmemb, size); \ +} +#define CALLOC(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define CALLOC_RET(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define MALLOC_NOMSG(sp, p, cast, size) { \ + p = (cast)malloc(size); \ +} +#define MALLOC(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define MALLOC_RET(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define REALLOC(sp, p, cast, size) { \ + if ((p = (cast)realloc(p, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} + +int binc __P((SCR *, void *, size_t *, size_t)); diff --git a/usr.bin/vi/msg.h b/usr.bin/vi/msg.h new file mode 100644 index 0000000000..6b20bb4bb6 --- /dev/null +++ b/usr.bin/vi/msg.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)msg.h 8.8 (Berkeley) 11/18/93 + */ + +/* + * M_BERR -- Error: ring a bell if O_VERBOSE not set, else + * display in inverse video. + * M_ERR -- Error: display in inverse video. + * M_INFO -- Info: display in normal video. + * M_SYSERR -- M_ERR, but use standard error message. + * M_VINFO -- Info: display only if O_VERBOSE set. + * + * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the + * error messages shorter. In this version, O_TERSE has no effect + * and O_VERBOSE results in informational displays about common + * errors. + */ +enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO }; + +typedef struct _msgh MSGH; /* MESG list head structure. */ +LIST_HEAD(_msgh, _msg); + +struct _msg { + LIST_ENTRY(_msg) q; /* Linked list of messages. */ + char *mbuf; /* Message buffer. */ + size_t blen; /* Message buffer length. */ + size_t len; /* Message length. */ + +#define M_EMPTY 0x01 /* No message. */ +#define M_INV_VIDEO 0x02 /* Inverse video. */ + u_int flags; /* Flags. */ +}; + +/* Messages. */ +void msg_app __P((GS *, SCR *, int, char *, size_t)); +int msg_rpt __P((SCR *, int)); +void msgq __P((SCR *, enum msgtype, const char *, ...)); diff --git a/usr.bin/vi/options.c b/usr.bin/vi/options.c new file mode 100644 index 0000000000..9c74160c5c --- /dev/null +++ b/usr.bin/vi/options.c @@ -0,0 +1,822 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options.c 8.36 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +static int opts_abbcmp __P((const void *, const void *)); +static int opts_cmp __P((const void *, const void *)); +static OPTLIST const *opts_prefix __P((char *)); +static int opts_print __P((SCR *, OPTLIST const *, OPTION *)); + +/* + * O'Reilly noted options and abbreviations are from "Learning the VI Editor", + * Fifth Edition, May 1992. There's no way of knowing what systems they are + * actually from. + * + * HPUX noted options and abbreviations are from "The Ultimate Guide to the + * VI and EX Text Editors", 1990. + */ +static OPTLIST const optlist[] = { +/* O_ALTWERASE 4.4BSD */ + {"altwerase", f_altwerase, OPT_0BOOL, 0}, +/* O_AUTOINDENT 4BSD */ + {"autoindent", NULL, OPT_0BOOL, 0}, +/* O_AUTOPRINT 4BSD */ + {"autoprint", NULL, OPT_1BOOL, 0}, +/* O_AUTOWRITE 4BSD */ + {"autowrite", NULL, OPT_0BOOL, 0}, +/* O_BEAUTIFY 4BSD */ + {"beautify", NULL, OPT_0BOOL, 0}, +/* O_COLUMNS 4.4BSD */ + {"columns", f_columns, OPT_NUM, OPT_NOSAVE}, +/* O_COMMENT 4.4BSD */ + {"comment", NULL, OPT_0BOOL, 0}, +/* O_DIGRAPH XXX: Elvis */ + {"digraph", NULL, OPT_0BOOL, 0}, +/* O_DIRECTORY 4BSD */ + {"directory", NULL, OPT_STR, 0}, +/* O_EDCOMPATIBLE 4BSD */ + {"edcompatible",NULL, OPT_0BOOL, 0}, +/* O_ERRORBELLS 4BSD */ + {"errorbells", NULL, OPT_0BOOL, 0}, +/* O_EXRC System V (undocumented) */ + {"exrc", NULL, OPT_0BOOL, 0}, +/* O_EXTENDED 4.4BSD */ + {"extended", NULL, OPT_0BOOL, 0}, +/* O_FLASH HPUX */ + {"flash", NULL, OPT_1BOOL, 0}, +/* O_HARDTABS 4BSD */ + {"hardtabs", NULL, OPT_NUM, 0}, +/* O_IGNORECASE 4BSD */ + {"ignorecase", NULL, OPT_0BOOL, 0}, +/* O_KEYTIME 4.4BSD */ + {"keytime", f_keytime, OPT_NUM, 0}, +/* O_LEFTRIGHT 4.4BSD */ + {"leftright", f_leftright, OPT_0BOOL, 0}, +/* O_LINES 4.4BSD */ + {"lines", f_lines, OPT_NUM, OPT_NOSAVE}, +/* O_LISP 4BSD */ + {"lisp", f_lisp, OPT_0BOOL, 0}, +/* O_LIST 4BSD */ + {"list", f_list, OPT_0BOOL, 0}, +/* O_MAGIC 4BSD */ + {"magic", NULL, OPT_1BOOL, 0}, +/* O_MATCHTIME 4.4BSD */ + {"matchtime", f_matchtime, OPT_NUM, 0}, +/* O_MESG 4BSD */ + {"mesg", f_mesg, OPT_1BOOL, 0}, +/* O_MODELINE 4BSD */ + {"modeline", f_modeline, OPT_0BOOL, 0}, +/* O_NUMBER 4BSD */ + {"number", f_number, OPT_0BOOL, 0}, +/* O_OPEN 4BSD */ + {"open", NULL, OPT_1BOOL, 0}, +/* O_OPTIMIZE 4BSD */ + {"optimize", f_optimize, OPT_1BOOL, 0}, +/* O_PARAGRAPHS 4BSD */ + {"paragraphs", f_paragraph, OPT_STR, 0}, +/* O_PROMPT 4BSD */ + {"prompt", NULL, OPT_1BOOL, 0}, +/* O_READONLY 4BSD (undocumented) */ + {"readonly", f_readonly, OPT_0BOOL, 0}, +/* O_RECDIR 4.4BSD */ + {"recdir", NULL, OPT_STR, 0}, +/* O_REDRAW 4BSD */ + {"redraw", NULL, OPT_0BOOL, 0}, +/* O_REMAP 4BSD */ + {"remap", NULL, OPT_1BOOL, 0}, +/* O_REPORT 4BSD */ + {"report", NULL, OPT_NUM, OPT_NOSTR}, +/* O_RULER 4.4BSD */ + {"ruler", f_ruler, OPT_0BOOL, 0}, +/* O_SCROLL 4BSD */ + {"scroll", NULL, OPT_NUM, 0}, +/* O_SECTIONS 4BSD */ + {"sections", f_section, OPT_STR, 0}, +/* O_SHELL 4BSD */ + {"shell", NULL, OPT_STR, 0}, +/* O_SHIFTWIDTH 4BSD */ + {"shiftwidth", f_shiftwidth, OPT_NUM, 0}, +/* O_SHOWDIRTY 4.4BSD */ + {"showdirty", NULL, OPT_0BOOL, 0}, +/* O_SHOWMATCH 4BSD */ + {"showmatch", NULL, OPT_0BOOL, 0}, +/* O_SHOWMODE 4.4BSD */ + {"showmode", NULL, OPT_0BOOL, 0}, +/* O_SIDESCROLL 4.4BSD */ + {"sidescroll", f_sidescroll, OPT_NUM, 0}, +/* O_SLOWOPEN 4BSD */ + {"slowopen", NULL, OPT_0BOOL, 0}, +/* O_SOURCEANY 4BSD (undocumented) */ + {"sourceany", f_sourceany, OPT_0BOOL, 0}, +/* O_TABSTOP 4BSD */ + {"tabstop", f_tabstop, OPT_NUM, 0}, +/* O_TAGLENGTH 4BSD */ + {"taglength", NULL, OPT_NUM, OPT_NOSTR}, +/* O_TAGS 4BSD */ + {"tags", f_tags, OPT_STR, 0}, +/* O_TERM 4BSD */ + {"term", f_term, OPT_STR, OPT_NOSAVE}, +/* O_TERSE 4BSD */ + {"terse", NULL, OPT_0BOOL, 0}, +/* O_TIMEOUT 4BSD (undocumented) */ + {"timeout", NULL, OPT_1BOOL, 0}, +/* O_TTYWERASE 4.4BSD */ + {"ttywerase", f_ttywerase, OPT_0BOOL, 0}, +/* O_VERBOSE 4.4BSD */ + {"verbose", NULL, OPT_0BOOL, 0}, +/* O_W1200 4BSD */ + {"w1200", f_w1200, OPT_NUM, OPT_NEVER}, +/* O_W300 4BSD */ + {"w300", f_w300, OPT_NUM, OPT_NEVER}, +/* O_W9600 4BSD */ + {"w9600", f_w9600, OPT_NUM, OPT_NEVER}, +/* O_WARN 4BSD */ + {"warn", NULL, OPT_1BOOL, 0}, +/* O_WINDOW 4BSD */ + {"window", f_window, OPT_NUM, 0}, +/* O_WRAPMARGIN 4BSD */ + {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR}, +/* O_WRAPSCAN 4BSD */ + {"wrapscan", NULL, OPT_1BOOL, 0}, +/* O_WRITEANY 4BSD */ + {"writeany", NULL, OPT_0BOOL, 0}, + {NULL}, +}; + +typedef struct abbrev { + char *name; + int offset; +} OABBREV; + +static OABBREV const abbrev[] = { + {"ai", O_AUTOINDENT}, /* 4BSD */ + {"ap", O_AUTOPRINT}, /* 4BSD */ + {"aw", O_AUTOWRITE}, /* 4BSD */ + {"bf", O_BEAUTIFY}, /* 4BSD */ + {"co", O_COLUMNS}, /* 4.4BSD */ + {"dir", O_DIRECTORY}, /* 4BSD */ + {"eb", O_ERRORBELLS}, /* 4BSD */ + {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */ + {"ex", O_EXRC}, /* System V (undocumented) */ + {"ht", O_HARDTABS}, /* 4BSD */ + {"ic", O_IGNORECASE}, /* 4BSD */ + {"li", O_LINES}, /* 4.4BSD */ + {"modelines", O_MODELINE}, /* HPUX */ + {"nu", O_NUMBER}, /* 4BSD */ + {"opt", O_OPTIMIZE}, /* 4BSD */ + {"para", O_PARAGRAPHS}, /* 4BSD */ + {"re", O_REDRAW}, /* O'Reilly */ + {"ro", O_READONLY}, /* 4BSD (undocumented) */ + {"scr", O_SCROLL}, /* 4BSD (undocumented) */ + {"sect", O_SECTIONS}, /* O'Reilly */ + {"sh", O_SHELL}, /* 4BSD */ + {"slow", O_SLOWOPEN}, /* 4BSD */ + {"sm", O_SHOWMATCH}, /* 4BSD */ + {"sw", O_SHIFTWIDTH}, /* 4BSD */ + {"tag", O_TAGS}, /* 4BSD (undocumented) */ + {"tl", O_TAGLENGTH}, /* 4BSD */ + {"to", O_TIMEOUT}, /* 4BSD (undocumented) */ + {"ts", O_TABSTOP}, /* 4BSD */ + {"tty", O_TERM}, /* 4BSD (undocumented) */ + {"ttytype", O_TERM}, /* 4BSD (undocumented) */ + {"w", O_WINDOW}, /* O'Reilly */ + {"wa", O_WRITEANY}, /* 4BSD */ + {"wi", O_WINDOW}, /* 4BSD (undocumented) */ + {"wm", O_WRAPMARGIN}, /* 4BSD */ + {"ws", O_WRAPSCAN}, /* 4BSD */ + {NULL}, +}; + +/* + * opts_init -- + * Initialize some of the options. Since the user isn't really + * "setting" these variables, don't set their OPT_SET bits. + */ +int +opts_init(sp) + SCR *sp; +{ + ARGS *argv[2], a, b; + OPTLIST const *op; + u_long v; + int cnt; + char *s, b1[1024]; + + a.bp = b1; + a.len = 0; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b; + +#define SET_DEF(opt, str) { \ + if (str != b1) /* GCC puts strings in text-space. */ \ + (void)strcpy(b1, str); \ + a.len = strlen(b1); \ + if (opts_set(sp, argv)) { \ + msgq(sp, M_ERR, \ + "Unable to set default %s option", optlist[opt]); \ + return (1); \ + } \ + F_CLR(&sp->opts[opt], OPT_SET); \ +} + /* Set default values. */ + for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) + if (op->type == OPT_0BOOL) + O_CLR(sp, cnt); + else if (op->type == OPT_1BOOL) + O_SET(sp, cnt); + + /* + * !!! + * Vi historically stored temporary files in /var/tmp. We store them + * in /tmp by default, hoping it's a memory based file system. There + * are two ways to change this -- the user can set either the directory + * option or the TMPDIR environmental variable. + */ + (void)snprintf(b1, sizeof(b1), "directory=%s", + (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s); + SET_DEF(O_DIRECTORY, b1); + SET_DEF(O_KEYTIME, "keytime=6"); + SET_DEF(O_MATCHTIME, "matchtime=7"); + SET_DEF(O_REPORT, "report=5"); + SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp"); + (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE); + SET_DEF(O_RECDIR, b1); + (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2); + SET_DEF(O_SCROLL, b1); + SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh"); + (void)snprintf(b1, sizeof(b1), + "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s); + SET_DEF(O_SHELL, b1); + SET_DEF(O_SHIFTWIDTH, "shiftwidth=8"); + SET_DEF(O_SIDESCROLL, "sidescroll=16"); + SET_DEF(O_TABSTOP, "tabstop=8"); + (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS); + SET_DEF(O_TAGS, b1); + (void)snprintf(b1, sizeof(b1), + "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s); + SET_DEF(O_TERM, b1); + + /* + * The default window option values are: + * 8 if baud rate <= 600 + * 16 if baud rate <= 1200 + * LINES - 1 if baud rate > 1200 + */ + v = baud_from_bval(sp); + if (v <= 600) + v = 8; + else if (v <= 1200) + v = 16; + else + v = O_VAL(sp, O_LINES) - 1; + (void)snprintf(b1, sizeof(b1), "window=%lu", v); + SET_DEF(O_WINDOW, b1); + + SET_DEF(O_WRAPMARGIN, "wrapmargin=0"); + + /* + * By default, the historic vi always displayed information + * about two options, redraw and term. Term seems sufficient. + */ + F_SET(&sp->opts[O_TERM], OPT_SET); + return (0); +} + +/* + * opts_set -- + * Change the values of one or more options. + */ +int +opts_set(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + enum optdisp disp; + OABBREV atmp, *ap; + OPTLIST const *op; + OPTLIST otmp; + OPTION *spo; + u_long value, turnoff; + int ch, offset, rval; + char *endp, *equals, *name, *p; + + disp = NO_DISPLAY; + for (rval = 0; (*argv)->len != 0; ++argv) { + /* + * The historic vi dumped the options for each occurrence of + * "all" in the set list. Puhleeze. + */ + if (!strcmp(argv[0]->bp, "all")) { + disp = ALL_DISPLAY; + continue; + } + + /* Find equals sign or end of set, skipping backquoted chars. */ + for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p) + switch(ch) { + case '=': + equals = p; + break; + case '\\': + /* Historic vi just used the backslash. */ + if (p[1] == '\0') + break; + ++p; + break; + } + + turnoff = 0; + op = NULL; + if (equals) + *equals++ = '\0'; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Try the name without any leading "no". */ + if (name[0] == 'n' && name[1] == 'o') { + turnoff = 1; + name += 2; + } else + goto prefix; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Check for prefix match. */ +prefix: op = opts_prefix(name); + +found: if (op == NULL) { + msgq(sp, M_ERR, + "no %s option: 'set all' gives all option values", + name); + continue; + } + + /* Find current option values. */ + offset = op - optlist; + spo = sp->opts + offset; + + /* Set name, value. */ + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (equals) { + msgq(sp, M_ERR, + "set: [no]%s option doesn't take a value", + name); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, NULL, turnoff)) { + rval = 1; + break; + } + } else if (turnoff) + O_CLR(sp, offset); + else + O_SET(sp, offset); + goto change; + case OPT_NUM: + /* + * !!! + * Extension to historic vi. If the OPT_NOSTR flag is + * set, a numeric option may be turned off by using a + * "no" prefix, e.g. "nowrapmargin". (We assume that + * setting the value to 0 turns a numeric option off.) + */ + if (turnoff) { + if (F_ISSET(op, OPT_NOSTR)) { + value = 0; + goto nostr; + } + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + value = strtol(equals, &endp, 10); + if (*endp && !isblank(*endp)) { + msgq(sp, M_ERR, + "set %s: illegal number %s", name, equals); + break; + } +nostr: if (op->func != NULL) { + if (op->func(sp, spo, equals, value)) { + rval = 1; + break; + } + } else + O_VAL(sp, offset) = value; + goto change; + case OPT_STR: + if (turnoff) { + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, equals, (u_long)0)) { + rval = 1; + break; + } + } else { + if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED)) + free(O_STR(sp, offset)); + if ((O_STR(sp, offset) = + strdup(equals)) == NULL) { + msgq(sp, M_SYSERR, NULL); + rval = 1; + break; + } else + F_SET(&sp->opts[offset], OPT_ALLOCATED); + } +change: if (sp->s_optchange != NULL) + (void)sp->s_optchange(sp, offset); + F_SET(&sp->opts[offset], OPT_SET); + break; + default: + abort(); + } + } + if (disp) + opts_dump(sp, disp); + return (rval); +} + +/* + * opts_dump -- + * List the current values of selected options. + */ +void +opts_dump(sp, type) + SCR *sp; + enum optdisp type; +{ + OPTLIST const *op; + int base, b_num, cnt, col, colwidth, curlen, s_num; + int numcols, numrows, row; + int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT]; + char nbuf[20]; + + /* + * Options are output in two groups -- those that fit in a column and + * those that don't. Output is done on 6 character "tab" boundaries + * for no particular reason. (Since we don't output tab characters, + * we can ignore the terminal's tab settings.) Ignore the user's tab + * setting because we have no idea how reasonable it is. + */ +#define BOUND 6 + + /* Find a column width we can live with. */ + for (cnt = 6; cnt > 1; --cnt) { + colwidth = (sp->cols - 1) / cnt & ~(BOUND - 1); + if (colwidth >= 10) { + colwidth = (colwidth + BOUND) & ~(BOUND - 1); + break; + } + colwidth = 0; + } + + /* + * Two passes. First, get the set of options to list, entering them + * into the column list or the overflow list. No error checking, + * since we know that at least one option (O_TERM) has the OPT_SET bit + * set. + */ + for (b_num = s_num = 0, op = optlist; op->name; ++op) { + cnt = op - optlist; + + /* If OPT_NEVER set, it's never displayed. */ + if (F_ISSET(op, OPT_NEVER)) + continue; + + switch (type) { + case ALL_DISPLAY: /* Display all. */ + break; + case CHANGED_DISPLAY: /* Display changed. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SET)) + continue; + break; + case SELECT_DISPLAY: /* Display selected. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED)) + continue; + break; + default: + case NO_DISPLAY: + abort(); + /* NOTREACHED */ + } + F_CLR(&sp->opts[cnt], OPT_SELECTED); + + curlen = strlen(op->name); + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (!O_ISSET(sp, cnt)) + curlen += 2; + break; + case OPT_NUM: + (void)snprintf(nbuf, + sizeof(nbuf), "%ld", O_VAL(sp, cnt)); + curlen += strlen(nbuf); + break; + case OPT_STR: + curlen += strlen(O_STR(sp, cnt)) + 3; + break; + } + /* Offset by two so there's a gap. */ + if (curlen < colwidth - 2) + s_op[s_num++] = cnt; + else + b_op[b_num++] = cnt; + } + + numcols = (sp->cols - 1) / colwidth; + if (s_num > numcols) { + numrows = s_num / numcols; + if (s_num % numcols) + ++numrows; + } else + numrows = 1; + + for (row = 0; row < numrows;) { + for (base = row, col = 0; col < numcols; ++col) { + cnt = opts_print(sp, + &optlist[s_op[base]], &sp->opts[s_op[base]]); + if ((base += numrows) >= s_num) + break; + (void)ex_printf(EXCOOKIE, + "%*s", (int)(colwidth - cnt), ""); + } + if (++row < numrows || b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + + for (row = 0; row < b_num;) { + (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]); + if (++row < b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + (void)ex_printf(EXCOOKIE, "\n"); +} + +/* + * opts_print -- + * Print out an option. + */ +static int +opts_print(sp, op, spo) + SCR *sp; + OPTLIST const *op; + OPTION *spo; +{ + int curlen, offset; + + curlen = 0; + offset = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + curlen += ex_printf(EXCOOKIE, + "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name); + break; + case OPT_NUM: + curlen += ex_printf(EXCOOKIE, + "%s=%ld", op->name, O_VAL(sp, offset)); + break; + case OPT_STR: + curlen += ex_printf(EXCOOKIE, + "%s=\"%s\"", op->name, O_STR(sp, offset)); + break; + } + return (curlen); +} + +/* + * opts_save -- + * Write the current configuration to a file. + */ +int +opts_save(sp, fp) + SCR *sp; + FILE *fp; +{ + OPTION *spo; + OPTLIST const *op; + int ch, cnt; + char *p; + + for (spo = sp->opts, op = optlist; op->name; ++op) { + if (F_ISSET(op, OPT_NOSAVE)) + continue; + cnt = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (O_ISSET(sp, cnt)) + (void)fprintf(fp, "set %s\n", op->name); + else + (void)fprintf(fp, "set no%s\n", op->name); + break; + case OPT_NUM: + (void)fprintf(fp, + "set %s=%-3d\n", op->name, O_VAL(sp, cnt)); + break; + case OPT_STR: + (void)fprintf(fp, "set "); + for (p = op->name; (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('=', fp); + for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + break; + } + if (ferror(fp)) { + msgq(sp, M_ERR, "I/O error: %s", strerror(errno)); + return (1); + } + } + return (0); +} + +/* + * opts_prefix -- + * Check to see if the name is the prefix of one (and only one) + * option. If so, return the option. + */ +static OPTLIST const * +opts_prefix(name) + char *name; +{ + OPTLIST const *op, *save_op; + size_t len; + + save_op = NULL; + len = strlen(name); + for (op = optlist; op->name != NULL; ++op) { + if (op->name[0] < name[0]) + continue; + if (op->name[0] > name[0]) + break; + if (!memcmp(op->name, name, len)) { + if (save_op != NULL) + return (NULL); + save_op = op; + } + } + return (save_op); +} + +static int +opts_abbcmp(a, b) + const void *a, *b; +{ + return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name)); +} + +static int +opts_cmp(a, b) + const void *a, *b; +{ + return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name)); +} + +/* + * opts_free -- + * Free all option strings + */ +void +opts_free(sp) + SCR *sp; +{ + int cnt; + char *p; + + for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) { + p = O_STR(sp, cnt); + FREE(p, strlen(p) + 1); + } +} + +/* + * opts_copy -- + * Copy a screen's OPTION array. + */ +int +opts_copy(orig, sp) + SCR *orig, *sp; +{ + OPTION *op; + int cnt; + + /* Copy most everything without change. */ + memmove(sp->opts, orig->opts, sizeof(orig->opts)); + + /* + * Allocate copies of the strings -- keep trying to reallocate + * after ENOMEM failure, otherwise end up with more than one + * screen referencing the original memory. + */ + for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) && + (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) { + msgq(orig, M_SYSERR, NULL); + return (1); + } + return (0); +} diff --git a/usr.bin/vi/options.h.stub b/usr.bin/vi/options.h.stub new file mode 100644 index 0000000000..f5f52b52b3 --- /dev/null +++ b/usr.bin/vi/options.h.stub @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h.stub 8.14 (Berkeley) 12/20/93 + */ + +struct _option { + union { + u_long val; /* Value or boolean. */ + char *str; /* String. */ + } o_u; + size_t len; /* String length. */ + +#define OPT_ALLOCATED 0x01 /* Allocated space. */ +#define OPT_SELECTED 0x02 /* Selected for display. */ +#define OPT_SET 0x04 /* Set (display for the user). */ + u_char flags; +}; + +struct _optlist { + char *name; /* Name. */ + /* Change function. */ + int (*func) __P((SCR *, OPTION *, char *, u_long)); + /* Type of object. */ + enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type; + +#define OPT_NEVER 0x01 /* Never display the option. */ +#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */ +#define OPT_NOSTR 0x04 /* String that takes a "no". */ + u_int flags; +}; + +/* Clear, set, test boolean options. */ +#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1 +#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0 +#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val) + +/* Get option values. */ +#define O_LEN(sp, o) (sp)->opts[(o)].len +#define O_STR(sp, o) (sp)->opts[(o)].o_u.str +#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val + +/* Option routines. */ +enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY }; + +int opts_copy __P((SCR *, SCR *)); +void opts_dump __P((SCR *, enum optdisp)); +void opts_free __P((SCR *)); +int opts_init __P((SCR *)); +int opts_save __P((SCR *, FILE *)); +int opts_set __P((SCR *, ARGS *[])); + +/* Per-option change routines. */ +int f_altwerase __P((SCR *, OPTION *, char *, u_long)); +int f_columns __P((SCR *, OPTION *, char *, u_long)); +int f_keytime __P((SCR *, OPTION *, char *, u_long)); +int f_leftright __P((SCR *, OPTION *, char *, u_long)); +int f_lines __P((SCR *, OPTION *, char *, u_long)); +int f_lisp __P((SCR *, OPTION *, char *, u_long)); +int f_list __P((SCR *, OPTION *, char *, u_long)); +int f_matchtime __P((SCR *, OPTION *, char *, u_long)); +int f_mesg __P((SCR *, OPTION *, char *, u_long)); +int f_modeline __P((SCR *, OPTION *, char *, u_long)); +int f_number __P((SCR *, OPTION *, char *, u_long)); +int f_optimize __P((SCR *, OPTION *, char *, u_long)); +int f_paragraph __P((SCR *, OPTION *, char *, u_long)); +int f_readonly __P((SCR *, OPTION *, char *, u_long)); +int f_ruler __P((SCR *, OPTION *, char *, u_long)); +int f_section __P((SCR *, OPTION *, char *, u_long)); +int f_shiftwidth __P((SCR *, OPTION *, char *, u_long)); +int f_sidescroll __P((SCR *, OPTION *, char *, u_long)); +int f_sourceany __P((SCR *, OPTION *, char *, u_long)); +int f_tabstop __P((SCR *, OPTION *, char *, u_long)); +int f_tags __P((SCR *, OPTION *, char *, u_long)); +int f_term __P((SCR *, OPTION *, char *, u_long)); +int f_ttywerase __P((SCR *, OPTION *, char *, u_long)); +int f_w1200 __P((SCR *, OPTION *, char *, u_long)); +int f_w300 __P((SCR *, OPTION *, char *, u_long)); +int f_w9600 __P((SCR *, OPTION *, char *, u_long)); +int f_window __P((SCR *, OPTION *, char *, u_long)); +int f_wrapmargin __P((SCR *, OPTION *, char *, u_long)); diff --git a/usr.bin/vi/options_f.c b/usr.bin/vi/options_f.c new file mode 100644 index 0000000000..efd596f958 --- /dev/null +++ b/usr.bin/vi/options_f.c @@ -0,0 +1,547 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options_f.c 8.25 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "vi.h" +#include "tag.h" + +static int opt_putenv __P((char *)); + +#define DECL(f) \ + int \ + f(sp, op, str, val) \ + SCR *sp; \ + OPTION *op; \ + char *str; \ + u_long val; +#define CALL(f) \ + f(sp, op, str, val) + +#define turnoff val + +DECL(f_altwerase) +{ + if (turnoff) + O_CLR(sp, O_ALTWERASE); + else { + O_SET(sp, O_ALTWERASE); + O_CLR(sp, O_TTYWERASE); + } + return (0); +} + +DECL(f_ttywerase) +{ + if (turnoff) + O_CLR(sp, O_TTYWERASE); + else { + O_SET(sp, O_TTYWERASE); + O_CLR(sp, O_ALTWERASE); + } + return (0); +} + +DECL(f_columns) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_COLS) { + msgq(sp, M_ERR, "Screen columns too small, less than %d.", + MINIMUM_SCREEN_COLS); + return (1); + } + if (val < O_VAL(sp, O_SHIFTWIDTH)) { + msgq(sp, M_ERR, + "Screen columns too small, less than shiftwidth."); + return (1); + } + if (val < O_VAL(sp, O_SIDESCROLL)) { + msgq(sp, M_ERR, + "Screen columns too small, less than sidescroll."); + return (1); + } + if (val < O_VAL(sp, O_TABSTOP)) { + msgq(sp, M_ERR, + "Screen columns too small, less than tabstop."); + return (1); + } + if (val < O_VAL(sp, O_WRAPMARGIN)) { + msgq(sp, M_ERR, + "Screen columns too small, less than wrapmargin."); + return (1); + } +#ifdef XXX_NOT_RIGHT + /* + * This has to be checked by reaching down into the screen code. + */ + if (val < O_NUMBER_LENGTH) { + msgq(sp, M_ERR, + "Screen columns too small, less than number option."); + return (1); + } +#endif + /* Set the columns value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_COLUMNS) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_COLUMNS) = val; + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_keytime) +{ + O_VAL(sp, O_KEYTIME) = val; + return (0); +} + +DECL(f_leftright) +{ + if (turnoff) + O_CLR(sp, O_LEFTRIGHT); + else + O_SET(sp, O_LEFTRIGHT); + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_lines) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen lines too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Set the rows value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_LINES) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_LINES) = val; + + /* + * If no window value set, set a new default window and, + * optionally, a new scroll value. + */ + if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) { + O_VAL(sp, O_WINDOW) = val - 1; + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = val / 2; + } + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_lisp) +{ + msgq(sp, M_ERR, "The lisp option is not implemented."); + return (0); +} + +DECL(f_list) +{ + if (turnoff) + O_CLR(sp, O_LIST); + else + O_SET(sp, O_LIST); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_matchtime) +{ + O_VAL(sp, O_MATCHTIME) = val; + return (0); +} + +DECL(f_mesg) +{ + struct stat sb; + char *tty; + + /* Find the tty. */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) { + msgq(sp, M_ERR, "ttyname: %s.", strerror(errno)); + return (1); + } + + /* Save the tty mode for later; only save it once. */ + if (!F_ISSET(sp->gp, G_SETMODE)) { + F_SET(sp->gp, G_SETMODE); + if (stat(tty, &sb) < 0) { + msgq(sp, M_ERR, "%s: %s.", tty, strerror(errno)); + return (1); + } + sp->gp->origmode = sb.st_mode; + } + + if (turnoff) { + if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned off: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_CLR(sp, O_MESG); + } else { + if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned on: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_SET(sp, O_MESG); + } + return (0); +} + +/* + * f_modeline -- + * This has been documented in historical systems as both "modeline" + * and as "modelines". Regardless of the name, this option represents + * a security problem of mammoth proportions, not to mention a stunning + * example of what your intro CS professor referred to as the perils of + * mixing code and data. Don't add it, or I will kill you. + */ +DECL(f_modeline) +{ + if (!turnoff) + msgq(sp, M_ERR, "The modeline(s) option may never be set."); + return (0); +} + +DECL(f_number) +{ + if (turnoff) + O_CLR(sp, O_NUMBER); + else + O_SET(sp, O_NUMBER); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_optimize) +{ + msgq(sp, M_ERR, "The optimize option is not implemented."); + return (0); +} + +DECL(f_paragraph) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Paragraph options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED)) + free(O_STR(sp, O_PARAGRAPHS)); + if ((O_STR(sp, O_PARAGRAPHS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_readonly) +{ + if (turnoff) { + O_CLR(sp, O_READONLY); + if (sp->frp != NULL) + F_CLR(sp->frp, FR_RDONLY); + } else { + O_SET(sp, O_READONLY); + if (sp->frp != NULL) + F_SET(sp->frp, FR_RDONLY); + } + return (0); +} + +DECL(f_ruler) +{ + if (turnoff) + O_CLR(sp, O_RULER); + else + O_SET(sp, O_RULER); + return (0); +} + +DECL(f_section) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Section options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_SECTIONS], OPT_ALLOCATED)) + free(O_STR(sp, O_SECTIONS)); + if ((O_STR(sp, O_SECTIONS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_SECTIONS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_shiftwidth) +{ + if (val == 0) { + msgq(sp, M_ERR, "The shiftwidth can't be set to 0."); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Shiftwidth can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SHIFTWIDTH) = val; + return (0); +} + +DECL(f_sidescroll) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Sidescroll can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SIDESCROLL) = val; + return (0); +} + +/* + * f_sourceany -- + * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they + * were owned by the user. The sourceany option was an undocumented + * feature of historic vi which permitted the startup source'ing of + * .exrc files the user didn't own. This is an obvious security problem, + * and we ignore the option. + */ +DECL(f_sourceany) +{ + if (!turnoff) + msgq(sp, M_ERR, "The sourceany option may never be set."); + return (0); +} + +DECL(f_tabstop) +{ + if (val == 0) { + msgq(sp, M_ERR, "Tab stops can't be set to 0."); + return (1); + } +#define MAXTABSTOP 20 + if (val > MAXTABSTOP) { + msgq(sp, M_ERR, + "Tab stops can't be larger than %d.", MAXTABSTOP); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Tab stops can't be larger than screen size.", + MAXTABSTOP); + return (1); + } + O_VAL(sp, O_TABSTOP) = val; + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_tags) +{ + char *p; + + /* Copy for user display. */ + p = O_STR(sp, O_TAGS); + if ((O_STR(sp, O_TAGS) = strdup(str)) == NULL) { + O_STR(sp, O_TAGS) = p; + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (F_ISSET(&sp->opts[O_TAGS], OPT_ALLOCATED)) + FREE(p, strlen(p) + 1); + F_SET(&sp->opts[O_TAGS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_term) +{ + char buf[256]; + + if (F_ISSET(&sp->opts[O_TERM], OPT_ALLOCATED)) + free(O_STR(sp, O_TERM)); + if ((O_STR(sp, O_TERM) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_TERM], OPT_ALLOCATED | OPT_SET); + + /* Set the terminal value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "TERM=%s", str); + if (opt_putenv(buf)) + return (1); + + if (set_window_size(sp, 0, 0)) + return (1); + return (0); +} + +DECL(f_w300) +{ + /* Historical behavior for w300 was < 1200. */ + if (baud_from_bval(sp) >= 1200) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W300) = val; + return (0); +} + +DECL(f_w1200) +{ + u_long v; + + /* Historical behavior for w1200 was == 1200. */ + v = baud_from_bval(sp); + if (v < 1200 || v > 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W1200) = val; + return (0); +} + +DECL(f_w9600) +{ + speed_t v; + + /* Historical behavior for w9600 was > 1200. */ + v = baud_from_bval(sp); + if (v <= 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W9600) = val; + return (0); +} + +DECL(f_window) +{ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Window too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_WINDOW) = val; + O_VAL(sp, O_SCROLL) = val / 2; + + return (0); +} + +DECL(f_wrapmargin) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Wrapmargin value can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_WRAPMARGIN) = val; + return (0); +} + +/* + * opt_putenv -- + * Put a value into the environment. We use putenv(3) because it's + * more portable. The following hack is because some moron decided + * to keep a reference to the memory passed to putenv(3), instead of + * having it allocate its own. Someone clearly needs to get promoted + * into management. + */ +static int +opt_putenv(s) + char *s; +{ + char *t; + + /* Memory leak. */ + if ((t = strdup(s)) == NULL) + return (1); + return (putenv(t)); +} diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h new file mode 100644 index 0000000000..be6352b9df --- /dev/null +++ b/usr.bin/vi/pathnames.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.5 (Berkeley) 12/21/93 + */ + +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEVNULL "/dev/null" +#define _PATH_EXRC ".exrc" +#define _PATH_NEXRC ".nexrc" +#define _PATH_PRESERVE "/var/tmp/vi.recover" +#define _PATH_SENDMAIL "/usr/sbin/sendmail" +#define _PATH_SYSEXRC "/etc/vi.exrc" +#define _PATH_TAGS "tags /var/db/libc.tags /sys/kern/tags" +#define _PATH_TMP "/tmp" +#define _PATH_TTY "/dev/tty" diff --git a/usr.bin/vi/recover.c b/usr.bin/vi/recover.c new file mode 100644 index 0000000000..f9a483236a --- /dev/null +++ b/usr.bin/vi/recover.c @@ -0,0 +1,622 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)recover.c 8.40 (Berkeley) 12/21/93"; +#endif /* not lint */ + +#include +#include +#include + +/* + * We include , because the flock(2) #defines were + * found there on historical systems. We also include + * because the open(2) #defines are found there on newer systems. + */ +#include + +#include /* MAXHOSTNAMELEN on some systems. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vi.h" +#include "pathnames.h" + +/* + * Recovery code. + * + * The basic scheme is there's a btree file, whose name we specify. The first + * time a file is modified, and then at RCV_PERIOD intervals after that, the + * btree file is synced to disk. Each time a keystroke is requested for a file + * the terminal routines check to see if the file needs to be synced. This, of + * course means that the data structures had better be consistent each time the + * key routines are called. + * + * We don't use timers other than to flag that the file should be synced. This + * would require that the SCR and EXF data structures be locked, the dbopen(3) + * routines lock out the timers for each update, etc. It's just not worth it. + * The only way we can lose in the current scheme is if the file is saved, then + * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more. + * Not likely. + * + * When a file is first modified, a file which can be handed off to the mailer + * is created. The file contains normal headers, with two additions, which + * occur in THIS order, as the FIRST TWO headers: + * + * Vi-recover-file: file_name + * Vi-recover-path: recover_path + * + * Since newlines delimit the headers, this means that file names cannot + * have newlines in them, but that's probably okay. + * + * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX". + */ + +#define VI_FHEADER "Vi-recover-file: " +#define VI_PHEADER "Vi-recover-path: " + +static void rcv_alrm __P((int)); +static int rcv_mailfile __P((SCR *, EXF *)); +static void rcv_syncit __P((SCR *, int)); + +/* + * rcv_tmp -- + * Build a file name that will be used as the recovery file. + */ +int +rcv_tmp(sp, ep, name) + SCR *sp; + EXF *ep; + char *name; +{ + struct stat sb; + int fd; + char *dp, *p, path[MAXPATHLEN]; + + /* + * If the recovery directory doesn't exist, try and create it. As + * the recovery files are themselves protected from reading/writing + * by other than the owner, the worst that can happen is that a user + * would have permission to remove other users recovery files. If + * the sticky bit has the BSD semantics, that too will be impossible. + */ + dp = O_STR(sp, O_RECDIR); + if (stat(dp, &sb)) { + if (errno != ENOENT || mkdir(dp, 0)) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); + } + + /* Newlines delimit the mail messages. */ + for (p = name; *p; ++p) + if (*p == '\n') { + msgq(sp, M_ERR, + "Files with newlines in the name are unrecoverable."); + return (1); + } + + (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp); + + /* + * !!! + * We depend on mkstemp(3) setting the permissions correctly. + * GP's, we do it ourselves, to keep the window as small as + * possible. + */ + if ((fd = mkstemp(path)) == -1) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(path, S_IRUSR | S_IWUSR); + (void)close(fd); + + if ((ep->rcv_path = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(path); + return (1); + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_init -- + * Force the file to be snapshotted for recovery. + */ +int +rcv_init(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + struct sigaction act; + recno_t lno; + + F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON); + + /* Build file to mail to the user. */ + if (rcv_mailfile(sp, ep)) + goto err; + + /* Force read of entire file. */ + if (file_lline(sp, ep, &lno)) + goto err; + + /* Turn on a busy message, and sync it to backing store. */ + busy_on(sp, 1, "Copying file for recovery..."); + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Preservation failed: %s: %s", + ep->rcv_path, strerror(errno)); + busy_off(sp); + goto err; + } + busy_off(sp); + + if (!F_ISSET(sp->gp, G_RECOVER_SET)) { + /* Install the recovery timer handler. */ + act.sa_handler = rcv_alrm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, NULL); + + /* Start the recovery timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, NULL)) { + msgq(sp, M_ERR, + "Error: setitimer: %s", strerror(errno)); +err: msgq(sp, M_ERR, + "Recovery after system crash not possible."); + return (1); + } + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_alrm -- + * Recovery timer interrupt handler. + */ +static void +rcv_alrm(signo) + int signo; +{ + F_SET(__global_list, G_SIGALRM); +} + +/* + * rcv_mailfile -- + * Build the file to mail to the user. + */ +static int +rcv_mailfile(sp, ep) + SCR *sp; + EXF *ep; +{ + struct passwd *pw; + uid_t uid; + FILE *fp; + time_t now; + int fd; + char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN]; + + if ((pw = getpwuid(uid = getuid())) == NULL) { + msgq(sp, M_ERR, "Information on user id %u not found.", uid); + return (1); + } + + (void)snprintf(path, sizeof(path), + "%s/recover.XXXXXX", O_STR(sp, O_RECDIR)); + if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) { + msgq(sp, M_ERR, + "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + if (fd != -1) + (void)close(fd); + return (1); + } + + /* + * We keep an open lock on the file so that the recover option can + * distinguish between files that are live and those that need to + * be recovered. There's an obvious window between the mkstemp call + * and the lock, but it's pretty small. + */ + if ((ep->rcv_fd = dup(fd)) != -1) + (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB); + + if ((ep->rcv_mpath = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + + t = FILENAME(sp->frp); + if ((p = strrchr(t, '/')) == NULL) + p = t; + else + ++p; + (void)time(&now); + (void)gethostname(host, sizeof(host)); + (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n", + VI_FHEADER, p, /* Non-standard. */ + VI_PHEADER, ep->rcv_path, /* Non-standard. */ + "Reply-To: root", + "From: root (Nvi recovery program)", + "To: ", pw->pw_name, + "Subject: Nvi saved the file ", p, + "Precedence: bulk"); /* For vacation(1). */ + (void)fprintf(fp, "%s%.24s%s%s\n%s%s", + "On ", ctime(&now), + ", the user ", pw->pw_name, + "was editing a file named ", p); + if (p != t) + (void)fprintf(fp, " (%s)", t); + (void)fprintf(fp, "\n%s%s%s\n", + "on the machine ", host, ", when it was saved for\nrecovery."); + (void)fprintf(fp, "\n%s\n%s\n%s\n\n", + "You can recover most, if not all, of the changes", + "to this file using the -l and -r options to nvi(1)", + "or nex(1)."); + + if (fflush(fp) || ferror(fp)) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + return (0); +} + +/* + * rcv_sync -- + * Sync the backing file. + */ +int +rcv_sync(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Automatic file backup failed: %s: %s", + ep->rcv_path, strerror(errno)); + value.it_interval.tv_sec = value.it_interval.tv_usec = 0; + value.it_value.tv_sec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + F_CLR(ep, F_RCV_ON); + return (1); + } + return (0); +} + +/* + * rcv_hup -- + * Recovery SIGHUP interrupt handler. (Modem line dropped, or + * xterm window closed.) + */ +void +rcv_hup() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. Send email to the user for each file saved. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGHUP, SIG_DFL); + (void)kill(0, SIGHUP); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_term -- + * Recovery SIGTERM interrupt handler. (Reboot or halt is running.) + */ +void +rcv_term() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGTERM, SIG_DFL); + (void)kill(0, SIGTERM); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_syncit -- + * Sync the file, optionally send mail. + */ +static void +rcv_syncit(sp, email) + SCR *sp; + int email; +{ + EXF *ep; + char comm[1024]; + + if ((ep = sp->ep) == NULL || + !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON)) + return; + + (void)ep->db->sync(ep->db, R_RECNOSYNC); + F_SET(ep, F_RCV_NORM); + + /* + * !!! + * If you need to port this to a system that doesn't have sendmail, + * the -t flag being used causes sendmail to read the message for + * the recipients instead of us specifying them some other way. + */ + if (email) { + (void)snprintf(comm, sizeof(comm), + "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath); + (void)system(comm); + } + (void)file_end(sp, ep, 1); +} + +/* + * people making love + * never exactly the same + * just like a snowflake + * + * rcv_list -- + * List the files that can be recovered by this user. + */ +int +rcv_list(sp) + SCR *sp; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FILE *fp; + int found; + char *p, file[1024]; + + if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) { + (void)fprintf(stderr, + "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + for (found = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + if ((fp = fopen(dp->d_name, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the header, get the file name. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL) { + (void)fprintf(stderr, + "vi: %s: malformed recovery file.\n", dp->d_name); + goto next; + } + *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + (void)fprintf(stderr, + "vi: %s: %s\n", dp->d_name, strerror(errno)); + goto next; + } + + /* Display. */ + (void)printf("%s: %s", + file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime)); + found = 1; + +next: (void)fclose(fp); + } + if (found == 0) + (void)printf("vi: no files to recover.\n"); + (void)closedir(dirp); + return (0); +} + +/* + * rcv_read -- + * Start a recovered file as the file to edit. + */ +int +rcv_read(sp, name) + SCR *sp; + char *name; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FREF *frp; + FILE *fp; + time_t rec_mtime; + int found, requested; + char *p, *t, *recp, *pathp; + char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN]; + + if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) { + msgq(sp, M_ERR, + "%s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + recp = pathp = NULL; + for (found = requested = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + (void)snprintf(recpath, sizeof(recpath), + "%s/%s", O_STR(sp, O_RECDIR), dp->d_name); + if ((fp = fopen(recpath, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the headers. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL || + fgets(path, sizeof(path), fp) == NULL || + strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) || + (t = strchr(path, '\n')) == NULL) { + msgq(sp, M_ERR, + "%s: malformed recovery file.", recpath); + goto next; + } + ++found; + *t = *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + msgq(sp, M_ERR, + "vi: %s: %s", dp->d_name, strerror(errno)); + goto next; + } + + /* Check the file name. */ + if (strcmp(file + sizeof(VI_FHEADER) - 1, name)) + goto next; + + ++requested; + + /* If we've found more than one, take the most recent. */ + if (recp == NULL || rec_mtime < sb.st_mtime) { + p = recp; + t = pathp; + if ((recp = strdup(recpath)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + recp = p; + goto next; + } + if ((pathp = strdup(path)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + FREE(recp, strlen(recp) + 1); + recp = p; + pathp = t; + goto next; + } + if (p != NULL) { + FREE(p, strlen(p) + 1); + FREE(t, strlen(t) + 1); + } + rec_mtime = sb.st_mtime; + } + +next: (void)fclose(fp); + } + (void)closedir(dirp); + + if (recp == NULL) { + msgq(sp, M_INFO, + "No files named %s, owned by you, to edit.", name); + return (1); + } + if (found) { + if (requested > 1) + msgq(sp, M_INFO, + "There are older versions of this file for you to recover."); + if (found > requested) + msgq(sp, M_INFO, + "There are other files that you can recover."); + } + + /* Create the FREF structure, start the btree file. */ + if ((frp = file_add(sp, NULL, name, 0)) == NULL || + file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) { + FREE(recp, strlen(recp) + 1); + FREE(pathp, strlen(pathp) + 1); + return (1); + } + sp->ep->rcv_mpath = recp; + return (0); +} diff --git a/usr.bin/vi/screen.c b/usr.bin/vi/screen.c new file mode 100644 index 0000000000..e18bd02183 --- /dev/null +++ b/usr.bin/vi/screen.c @@ -0,0 +1,296 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)screen.c 8.51 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "tag.h" + +/* + * screen_init -- + * Do the default initialization of an SCR structure. + */ +int +screen_init(orig, spp, flags) + SCR *orig, **spp; + u_int flags; +{ + SCR *sp; + size_t len; + + *spp = NULL; + CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR)); + *spp = sp; + +/* INITIALIZED AT SCREEN CREATE. */ + sp->gp = __global_list; /* All ref the GS structure. */ + + LIST_INIT(&sp->msgq); + CIRCLEQ_INIT(&sp->frefq); + + sp->ccnt = 2; /* Anything > 1 */ + + FD_ZERO(&sp->rdfd); + + CIRCLEQ_INIT(&sp->tiq); + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + sp->searchdir = NOTSET; + sp->csearchdir = CNOTSET; + + switch (flags & S_SCREENS) { + case S_EX: + if (sex_screen_init(sp)) + return (1); + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + return (1); + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + return (1); + break; + default: + abort(); + } + + sp->flags = flags; + } else { + if (orig->alt_name != NULL && + (sp->alt_name = strdup(orig->alt_name)) == NULL) + goto mem; + + /* Retain all searching/substitution information. */ + if (F_ISSET(orig, S_SRE_SET)) { + F_SET(sp, S_SRE_SET); + sp->sre = orig->sre; + } + if (F_ISSET(orig, S_SUBRE_SET)) { + F_SET(sp, S_SUBRE_SET); + sp->subre = orig->subre; + } + sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD; + sp->csearchdir = CNOTSET; + sp->lastckey = orig->lastckey; + + if (orig->matchsize) { + len = orig->matchsize * sizeof(regmatch_t); + MALLOC(sp, sp->match, regmatch_t *, len); + if (sp->match == NULL) + goto mem; + sp->matchsize = orig->matchsize; + memmove(sp->match, orig->match, len); + } + if (orig->repl_len) { + MALLOC(sp, sp->repl, char *, orig->repl_len); + if (sp->repl == NULL) + goto mem; + sp->repl_len = orig->repl_len; + memmove(sp->repl, orig->repl, orig->repl_len); + } + if (orig->newl_len) { + len = orig->newl_len * sizeof(size_t); + MALLOC(sp, sp->newl, size_t *, len); + if (sp->newl == NULL) + goto mem; + sp->newl_len = orig->newl_len; + sp->newl_cnt = orig->newl_cnt; + memmove(sp->newl, orig->newl, len); + } + + sp->saved_vi_mode = orig->saved_vi_mode; + + if (opts_copy(orig, sp)) { +mem: msgq(orig, M_SYSERR, "new screen attributes"); + (void)screen_end(sp); + return (1); + } + + sp->s_bell = orig->s_bell; + sp->s_bg = orig->s_bg; + sp->s_busy = orig->s_busy; + sp->s_change = orig->s_change; + sp->s_chposition = orig->s_chposition; + sp->s_clear = orig->s_clear; + sp->s_column = orig->s_column; + sp->s_confirm = orig->s_confirm; + sp->s_down = orig->s_down; + sp->s_edit = orig->s_edit; + sp->s_end = orig->s_end; + sp->s_ex_cmd = orig->s_ex_cmd; + sp->s_ex_run = orig->s_ex_run; + sp->s_ex_write = orig->s_ex_write; + sp->s_fg = orig->s_fg; + sp->s_fill = orig->s_fill; + sp->s_get = orig->s_get; + sp->s_key_read = orig->s_key_read; + sp->s_optchange = orig->s_optchange; + sp->s_position = orig->s_position; + sp->s_rabs = orig->s_rabs; + sp->s_refresh = orig->s_refresh; + sp->s_relative = orig->s_relative; + sp->s_rrel = orig->s_rrel; + sp->s_split = orig->s_split; + sp->s_suspend = orig->s_suspend; + sp->s_up = orig->s_up; + + F_SET(sp, F_ISSET(orig, S_SCREENS)); + } + + if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */ + return (1); + if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */ + return (1); + if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */ + return (1); + if (v_screen_copy(orig, sp)) /* Init vi. */ + return (1); + if (ex_screen_copy(orig, sp)) /* Init ex. */ + return (1); + + *spp = sp; + return (0); +} + +/* + * screen_end -- + * Release a screen. + */ +int +screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */ + rval = 1; + if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */ + rval = 1; + if (sex_screen_end(sp)) /* End S_EX screen. */ + rval = 1; + if (v_screen_end(sp)) /* End vi. */ + rval = 1; + if (ex_screen_end(sp)) /* End ex. */ + rval = 1; + + /* Free FREF's. */ + { FREF *frp; + while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) { + CIRCLEQ_REMOVE(&sp->frefq, frp, q); + if (frp->cname != NULL) + free(frp->cname); + if (frp->name != NULL) + free(frp->name); + if (frp->tname != NULL) + free(frp->tname); + FREE(frp, sizeof(FREF)); + } + } + + /* Free any text input. */ + text_lfree(&sp->tiq); + + /* Free any script information. */ + if (F_ISSET(sp, S_SCRIPT)) + sscr_end(sp); + + /* Free alternate file name. */ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + + /* Free up search information. */ + if (sp->match != NULL) + FREE(sp->match, sizeof(regmatch_t)); + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + if (sp->newl != NULL) + FREE(sp->newl, sp->newl_len); + + /* Free all the options */ + opts_free(sp); + + /* + * Free the message chain last, so previous failures have a place + * to put messages. Copy messages to (in order) a related screen, + * any screen, the global area. + */ + { SCR *c_sp; MSG *mp, *next; + if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = + sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else { + c_sp = NULL; + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(sp->gp, G_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, c_sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + FREE(mp->mbuf, mp->blen); + FREE(mp, sizeof(MSG)); + } + } + + /* Remove the screen from the displayed queue. */ + CIRCLEQ_REMOVE(&sp->gp->dq, sp, q); + + /* Free the screen itself. */ + FREE(sp, sizeof(SCR)); + + return (rval); +} diff --git a/usr.bin/vi/screen.h b/usr.bin/vi/screen.h new file mode 100644 index 0000000000..d95179352b --- /dev/null +++ b/usr.bin/vi/screen.h @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.h 8.81 (Berkeley) 12/29/93 + */ + +/* + * There are minimum values that vi has to have to display a screen. The + * row minimum is fixed at 1 line for the text, and 1 line for any error + * messages. The column calculation is a lot trickier. For example, you + * have to have enough columns to display the line number, not to mention + * guaranteeing that tabstop and shiftwidth values are smaller than the + * current column value. It's a lot simpler to have a fixed value and not + * worry about it. + * + * XXX + * MINIMUM_SCREEN_COLS is probably wrong. + */ +#define MINIMUM_SCREEN_ROWS 2 +#define MINIMUM_SCREEN_COLS 20 + /* Line operations. */ +enum operation { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET }; + /* Position values. */ +enum position { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP }; + +/* + * Structure for holding file references. Each SCR structure contains a + * linked list of these (the user's argument list) as well as pointers to + * the current and previous files. The structure contains the name of the + * file, along with the information that follows the name. A file has up + * to three "names". The tname field is the path of the temporary backing + * file, if any. The name field is the name the user originally used to + * specify the file to be edited. The cname field is the changed name if + * the user changed the name. + * + * Note that the read-only bit follows the file name, not the file itself. + * + * XXX + * The mtime field should be a struct timespec, but time_t is more portable. + */ +struct _fref { + CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */ + char *cname; /* Changed file name. */ + char *name; /* File name. */ + char *tname; /* Temporary file name. */ + + recno_t lno; /* 1-N: file cursor line. */ + size_t cno; /* 0-N: file cursor column. */ + time_t mtime; /* Last modification time. */ + +#define FR_CHANGEWRITE 0x001 /* Name changed and then written. */ +#define FR_CURSORSET 0x002 /* If lno/cno valid. */ +#define FR_EDITED 0x004 /* If the file was ever edited. */ +#define FR_IGNORE 0x008 /* File isn't part of argument list. */ +#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */ +#define FR_RDONLY 0x020 /* File is read-only. */ + u_int flags; +}; + +/* + * There's a file name hierarchy -- if the user has changed the name, we + * use it, otherwise, we use the original name, if there was one, othewise + * use the temporary name. + */ +#define FILENAME(frp) \ + ((frp)->cname != NULL) ? (frp)->cname : \ + ((frp)->name != NULL) ? (frp)->name : (frp)->tname + +/* + * SCR -- + * The screen structure. To the extent possible, all screen information + * is stored in the various private areas. The only information here + * is used by global routines or is shared by too many screens. + */ +struct _scr { +/* INITIALIZED AT SCREEN CREATE. */ + CIRCLEQ_ENTRY(_scr) q; /* Screens. */ + + GS *gp; /* Pointer to global area. */ + + SCR *nextdisp; /* Next display screen. */ + + EXF *ep; /* Screen's current EXF structure. */ + + MSGH msgq; /* Message list. */ + /* FREF list. */ + CIRCLEQ_HEAD(_frefh, _fref) frefq; + FREF *frp; /* FREF being edited. */ + FREF *a_frp; /* Last argument list FREF edited. */ + FREF *p_frp; /* Last FREF edited. */ + + u_long ccnt; /* Command count. */ + u_long q_ccnt; /* Quit or ZZ command count. */ + + /* Screen's: */ + size_t rows; /* 1-N: number of rows. */ + size_t cols; /* 1-N: number of columns. */ + size_t woff; /* 0-N: row offset in screen. */ + size_t t_rows; /* 1-N: cur number of text rows. */ + size_t t_maxrows; /* 1-N: max number of text rows. */ + size_t t_minrows; /* 1-N: min number of text rows. */ + + /* Cursor's: */ + recno_t lno; /* 1-N: file line. */ + size_t cno; /* 0-N: file character in line. */ + + size_t rcm; /* Vi: 0-N: Column suck. */ +#define RCM_FNB 0x01 /* Column suck: first non-blank. */ +#define RCM_LAST 0x02 /* Column suck: last. */ + u_int rcmflags; + +#define L_ADDED 0 /* Added lines. */ +#define L_CHANGED 1 /* Changed lines. */ +#define L_COPIED 2 /* Copied lines. */ +#define L_DELETED 3 /* Deleted lines. */ +#define L_JOINED 4 /* Joined lines. */ +#define L_MOVED 5 /* Moved lines. */ +#define L_PUT 6 /* Put lines. */ +#define L_LSHIFT 7 /* Left shift lines. */ +#define L_RSHIFT 8 /* Right shift lines. */ +#define L_YANKED 9 /* Yanked lines. */ + recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */ + + FILE *stdfp; /* Ex output file pointer. */ + + char *if_name; /* Ex input file name, for messages. */ + recno_t if_lno; /* Ex input file line, for messages. */ + + fd_set rdfd; /* Ex/vi: read fd select mask. */ + + TEXTH tiq; /* Ex/vi: text input queue. */ + + SCRIPT *script; /* Vi: script mode information .*/ + + char const *time_msg; /* ITIMER_REAL message. */ + struct itimerval time_value; /* ITIMER_REAL saved value. */ + struct sigaction time_handler; /* ITIMER_REAL saved handler. */ + + void *vi_private; /* Vi private area. */ + void *ex_private; /* Ex private area. */ + void *svi_private; /* Vi curses screen private area. */ + void *xaw_private; /* Vi XAW screen private area. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + char *alt_name; /* Ex/vi: alternate file name. */ + + /* Ex/vi: search/substitute info. */ + regex_t sre; /* Last search RE. */ + regex_t subre; /* Last substitute RE. */ + enum direction searchdir; /* File search direction. */ + enum cdirection csearchdir; /* Character search direction. */ + CHAR_T lastckey; /* Last search character. */ + regmatch_t *match; /* Substitute match array. */ + size_t matchsize; /* Substitute match array size. */ + char *repl; /* Substitute replacement. */ + size_t repl_len; /* Substitute replacement length.*/ + size_t *newl; /* Newline offset array. */ + size_t newl_len; /* Newline array size. */ + size_t newl_cnt; /* Newlines in replacement. */ + + u_int saved_vi_mode; /* Saved vi display type. */ + + OPTION opts[O_OPTIONCOUNT]; /* Options. */ + +/* + * SCREEN SUPPORT ROUTINES. + * + * A SCR * MUST be the first argument to these routines. + */ + /* Ring the screen bell. */ + void (*s_bell) __P((SCR *)); + /* Background the screen. */ + int (*s_bg) __P((SCR *)); + /* Put up a busy message. */ + int (*s_busy) __P((SCR *, char const *)); + /* Change a screen line. */ + int (*s_change) __P((SCR *, EXF *, recno_t, enum operation)); + /* Return column close to specified. */ + size_t (*s_chposition) __P((SCR *, EXF *, recno_t, size_t)); + /* Clear the screen. */ + int (*s_clear) __P((SCR *)); + /* Return the logical cursor column. */ + int (*s_column) __P((SCR *, EXF *, size_t *)); + enum confirm /* Confirm an action with the user. */ + (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *)); + /* Move down the screen. */ + int (*s_down) __P((SCR *, EXF *, MARK *, recno_t, int)); + /* Edit a file. */ + int (*s_edit) __P((SCR *, EXF *)); + /* End a screen. */ + int (*s_end) __P((SCR *)); + /* Run a single ex command. */ + int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *)); + /* Run user's ex commands. */ + int (*s_ex_run) __P((SCR *, EXF *, MARK *)); + /* Screen's ex write function. */ + int (*s_ex_write) __P((void *, const char *, int)); + /* Foreground the screen. */ + int (*s_fg) __P((SCR *, CHAR_T *)); + /* Fill the screen's map. */ + int (*s_fill) __P((SCR *, EXF *, recno_t, enum position)); + enum input /* Get a line from the user. */ + (*s_get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + enum input /* Get a key from the user. */ + (*s_key_read) __P((SCR *, int *, struct timeval *)); + /* Tell the screen an option changed. */ + int (*s_optchange) __P((SCR *, int)); + /* Return column at screen position. */ + int (*s_position) __P((SCR *, EXF *, + MARK *, u_long, enum position)); + /* Change the absolute screen size. */ + int (*s_rabs) __P((SCR *, long)); + /* Refresh the screen. */ + int (*s_refresh) __P((SCR *, EXF *)); + /* Return column close to last char. */ + size_t (*s_relative) __P((SCR *, EXF *, recno_t)); + /* Change the relative screen size. */ + int (*s_rrel) __P((SCR *, long)); + /* Split the screen. */ + int (*s_split) __P((SCR *, ARGS *[])); + /* Suspend the screen. */ + int (*s_suspend) __P((SCR *)); + /* Move up the screen. */ + int (*s_up) __P((SCR *, EXF *, MARK *, recno_t, int)); + +/* Editor screens. */ +#define S_EX 0x0000001 /* Ex screen. */ +#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */ +#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */ + +#define IN_EX_MODE(sp) /* If in ex mode. */ \ + (F_ISSET(sp, S_EX)) +#define IN_VI_MODE(sp) /* If in vi mode. */ \ + (F_ISSET(sp, S_VI_CURSES | S_VI_XAW)) +#define S_SCREENS /* Screens. */ \ + (S_EX | S_VI_CURSES | S_VI_XAW) + +/* Major screen/file changes. */ +#define S_EXIT 0x0000008 /* Exiting (not forced). */ +#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */ +#define S_FSWITCH 0x0000020 /* Switch files. */ +#define S_SSWITCH 0x0000040 /* Switch screens. */ +#define S_MAJOR_CHANGE /* Screen or file changes. */ \ + (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH) + +#define S_BELLSCHED 0x0000080 /* Bell scheduled. */ +#define S_CONTINUE 0x0000100 /* Need to ask the user to continue. */ +#define S_EXSILENT 0x0000200 /* Ex batch script. */ +#define S_GLOBAL 0x0000400 /* Doing a global command. */ +#define S_INPUT 0x0000800 /* Doing text input. */ +#define S_INTERRUPTED 0x0001000 /* If have been interrupted. */ +#define S_INTERRUPTIBLE 0x0002000 /* If can be interrupted. */ +#define S_REDRAW 0x0004000 /* Redraw the screen. */ +#define S_REFORMAT 0x0008000 /* Reformat the screen. */ +#define S_REFRESH 0x0010000 /* Refresh the screen. */ +#define S_RENUMBER 0x0020000 /* Renumber the screen. */ +#define S_RESIZE 0x0040000 /* Resize the screen. */ +#define S_SCRIPT 0x0080000 /* Window is a shell script. */ +#define S_SRE_SET 0x0100000 /* The search RE has been set. */ +#define S_SUBRE_SET 0x0200000 /* The substitute RE has been set. */ +#define S_TIMER_SET 0x0400000 /* If a busy timer is running. */ +#define S_UPDATE_MODE 0x0800000 /* Don't repaint modeline. */ + u_int flags; +}; + +/* Generic routines to start/stop a screen. */ +int screen_end __P((SCR *)); +int screen_init __P((SCR *, SCR **, u_int)); + +/* Public interfaces to the underlying screens. */ +int ex_screen_copy __P((SCR *, SCR *)); +int ex_screen_end __P((SCR *)); +int ex_screen_init __P((SCR *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_end __P((SCR *)); +int sex_screen_init __P((SCR *)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_end __P((SCR *)); +int svi_screen_init __P((SCR *)); +int v_screen_copy __P((SCR *, SCR *)); +int v_screen_end __P((SCR *)); +int v_screen_init __P((SCR *)); +int xaw_screen_copy __P((SCR *, SCR *)); +int xaw_screen_end __P((SCR *)); +int xaw_screen_init __P((SCR *)); diff --git a/usr.bin/vi/search.c b/usr.bin/vi/search.c new file mode 100644 index 0000000000..22aadef487 --- /dev/null +++ b/usr.bin/vi/search.c @@ -0,0 +1,860 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)search.c 8.32 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "vi.h" +#include "interrupt.h" + +static int check_delta __P((SCR *, EXF *, long, recno_t)); +static int ctag_conv __P((SCR *, char **, int *)); +static int get_delta __P((SCR *, char **, long *, u_int *)); +static int resetup __P((SCR *, regex_t **, enum direction, + char *, char **, long *, u_int *)); +static void search_intr __P((int)); + +/* + * resetup -- + * Set up a search for a regular expression. + */ +static int +resetup(sp, rep, dir, ptrn, epp, deltap, flagp) + SCR *sp; + regex_t **rep; + enum direction dir; + char *ptrn, **epp; + long *deltap; + u_int *flagp; +{ + u_int flags; + int delim, eval, re_flags, replaced; + char *p, *t; + + /* Set return information the default. */ + *deltap = 0; + + /* + * Use saved pattern if no pattern supplied, or if only a delimiter + * character is supplied. Only the pattern was saved, historic vi + * did not reuse any delta supplied. + */ + flags = *flagp; + if (ptrn == NULL) + goto prev; + if (ptrn[1] == '\0') { + if (epp != NULL) + *epp = ptrn + 1; + goto prev; + } + if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') { + if (epp != NULL) + *epp = ptrn + 2; +prev: if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + } + *rep = &sp->sre; + + /* Empty patterns set the direction. */ + if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + return (0); + } + + re_flags = 0; /* Set RE flags. */ + if (O_ISSET(sp, O_EXTENDED)) + re_flags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + re_flags |= REG_ICASE; + + if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */ + /* Set delimiter. */ + delim = *ptrn++; + + /* Find terminating delimiter, handling escaped delimiters. */ + for (p = t = ptrn;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + *t = '\0'; + break; + } + if (p[1] == delim && p[0] == '\\') + ++p; + *t++ = *p++; + } + + /* + * If characters after the terminating delimiter, it may + * be an error, or may be an offset. In either case, we + * return the end of the string, whatever it may be. + */ + if (*p) { + if (get_delta(sp, &p, deltap, flagp)) + return (1); + if (*p && LF_ISSET(SEARCH_TERM)) { + msgq(sp, M_ERR, + "Characters after search string and/or delta."); + return (1); + } + } + if (epp != NULL) + *epp = p; + + /* Check for "/ " or other such silliness. */ + if (*ptrn == '\0') + goto prev; + + if (re_conv(sp, &ptrn, &replaced)) + return (1); + } else if (LF_ISSET(SEARCH_TAG)) { + if (ctag_conv(sp, &ptrn, &replaced)) + return (1); + re_flags &= ~(REG_EXTENDED | REG_ICASE); + } + + /* Compile the RE. */ + if (eval = regcomp(*rep, ptrn, re_flags)) + re_error(sp, eval, *rep); + else if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + + /* Free up any extra memory. */ + if (replaced) + FREE_SPACE(sp, ptrn, 0); + return (eval); +} + +/* + * ctag_conv -- + * Convert a tags search path into something that the POSIX + * 1003.2 RE functions can handle. + */ +static int +ctag_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, len; + int lastdollar; + char *bp, *p, *t; + + *replacedp = 0; + + len = strlen(p = *ptrnp); + + /* Max memory usage is 2 times the length of the string. */ + GET_SPACE_RET(sp, bp, blen, len * 2); + + t = bp; + + /* The last charcter is a '/' or '?', we just strip it. */ + if (p[len - 1] == '/' || p[len - 1] == '?') + p[len - 1] = '\0'; + + /* The next-to-last character is a '$', and it's magic. */ + if (p[len - 2] == '$') { + lastdollar = 1; + p[len - 2] = '\0'; + } else + lastdollar = 0; + + /* The first character is a '/' or '?', we just strip it. */ + if (p[0] == '/' || p[0] == '?') + ++p; + + /* The second character is a '^', and it's magic. */ + if (p[0] == '^') + *t++ = *p++; + + /* + * Escape every other magic character we can find, stripping the + * backslashes ctags inserts to escape the search delimiter + * characters. + */ + while (p[0]) { + /* Ctags escapes the search delimiter characters. */ + if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) + ++p; + else if (strchr("^.[]$*", p[0])) + *t++ = '\\'; + *t++ = *p++; + } + if (lastdollar) + *t++ = '$'; + *t++ = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * search_intr -- + * Set the interrupt bit in any screen that is interruptible. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one search/global (see ex/ex_global.c) running, it will be hard + * to decide which one to stop. + */ +static void +search_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} + +#define EMPTYMSG "File empty; nothing to search." +#define EOFMSG "Reached end-of-file without finding the pattern." +#define NOTFOUND "Pattern not found." +#define SOFMSG "Reached top-of-file without finding the pattern." +#define WRAPMSG "Search wrapped." + +int +f_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* + * Start searching immediately after the cursor. If at the end of the + * line, start searching on the next line. This is incompatible (read + * bug fix) with the historic vi -- searches for the '$' pattern never + * moved forward, and "-t foo" didn't work if "foo" was the first thing + * in the file. + */ + if (LF_ISSET(SEARCH_FILE)) { + lno = 1; + coff = 0; + } else { + if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + if (fm->cno + 1 >= len) { + if (fm->lno == lno) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + return (1); + } + lno = 1; + } else + lno = fm->lno + 1; + coff = 0; + } else { + lno = fm->lno; + coff = fm->cno + 1; + } + } + + /* + * Set up busy message, interrupts. + * + * F_search is called from the ex_tagfirst() routine, which runs + * before the screen really exists. Make sure we don't step on + * anything. + */ + if (sp->s_position != NULL) + busy_on(sp, 1, "Searching..."); + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0;; ++lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno > fm->lno || + (l = file_gline(sp, ep, lno, &len)) == NULL) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + break; + } + lno = 0; + wrapped = 1; + continue; + } + + /* If already at EOL, just keep going. */ + if (len && coff == len) + continue; + + /* Set the termination. */ + match[0].rm_so = coff; + match[0].rm_eo = len; + +#if defined(DEBUG) && 0 + TRACE(sp, "F search: %lu from %u to %u\n", + lno, coff, len ? len - 1 : len); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + /* + * If an offset, see if it's legal. It's possible to match + * past the end of the line with $, so check for that case. + */ + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + rm->lno = lno; + rm->cno = match[0].rm_so; + + /* + * If a change command, it's possible to move beyond + * the end of a line. Historic vi generally got this + * wrong (try "c?$"). Not all that sure this gets + * it right, there are lots of strange cases. + */ + if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len) + rm->cno = len ? len - 1 : 0; + } + rval = 0; + break; + } + +interrupt_err: + + /* Turn off busy message, interrupts. */ + if (sp->s_position != NULL) + busy_off(sp); + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +int +b_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len, last; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* If in the first column, start searching on the previous line. */ + if (fm->cno == 0) { + if (fm->lno == 1) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + return (1); + } + } else + lno = fm->lno - 1; + } else + lno = fm->lno; + + /* Turn on busy message, interrupts. */ + busy_on(sp, 1, "Searching..."); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno < fm->lno || lno == 0) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + break; + } + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + break; + } + ++lno; + wrapped = 1; + continue; + } + + if ((l = file_gline(sp, ep, lno, &len)) == NULL) + goto err; + + /* Set the termination. */ + match[0].rm_so = 0; + match[0].rm_eo = coff ? coff : len; + +#if defined(DEBUG) && 0 + TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + /* + * Find the last acceptable one in this line. This + * is really painful, we need a cleaner interface to + * regexec to make this possible. + */ + for (;;) { + last = match[0].rm_so; + match[0].rm_so = match[0].rm_eo + 1; + if (match[0].rm_so >= len || + coff && match[0].rm_so >= coff) + break; + match[0].rm_eo = coff ? coff : len; + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | + REG_STARTEND); + if (eval == REG_NOMATCH) + break; + if (eval != 0) { + re_error(sp, eval, re); + goto err; + } + } + rm->lno = lno; + + /* See comment in f_search(). */ + if (!LF_ISSET(SEARCH_EOL) && last >= len) + rm->cno = len ? len - 1 : 0; + else + rm->cno = last; + } + rval = 0; + break; + } + + /* Turn off busy message, interrupts. */ +interrupt_err: +err: busy_off(sp); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +/* + * re_conv -- + * Convert vi's regular expressions into something that the + * the POSIX 1003.2 RE functions can handle. + * + * There are three conversions we make to make vi's RE's (specifically + * the global, search, and substitute patterns) work with POSIX RE's. + * + * 1: If O_MAGIC is not set, strip backslashes from the magic character + * set (.[]*~) that have them, and add them to the ones that don't. + * 2: If O_MAGIC is not set, the string "\~" is replaced with the text + * from the last substitute command's replacement string. If O_MAGIC + * is set, it's the string "~". + * 3: The pattern \ does "word" searches, convert it to use the + * new RE escapes. + */ +int +re_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, needlen; + int magic; + char *bp, *p, *t; + + /* + * First pass through, we figure out how much space we'll need. + * We do it in two passes, on the grounds that most of the time + * the user is doing a search and won't have magic characters. + * That way we can skip the malloc and memmove's. + */ + for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + magic = 1; + needlen += sizeof(RE_WSTART); + break; + case '>': + magic = 1; + needlen += sizeof(RE_WSTOP); + break; + case '~': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 1; + } + break; + default: + needlen += 2; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 2; + } + break; + default: + needlen += 1; + break; + } + + if (!magic) { + *replacedp = 0; + return (0); + } + + /* + * Get enough memory to hold the final pattern. + * + * XXX + * It's nul-terminated, for now. + */ + GET_SPACE_RET(sp, bp, blen, needlen + 1); + + for (p = *ptrnp, t = bp; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1); + t += sizeof(RE_WSTART) - 1; + break; + case '>': + memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1); + t += sizeof(RE_WSTOP) - 1; + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '~'; + else { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = '\\'; + *t++ = *p; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } else + *t++ = '~'; + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = *p; + break; + } + *t = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * get_delta -- + * Get a line delta. The trickiness is that the delta can be pretty + * complicated, i.e. "+3-2+3++- ++" is allowed. + * + * !!! + * In historic vi, if you had a delta on a search pattern which was used as + * a motion command, the command became a line mode command regardless of the + * cursor positions. A fairly common trick is to use a delta of "+0" to make + * the command a line mode command. This is the only place that knows about + * delta's, so we set the return flag information here. + */ +static int +get_delta(sp, dp, valp, flagp) + SCR *sp; + char **dp; + long *valp; + u_int *flagp; +{ + char *p; + long val, tval; + + for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) { + if (isblank(*p)) { + ++p; + continue; + } + if (*p == '+' || *p == '-') { + if (!isdigit(*(p + 1))) { + if (*p == '+') { + if (tval == LONG_MAX) + goto overflow; + ++tval; + } else { + if (tval == LONG_MIN) + goto underflow; + --tval; + } + ++p; + continue; + } + } else + if (!isdigit(*p)) + break; + + errno = 0; + val = strtol(p, &p, 10); + if (errno == ERANGE) { + if (val == LONG_MAX) +overflow: msgq(sp, M_ERR, "Delta value overflow."); + else if (val == LONG_MIN) +underflow: msgq(sp, M_ERR, "Delta value underflow."); + else + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (val >= 0) { + if (LONG_MAX - val < tval) + goto overflow; + } else + if (-(LONG_MIN - tval) > val) + goto underflow; + tval += val; + } + *dp = p; + *valp = tval; + return (0); +} + +/* + * check_delta -- + * Check a line delta to see if it's legal. + */ +static int +check_delta(sp, ep, delta, lno) + SCR *sp; + EXF *ep; + long delta; + recno_t lno; +{ + /* A delta can overflow a record number. */ + if (delta < 0) { + if (lno < LONG_MAX && delta >= (long)lno) { + msgq(sp, M_ERR, "Search offset before line 1."); + return (1); + } + } else { + if (ULONG_MAX - lno < delta) { + msgq(sp, M_ERR, "Delta value overflow."); + return (1); + } + if (file_gline(sp, ep, lno + delta, NULL) == NULL) { + msgq(sp, M_ERR, "Search offset past end-of-file."); + return (1); + } + } + return (0); +} + +/* + * re_error -- + * Report a regular expression error. + */ +void +re_error(sp, errcode, preg) + SCR *sp; + int errcode; + regex_t *preg; +{ + size_t s; + char *oe; + + s = regerror(errcode, preg, "", 0); + if ((oe = malloc(s)) == NULL) + msgq(sp, M_SYSERR, NULL); + else { + (void)regerror(errcode, preg, oe, s); + msgq(sp, M_ERR, "RE error: %s", oe); + } + free(oe); +} diff --git a/usr.bin/vi/search.h b/usr.bin/vi/search.h new file mode 100644 index 0000000000..00feff6027 --- /dev/null +++ b/usr.bin/vi/search.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)search.h 8.7 (Berkeley) 11/18/93 + */ + +#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */ +#define RE_WSTOP "[[:>:]]" + +#define SEARCH_DELTA 0x001 /* A delta part of the search.*/ +#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */ +#define SEARCH_FILE 0x004 /* Search the entire file. */ +#define SEARCH_MSG 0x008 /* Display search warning messages. */ +#define SEARCH_PARSE 0x010 /* Parse the search pattern. */ +#define SEARCH_SET 0x020 /* Set search direction. */ +#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */ +#define SEARCH_TERM 0x080 /* Search pattern should terminate. */ + +enum direction { NOTSET, FORWARD, BACKWARD }; +enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH }; + +/* Search functions. */ +int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int re_conv __P((SCR *, char **, int *)); +void re_error __P((SCR *, int, regex_t *)); diff --git a/usr.bin/vi/seq.c b/usr.bin/vi/seq.c new file mode 100644 index 0000000000..4d6d44956b --- /dev/null +++ b/usr.bin/vi/seq.c @@ -0,0 +1,299 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)seq.c 8.21 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * seq_set -- + * Internal version to enter a sequence. + */ +int +seq_set(sp, name, nlen, input, ilen, output, olen, stype, userdef) + SCR *sp; + char *name, *input, *output; + size_t nlen, ilen, olen; + enum seqtype stype; + int userdef; +{ + SEQ *lastqp, *qp; + CHAR_T *p; + +#if defined(DEBUG) && 0 + TRACE(sp, "seq_set: name {%s} input {%s} output {%s}\n", + name ? name : "", input, output); +#endif + /* Just replace the output field in any previous occurrence. */ + if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) { + if ((p = v_strdup(sp, output, olen)) == NULL) + goto mem1; + FREE(qp->output, qp->olen); + qp->olen = olen; + qp->output = p; + return (0); + } + + /* Allocate and initialize space. */ + CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ)); + if (qp == NULL) + goto mem1; + if (name == NULL) + qp->name = NULL; + else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) + goto mem2; + if ((qp->input = v_strdup(sp, input, ilen)) == NULL) + goto mem3; + if ((qp->output = v_strdup(sp, output, olen)) == NULL) { + FREE(qp->input, ilen); +mem3: if (qp->name != NULL) + FREE(qp->name, nlen); +mem2: FREE(qp, sizeof(SEQ)); +mem1: msgq(sp, M_SYSERR, NULL); + return (1); + } + + qp->stype = stype; + qp->nlen = nlen; + qp->ilen = ilen; + qp->olen = olen; + qp->flags = userdef ? S_USERDEF : 0; + + /* Link into the chain. */ + if (lastqp == NULL) { + LIST_INSERT_HEAD(&sp->gp->seqq, qp, q); + } else { + LIST_INSERT_AFTER(lastqp, qp, q); + } + + /* Set the fast lookup bit. */ + bit_set(sp->gp->seqb, qp->input[0]); + + return (0); +} + +/* + * seq_delete -- + * Delete a sequence. + */ +int +seq_delete(sp, input, ilen, stype) + SCR *sp; + char *input; + size_t ilen; + enum seqtype stype; +{ + SEQ *qp; + + if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL) + return (1); + + LIST_REMOVE(qp, q); + if (qp->name != NULL) + FREE(qp->name, qp->nlen); + FREE(qp->input, qp->ilen); + FREE(qp->output, qp->olen); + FREE(qp, sizeof(SEQ)); + return (0); +} + +/* + * seq_find -- + * Search the sequence list for a match to a buffer, if ispartial + * isn't NULL, partial matches count. + */ +SEQ * +seq_find(sp, lastqp, input, ilen, stype, ispartialp) + SCR *sp; + SEQ **lastqp; + char *input; + size_t ilen; + enum seqtype stype; + int *ispartialp; +{ + SEQ *lqp, *qp; + int diff; + + /* + * Ispartialp is a location where we return if there was a + * partial match, i.e. if the string were extended it might + * match something. + * + * XXX + * Overload the meaning of ispartialp; only the terminal key + * search doesn't want the search limited to complete matches, + * i.e. ilen may be longer than the match. + */ + if (ispartialp != NULL) + *ispartialp = 0; + for (lqp = NULL, qp = sp->gp->seqq.lh_first; + qp != NULL; lqp = qp, qp = qp->q.le_next) { + /* Fast checks on the first character and type. */ + if (qp->input[0] > input[0]) + break; + if (qp->input[0] < input[0] || qp->stype != stype) + continue; + + /* Check on the real comparison. */ + diff = memcmp(qp->input, input, MIN(qp->ilen, ilen)); + if (diff > 0) + break; + if (diff < 0) + continue; + /* + * If the entry is the same length as the string, return a + * match. If the entry is shorter than the string, return a + * match if called from the terminal key routine. Otherwise, + * keep searching for a complete match. + */ + if (qp->ilen <= ilen) { + if (qp->ilen == ilen || ispartialp != NULL) { + if (lastqp != NULL) + *lastqp = lqp; + return (qp); + } + continue; + } + /* + * If the entry longer than the string, return partial match + * if called from the terminal key routine. Otherwise, no + * match. + */ + if (ispartialp != NULL) + *ispartialp = 1; + break; + } + if (lastqp != NULL) + *lastqp = lqp; + return (NULL); +} + +/* + * seq_dump -- + * Display the sequence entries of a specified type. + */ +int +seq_dump(sp, stype, isname) + SCR *sp; + enum seqtype stype; + int isname; +{ + CHNAME const *cname; + SEQ *qp; + int cnt, len, olen, tablen; + char *p; + + cnt = 0; + cname = sp->gp->cname; + tablen = O_VAL(sp, O_TABSTOP); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (stype != qp->stype) + continue; + ++cnt; + for (p = qp->input, + olen = qp->ilen, len = 0; olen > 0; --olen, ++len) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + + for (p = qp->output, olen = qp->olen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + + if (isname && qp->name != NULL) { + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + for (p = qp->name, olen = qp->nlen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, + "%s", cname[*p++].name); + } + (void)ex_printf(EXCOOKIE, "\n"); + } + return (cnt); +} + +/* + * seq_save -- + * Save the sequence entries to a file. + */ +int +seq_save(sp, fp, prefix, stype) + SCR *sp; + FILE *fp; + char *prefix; + enum seqtype stype; +{ + CHAR_T esc; + SEQ *qp; + size_t olen; + int ch; + char *p; + + /* Write a sequence command for all keys the user defined. */ + (void)term_key_ch(sp, K_VLNEXT, &esc); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (!F_ISSET(qp, S_USERDEF)) + continue; + if (stype != qp->stype) + continue; + if (prefix) + (void)fprintf(fp, "%s", prefix); + for (p = qp->input, olen = qp->ilen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + isblank(ch) || term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc(' ', fp); + for (p = qp->output, olen = qp->olen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + } + return (0); +} diff --git a/usr.bin/vi/seq.h b/usr.bin/vi/seq.h new file mode 100644 index 0000000000..b9d9ac6cfd --- /dev/null +++ b/usr.bin/vi/seq.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)seq.h 8.7 (Berkeley) 12/2/93 + */ + +/* + * Map and abbreviation structures. + * + * The map structure is doubly linked list, sorted by input string and by + * input length within the string. (The latter is necessary so that short + * matches will happen before long matches when the list is searched.) + * Additionally, there is a bitmap which has bits set if there are entries + * starting with the corresponding character. This keeps us from walking + * the list unless it's necessary. + * + * XXX + * The fast-lookup bits are never turned off -- users don't usually unmap + * things, though, so it's probably not a big deal. + */ + /* Sequence type. */ +enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT }; + +struct _seq { + LIST_ENTRY(_seq) q; /* Linked list of all sequences. */ + enum seqtype stype; /* Sequence type. */ + char *name; /* Sequence name (if any). */ + size_t nlen; /* Name length. */ + char *input; /* Sequence input keys. */ + size_t ilen; /* Input keys length. */ + char *output; /* Sequence output keys. */ + size_t olen; /* Output keys length. */ + +#define S_USERDEF 0x01 /* If sequence user defined. */ + u_char flags; +}; + +int abbr_save __P((SCR *, FILE *)); +int map_save __P((SCR *, FILE *)); +int seq_delete __P((SCR *, char *, size_t, enum seqtype)); +int seq_dump __P((SCR *, enum seqtype, int)); +SEQ *seq_find __P((SCR *, SEQ **, char *, size_t, enum seqtype, int *)); +void seq_init __P((SCR *)); +int seq_save __P((SCR *, FILE *, char *, enum seqtype)); +int seq_set __P((SCR *, char *, size_t, + char *, size_t, char *, size_t, enum seqtype, int)); diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c new file mode 100644 index 0000000000..8b55fadeda --- /dev/null +++ b/usr.bin/vi/sex/sex_confirm.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +enum confirm +sex_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + recno_t cnt; + + if (ex_print(sp, ep, fp, tp, 0)) + return (CONF_QUIT); + + for (cnt = fp->cno; cnt; --cnt) + (void)putc(' ', stdout); + for (cnt = tp->cno; cnt; --cnt) + (void)putc('^', stdout); + (void)fprintf(stdout, "[ynq]"); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c new file mode 100644 index 0000000000..54b7343608 --- /dev/null +++ b/usr.bin/vi/sex/sex_get.c @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_get.c 8.12 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include + +#include +#include + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +static void repaint __P((SCR *, int, char *, size_t)); + +#define DISPLAY(wval, ch, col) { \ + size_t __len; \ + int __ch; \ + if ((__ch = (ch)) == '\t') { \ + __len = O_VAL(sp, O_TABSTOP) - \ + ((col) % O_VAL(sp, O_TABSTOP)); \ + (col) += (wval) = __len; \ + while (__len--) \ + putc(' ', stdout); \ + } else { \ + (col) += (wval) = cname[(__ch)].len; \ + (void)fprintf(stdout, \ + "%.*s", cname[(__ch)].len, cname[(__ch)].name); \ + } \ +} + +#define ERASECH { \ + for (cnt = tp->wd[tp->len]; cnt > 0; --cnt, --col) \ + (void)fprintf(stdout, "%s", "\b \b"); \ +} + +/* + * sex_get -- + * Fill a buffer from the terminal for ex. + */ +enum input +sex_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + size_t cnt; + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + if (LF_ISSET(TXT_PROMPT) && O_ISSET(sp, O_PROMPT)) { + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + (void)fflush(stdout); + + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) { + ERASECH; + goto ins_ch; + } + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + repaint(sp, prompt, tp->lb, tp->len); + break; + case K_CR: + case K_NL: + if (LF_ISSET(TXT_NLECHO)) { + (void)putc('\r', stdout); + (void)putc('\n', stdout); + (void)fflush(stdout); + } + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) { + --tp->len; + ERASECH; + } + break; + case K_VKILL: + for (; tp->len; --tp->len) + ERASECH; + break; + case K_VLNEXT: + (void)fprintf(stdout, "%s%c", cname['^'].name, '\b'); + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + DISPLAY(tp->wd[tp->len], ikey.ch, col); + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * sex_get_notty -- + * Fill a buffer from the terminal for ex, but don't echo + * input. + */ +enum input +sex_get_notty(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) + goto ins_ch; + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + break; + case K_CR: + case K_NL: + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) + --tp->len; + break; + case K_VKILL: + tp->len = 0; + break; + case K_VLNEXT: + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * repaint -- + * Repaint the line. + */ +static void +repaint(sp, prompt, p, len) + SCR *sp; + int prompt; + char *p; + size_t len; +{ + CHNAME const *cname; + size_t col; + u_char width; + + cname = sp->gp->cname; + + (void)putc('\n', stdout); + if (prompt && O_ISSET(sp, O_PROMPT)) { /* Display prompt. */ + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + while (len--) + DISPLAY(width, *p++, col); +} diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c new file mode 100644 index 0000000000..0ba5892990 --- /dev/null +++ b/usr.bin/vi/sex/sex_refresh.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_refresh.c 8.5 (Berkeley) 11/18/93"; +#endif /* not lint */ + +#include + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_refresh -- + * In ex, just display any messages. + */ +int +sex_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + MSG *mp; + + if (F_ISSET(sp, S_RESIZE)) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + F_CLR(sp, S_RESIZE); + } + + /* Ring the bell. */ + if (F_ISSET(sp, S_BELLSCHED)) { + sex_bell(sp); + F_CLR(sp, S_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) { + (void)fprintf(stdout, "%.*s\n", (int)mp->len, mp->mbuf); + F_SET(mp, M_EMPTY); + } + return (0); +} diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c new file mode 100644 index 0000000000..d087d9f174 --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_screen.c 8.30 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include + +#include +#include + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" +#include "../svi/svi_screen.h" + +static void sex_abort __P((void)); +static int sex_noop __P((void)); +static int sex_nope __P((SCR *)); + +/* + * sex_screen_init -- + * Initialize the ex screen. + */ +int +sex_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = sex_bell; + sp->s_bg = (int (*)())sex_nope; + sp->s_busy = (int (*)())sex_busy; + sp->s_change = (int (*)())sex_noop; + sp->s_chposition = (size_t (*)())sex_abort; + sp->s_clear = (int (*)())sex_noop; + sp->s_column = (int (*)())sex_abort; + sp->s_confirm = sex_confirm; + sp->s_down = (int (*)())sex_abort; + sp->s_edit = sex_screen_edit; + sp->s_end = (int (*)())sex_noop; + sp->s_ex_cmd = (int (*)())sex_abort; + sp->s_ex_run = (int (*)())sex_abort; + sp->s_ex_write = (int (*)())sex_abort; + sp->s_fg = (int (*)())sex_nope; + sp->s_fill = (int (*)())sex_abort; + sp->s_get = F_ISSET(sp->gp, + G_ISFROMTTY) ? sex_get : sex_get_notty; + sp->s_key_read = sex_key_read; + sp->s_optchange = (int (*)())sex_noop; + sp->s_position = (int (*)())sex_abort; + sp->s_rabs = (int (*)())sex_nope; + sp->s_refresh = sex_refresh; + sp->s_relative = (size_t (*)())sex_abort; + sp->s_rrel = (int (*)())sex_nope; + sp->s_split = (int (*)())sex_nope; + sp->s_suspend = sex_suspend; + sp->s_up = (int (*)())sex_abort; + + return (0); +} + +/* + * sex_screen_copy -- + * Copy to a new screen. + */ +int +sex_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * sex_screen_end -- + * End a screen. + */ +int +sex_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * sex_screen_edit -- + * Main ex screen loop. The ex screen is relatively uncomplicated. + * As long as it has a stdio FILE pointer for output, it's happy. + */ +int +sex_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + struct termios rawt, t; + GS *saved_gp; + int force, rval; + + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SEX_RAW(t, rawt); + + /* Write to the terminal. */ + sp->stdfp = stdout; + + for (;;) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + + /* + * Run ex. If ex fails, sex data structures + * may be corrupted, be careful what you do. + */ + if (rval = ex(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); + (void)screen_end(sp); /* General SCR info. */ + break; + } + + saved_gp = sp->gp; + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force)) + break; + (void)screen_end(sp); /* General SCR info. */ + goto ret; + case 0: /* Changing from ex mode. */ + goto ret; + case S_FSWITCH: + F_CLR(sp, S_FSWITCH); + break; + case S_SSWITCH: + default: + abort(); + } + } + + /* Reset the terminal state. */ +ret: if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + return (rval); +} + +/* + * sex_abort -- + * Fake function. Die. + */ +static void +sex_abort() +{ + abort(); +} + +/* + * sex_noop -- + * Fake function. Do nothing. + */ +static int +sex_noop() +{ + return (0); +} + +/* + * sex_nope -- + * Fake function. Not in ex, you don't. + */ +static int +sex_nope(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "Command not applicable to ex mode."); + return (1); +} diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h new file mode 100644 index 0000000000..419a2f274e --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sex_screen.h 8.12 (Berkeley) 11/29/93 + */ + +#define SEX_NORAW(t) \ + tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t)) + +#define SEX_RAW(t, rawt) { \ + if (tcgetattr(STDIN_FILENO, &(t))) \ + return (1); \ + (rawt) = (t); \ + (rawt).c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL); \ + (rawt).c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \ + (rawt).c_cc[VMIN] = 1; \ + (rawt).c_cc[VTIME] = 0; \ + if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(rawt))) \ + return (1); \ +} + +void sex_bell __P((SCR *)); +void sex_busy __P((SCR *, char const *)); +enum confirm + sex_confirm __P((SCR *, EXF *, MARK *, MARK *)); +enum input + sex_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_get_notty __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_key_read __P((SCR *, int *, struct timeval *)); +int sex_refresh __P((SCR *, EXF *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_edit __P((SCR *, EXF *)); +int sex_screen_end __P((SCR *)); +int sex_split __P((SCR *, char *[])); +int sex_suspend __P((SCR *)); diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c new file mode 100644 index 0000000000..4e3980f21e --- /dev/null +++ b/usr.bin/vi/sex/sex_term.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_term.c 8.24 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include + +#include "vi.h" +#include "excmd.h" +#include "script.h" + +/* + * sex_key_read -- + * Read characters from the input. + */ +enum input +sex_key_read(sp, nrp, timeout) + SCR *sp; + int *nrp; + struct timeval *timeout; +{ + struct timeval t, *tp; + IBUF *tty; + SCR *tsp; + int maxfd, nr; + + *nrp = 0; + tty = sp->gp->tty; + + /* + * We're about to block; check for signals. If a signal received, + * clear it immediately, so that if it's reset while being serviced + * we won't miss it. + * + * Signal recipients set global flags. If one is set, we either + * set local flags or call handling routines. None of this has + * anything to do with input keys, but it's something that can't + * be done asynchronously without doing a lot of locking to handle + * race conditions, and which needs to be done periodically. + */ +sigchk: while (F_ISSET(sp->gp, + G_SIGALRM | G_SIGHUP | G_SIGTERM | G_SIGWINCH)) { + if (F_ISSET(sp->gp, G_SIGALRM)) { + F_CLR(sp->gp, G_SIGALRM); + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp->ep != NULL && + F_ISSET(tsp->ep, F_RCV_ON)) + F_SET(tsp->ep, F_RCV_ALRM); + } + if (F_ISSET(sp->ep, F_RCV_ALRM)) { + F_CLR(sp->ep, F_RCV_ALRM); + (void)rcv_sync(sp, sp->ep); + } + if (F_ISSET(sp->gp, G_SIGHUP)) { + F_CLR(sp->gp, G_SIGHUP); + rcv_hup(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGTERM)) { + F_CLR(sp->gp, G_SIGTERM); + rcv_term(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGWINCH)) { + F_CLR(sp->gp, G_SIGWINCH); + set_window_size(sp, 0, 1); + F_SET(sp, S_RESIZE); + (void)sp->s_refresh(sp, sp->ep); + } + } + + /* + * There are three cases here: + * + * 1: A read from a file or a pipe. In this case, the reads + * never timeout regardless. This means that we can hang + * when trying to complete a map, but we're going to hang + * on the next read anyway. + */ + if (!F_ISSET(sp->gp, G_ISFROMTTY)) { + if ((nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) > 0) { + tty->cnt += *nrp = nr; + return (INP_OK); + } + return (INP_EOF); + } + + /* + * 2: A read with an associated timeout. In this case, we are trying + * to complete a map sequence. Ignore script windows and timeout + * as specified. If input arrives, we fall into #3, but because + * timeout isn't NULL, don't read anything but command input. + * + * If interrupted, go back and check to see what it was. + */ + if (timeout != NULL) { + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + FD_SET(STDIN_FILENO, &sp->rdfd); + for (;;) { + switch (select(STDIN_FILENO + 1, + &sp->rdfd, NULL, NULL, timeout)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + goto err; + case 1: /* Characters ready. */ + break; + case 0: /* Timeout. */ + return (INP_OK); + } + break; + } + } + + /* + * 3: At this point, we'll take anything that comes. Select on the + * command file descriptor and the file descriptor for the script + * window if there is one. Poll the fd's, increasing the timeout + * each time each time we don't get anything until we're blocked + * on I/O. + * + * If interrupted, go back and check to see what it was. + */ + for (t.tv_sec = t.tv_usec = 0;;) { + /* + * Reset each time -- sscr_input() may call other + * routines which could reset bits. + */ + if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) { + tp = &t; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) { + FD_SET(sp->script->sh_master, &sp->rdfd); + maxfd = + MAX(STDIN_FILENO, sp->script->sh_master); + } else + maxfd = STDIN_FILENO; + } else { + tp = NULL; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + maxfd = STDIN_FILENO; + } + + switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; +err: msgq(sp, M_SYSERR, "select"); + return (INP_ERR); + case 0: /* Timeout. */ + if (t.tv_usec) { + ++t.tv_sec; + t.tv_usec = 0; + } else + t.tv_usec += 500000; + continue; + } + + if (timeout == NULL && F_ISSET(sp, S_SCRIPT) && + FD_ISSET(sp->script->sh_master, &sp->rdfd)) { + sscr_input(sp); + continue; + } + + switch (nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) { + case 0: /* EOF. */ + return (INP_EOF); + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + msgq(sp, M_SYSERR, "read"); + return (INP_ERR); + default: + tty->cnt += *nrp = nr; + return (INP_OK); + } + /* NOTREACHED */ + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c new file mode 100644 index 0000000000..25eb17f049 --- /dev/null +++ b/usr.bin/vi/sex/sex_util.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_util.c 8.9 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_bell -- + * Ring the bell. + */ +void +sex_bell(sp) + SCR *sp; +{ + (void)write(STDOUT_FILENO, "\07", 1); /* \a */ +} + +void +sex_busy(sp, msg) + SCR *sp; + char const *msg; +{ + (void)fprintf(stdout, "%s\n", msg); + (void)fflush(stdout); +} + +/* + * sex_suspend -- + * Suspend an ex screen. + */ +int +sex_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* Save ex/vi terminal settings, and restore the original ones. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSADRAIN, &sp->gp->original_termios); + } + + /* Kill the process group. */ + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(0, SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + /* Restore ex/vi terminal settings. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &t); + + return (rval); +} diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c new file mode 100644 index 0000000000..01546d7d77 --- /dev/null +++ b/usr.bin/vi/svi/svi_confirm.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include + +#include + +#include "vi.h" +#include "svi_screen.h" + +enum confirm +svi_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + size_t oldy, oldx; + + /* + * Refresh the cursor first -- this means that we won't have to + * set S_UPDATE_MODE to keep refresh from erasing the mode line + * or SVI_CUR_INVALID because we sneaked the cursor off somewhere + * else. + */ + sp->lno = fp->lno; + sp->cno = fp->cno; + if (svi_paint(sp, ep)) + return (CONF_QUIT); + + getyx(stdscr, oldy, oldx); + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + ADDNSTR(CONFSTRING, sizeof(CONFSTRING) - 1); + MOVEA(sp, oldy, oldx); + refresh(); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c new file mode 100644 index 0000000000..7a83d409b3 --- /dev/null +++ b/usr.bin/vi/svi/svi_ex.c @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_ex.c 8.36 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "../sex/sex_screen.h" + +static int svi_ex_done __P((SCR *, EXF *, MARK *)); +static int svi_ex_scroll __P((SCR *, int, int, CH *)); + +/* + * svi_ex_cmd -- + * Execute an ex command. + */ +int +svi_ex_cmd(sp, ep, exp, rp) + SCR *sp; + EXF *ep; + EXCMDARG *exp; + MARK *rp; +{ + SVI_PRIVATE *svp; + int rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + (void)svi_busy(sp, NULL); + rval = exp->cmd->fn(sp, ep, exp); + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + (void)msg_rpt(sp, 0); + (void)ex_fflush(EXCOOKIE); + + /* + * If displayed anything, figure out if we have to wait. If the + * screen wasn't trashed, only one line and there are no waiting + * messages, don't wait, but don't overwrite it with mode information + * either. If there's a screen under this one, change the line to + * inverse video. + */ + if (svp->extotalcount > 0) + if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY))) + F_SET(sp, S_UPDATE_MODE); + else + (void)svi_ex_scroll(sp, 1, 0, NULL); + return (svi_ex_done(sp, ep, rp) || rval); +} + +/* + * svi_ex_run -- + * Execute strings of ex commands. + */ +int +svi_ex_run(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + enum input (*get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + struct termios rawt, t; + CH ikey; + SVI_PRIVATE *svp; + TEXT *tp; + int flags, in_exmode, rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + /* + * There's some tricky stuff going on here to handle when a user has + * mapped a key to multiple ex commands. Historic practice was that + * vi ran without any special actions, as if the user were entering + * the characters, until ex trashed the screen, e.g. something like a + * '!' command. At that point, we no longer know what the screen + * looks like, so we can't afford to overwrite anything. The solution + * is to go into real ex mode until we get to the end of the command + * strings. + */ + get = svi_get; + flags = TXT_BS | TXT_PROMPT; + for (in_exmode = rval = 0;;) { + if (get(sp, ep, &sp->tiq, ':', flags) != INP_OK) { + rval = 1; + break; + } + + /* + * Len is 0 if the user backspaced over the prompt, + * 1 if only a CR was entered. + */ + tp = sp->tiq.cqh_first; + if (tp->len == 0) + break; + + if (!in_exmode) + (void)svi_busy(sp, NULL); + + (void)ex_icmd(sp, ep, tp->lb, tp->len); + (void)ex_fflush(EXCOOKIE); + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + break; + + /* + * If continue not required, and one or no lines, and there + * are no waiting messages, don't wait, but don't overwrite + * it with mode information either. If there's a screen under + * this one, change the line to inverse video. + */ + if (!F_ISSET(sp, S_CONTINUE) && + (svp->extotalcount == 0 || svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY)))) { + if (svp->extotalcount == 1) + F_SET(sp, S_UPDATE_MODE); + break; + } + + /* If the screen is trashed, go into ex mode. */ + if (!in_exmode && F_ISSET(sp, S_REFRESH)) { + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + SEX_RAW(t, rawt); + get = sex_get; + } else + get = sex_get_notty; + flags = TXT_CR | TXT_NLECHO | TXT_PROMPT; + in_exmode = 1; + } + + /* + * If the user hasn't already indicated that they're done, + * they may continue in ex mode by entering a ':'. + */ + if (F_ISSET(sp, S_INTERRUPTED)) + break; + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + if (in_exmode) { + (void)write(STDOUT_FILENO, + CONTMSG, sizeof(CONTMSG) - 1); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) { + rval = 1; + goto ret; + } + if (ikey.ch == ' ' || ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + sex_bell(sp); + } + } else + (void)svi_ex_scroll(sp, 1, 1, &ikey); + if (ikey.ch != ':') + break; + + if (in_exmode) + (void)write(STDOUT_FILENO, "\r\n", 2); + else { + ++svp->extotalcount; + ++svp->exlinecount; + } + } + +ret: if (in_exmode) { + /* Reset the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + F_SET(sp, S_REFRESH); + } else + if (svi_ex_done(sp, ep, rp)) + rval = 1; + F_CLR(sp, S_CONTINUE); + return (rval); +} + +/* + * svi_ex_done -- + * Cleanup from dipping into ex. + */ +static int +svi_ex_done(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + SMAP *smp; + SVI_PRIVATE *svp; + recno_t lno; + size_t cnt, len; + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + return (0); + + /* + * Otherwise, the only cursor modifications will be real, however, the + * underlying line may have changed; don't trust anything. This code + * has been a remarkably fertile place for bugs. + * + * Repaint the entire screen if at least half the screen is trashed. + * Else, repaint only over the overwritten lines. The "-2" comes + * from one for the mode line and one for the fact that it's an offset. + * Note the check for small screens. + * + * Don't trust ANYTHING. + */ + svp = SVP(sp); + if (svp->extotalcount >= HALFTEXT(sp)) + F_SET(sp, S_REDRAW); + else + for (cnt = sp->rows - 2; svp->extotalcount--; --cnt) + if (cnt > sp->t_rows) { + MOVE(sp, cnt, 0); + clrtoeol(); + } else { + smp = HMAP + cnt; + SMAP_FLUSH(smp); + if (svi_line(sp, ep, smp, NULL, NULL)) + return (1); + } + /* + * Do a reality check on a cursor value, and make sure it's okay. + * If necessary, change it. Ex keeps track of the line number, + * but ex doesn't care about the column and it may have disappeared. + */ + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + GETLINE_ERR(sp, sp->lno); + sp->lno = 1; + sp->cno = 0; + } else if (sp->cno >= len) + sp->cno = len ? len - 1 : 0; + + rp->lno = sp->lno; + rp->cno = sp->cno; + return (0); +} + +/* + * svi_ex_write -- + * Write out the ex messages. + * + * There is no tab or character translation going on, so, whatever the ex + * and/or curses routines do with special characters is all that gets done. + * This is probably okay, I don't see any reason that user's tab settings + * should affect ex output, and ex should have displayed everything else + * exactly as it wanted it on the screen. + */ +int +svi_ex_write(cookie, line, llen) + void *cookie; + const char *line; + int llen; +{ + SCR *sp; + SVI_PRIVATE *svp; + size_t oldy, oldx; + int len, rlen; + const char *p; + + /* + * XXX + * If it's a 4.4BSD system, we could just use fpurge(3). + * This shouldn't be too expensive, though. + */ + sp = cookie; + svp = SVP(sp); + if (F_ISSET(sp, S_INTERRUPTED)) + return (llen); + + p = line; /* In case of a write of 0. */ + for (rlen = llen; llen;) { + /* Get the next line. */ + if ((p = memchr(line, '\n', llen)) == NULL) + len = llen; + else + len = p - line; + + /* + * The max is sp->cols characters, and we may + * have already written part of the line. + */ + if (len + svp->exlcontinue > sp->cols) + len = sp->cols - svp->exlcontinue; + + /* + * If the first line output, do nothing. + * If the second line output, draw the divider line. + * If drew a full screen, remove the divider line. + * If it's a continuation line, move to the continuation + * point, else, move the screen up. + */ + if (svp->exlcontinue == 0) { + if (svp->extotalcount == 1) { + MOVE(sp, INFOLINE(sp) - 1, 0); + clrtoeol(); + if (svi_divider(sp)) + return (-1); + F_SET(svp, SVI_DIVIDER); + ++svp->extotalcount; + ++svp->exlinecount; + } + if (svp->extotalcount == sp->t_maxrows && + F_ISSET(svp, SVI_DIVIDER)) { + --svp->extotalcount; + --svp->exlinecount; + F_CLR(svp, SVI_DIVIDER); + } + if (svp->extotalcount != 0 && + svi_ex_scroll(sp, 0, 0, NULL)) + return (-1); + MOVE(sp, INFOLINE(sp), 0); + ++svp->extotalcount; + ++svp->exlinecount; + if (F_ISSET(sp, S_INTERRUPTIBLE) && + F_ISSET(sp, S_INTERRUPTED)) + break; + } else + MOVE(sp, INFOLINE(sp), svp->exlcontinue); + + /* Display the line. */ + if (len) + ADDNSTR(line, len); + + /* Clear to EOL. */ + getyx(stdscr, oldy, oldx); + if (oldx < sp->cols) + clrtoeol(); + + /* If we loop, it's a new line. */ + svp->exlcontinue = 0; + + /* Reset for the next line. */ + line += len; + llen -= len; + if (p != NULL) { + ++line; + --llen; + } + } + /* Refresh the screen, even if it's a partial. */ + refresh(); + + /* Set up next continuation line. */ + if (p == NULL) + getyx(stdscr, oldy, svp->exlcontinue); + return (rlen); +} + +/* + * svi_ex_scroll -- + * Scroll the screen for ex output. + */ +static int +svi_ex_scroll(sp, mustwait, colon_ok, chp) + SCR *sp; + int mustwait, colon_ok; + CH *chp; +{ + CH ikey; + SVI_PRIVATE *svp; + + /* + * Scroll the screen. Instead of scrolling the entire screen, delete + * the line above the first line output so preserve the maximum amount + * of the screen. + */ + svp = SVP(sp); + if (svp->extotalcount >= sp->rows) { + MOVE(sp, 0, 0); + } else + MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0); + + deleteln(); + + /* If there are screens below us, push them back into place. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) { + MOVE(sp, INFOLINE(sp), 0); + insertln(); + } + + /* If just displayed a full screen, wait. */ + if (mustwait || svp->exlinecount == sp->t_maxrows) { + MOVE(sp, INFOLINE(sp), 0); + if (F_ISSET(sp, S_INTERRUPTIBLE)) { + ADDNSTR(CONTMSG_I, (int)sizeof(CONTMSG_I) - 1); + } else { + ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1); + } + clrtoeol(); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + return (-1); + if (ikey.ch == ' ') + break; + if (colon_ok && ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + if (ikey.ch == QUIT_CH && + F_ISSET(sp, S_INTERRUPTIBLE)) { + F_SET(sp, S_INTERRUPTED); + break; + } + svi_bell(sp); + } + if (chp != NULL) + *chp = ikey; + svp->exlinecount = 0; + } + return (0); +} diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c new file mode 100644 index 0000000000..46f5f0e240 --- /dev/null +++ b/usr.bin/vi/svi/svi_get.c @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_get.c 8.19 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +/* + * svi_get -- + * Fill a buffer from the terminal for vi. + */ +enum input +svi_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + MARK save; + SMAP *esmp; + recno_t bot_lno, top_lno; + size_t bot_off, cnt, top_off; + int eval; + + /* + * The approach used is to fake like the user is doing input on + * the last line of the screen. This makes all of the scrolling + * work correctly, and allows us the use of the vi text editing + * routines, not to mention practically infinite length ex commands. + * + * Save the current location. + */ + bot_lno = TMAP->lno; + bot_off = TMAP->off; + top_lno = HMAP->lno; + top_off = HMAP->off; + save.lno = sp->lno; + save.cno = sp->cno; + + /* + * If it's a small screen, TMAP may be small for the screen. + * Fix it, filling in fake lines as we go. + */ + if (ISSMALLSCREEN(sp)) + for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + } + + /* Build the fake entry. */ + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + SMAP_FLUSH(&TMAP[1]); + ++TMAP; + + /* Move to it. */ + sp->lno = TMAP[0].lno; + sp->cno = 0; + + if (O_ISSET(sp, O_ALTWERASE)) + LF_SET(TXT_ALTWERASE); + if (O_ISSET(sp, O_TTYWERASE)) + LF_SET(TXT_TTYWERASE); + LF_SET(TXT_APPENDEOL | + TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); + + /* Don't update the modeline for now. */ + F_SET(SVP(sp), SVI_INFOLINE); + + eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags); + + F_CLR(SVP(sp), SVI_INFOLINE); + + /* Put it all back. */ + --TMAP; + sp->lno = save.lno; + sp->cno = save.cno; + + /* + * If it's a small screen, TMAP may be wrong. Clear any + * lines that might have been overwritten. + */ + if (ISSMALLSCREEN(sp)) { + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } + + /* + * The map may be wrong if the user entered more than one + * (logical) line. Fix it. If the user entered a whole + * screen, this will be slow, but it's not worth caring. + */ + while (bot_lno != TMAP->lno || bot_off != TMAP->off) + if (svi_sm_1down(sp, ep)) + return (INP_ERR); + + /* + * Invalidate the cursor, the line never really existed. This fixes + * a bug where the user searches for the last line on the screen + 1 + * and the refresh routine thinks that's where we just were. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + return (eval ? INP_ERR : INP_OK); +} diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c new file mode 100644 index 0000000000..80d736d323 --- /dev/null +++ b/usr.bin/vi/svi/svi_line.c @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_line.c 8.16 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include + +#include +#include + +#include "vi.h" +#include "svi_screen.h" + +#if defined(DEBUG) && 0 +#define TABCH '-' +#define TABSTR "--------------------" +#else +#define TABSTR " " +#define TABCH ' ' +#endif + +/* + * svi_line -- + * Update one line on the screen. + */ +int +svi_line(sp, ep, smp, yp, xp) + SCR *sp; + EXF *ep; + SMAP *smp; + size_t *xp, *yp; +{ + CHNAME const *cname; + SMAP *tsmp; + size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens; + size_t offset_in_char, offset_in_line; + size_t oldy, oldx; + int ch, is_cached, is_infoline, is_partial, is_tab, listset; + char *p, nbuf[10]; + +#if defined(DEBUG) && 0 + TRACE(sp, "svi_line: row %u: line: %u off: %u\n", + smp - HMAP, smp->lno, smp->off); +#endif + + /* + * Assume that, if the cache entry for the line is filled in, the + * line is already on the screen, and all we need to do is return + * the cursor position. If the calling routine doesn't need the + * cursor position, we can just return. + */ + is_cached = SMAP_CACHE(smp); + if (yp == NULL && is_cached) + return (0); + + /* + * A nasty side effect of this routine is that it returns the screen + * position for the "current" character. Not pretty, but this is the + * only routine that really knows what's out there. + * + * Move to the line. This routine can be called by svi_sm_position(), + * which uses it to fill in the cache entry so it can figure out what + * the real contents of the screen are. Because of this, we have to + * return to whereever we started from. + */ + getyx(stdscr, oldy, oldx); + MOVE(sp, smp - HMAP, 0); + + /* Get the character map. */ + cname = sp->gp->cname; + + /* Get a copy of the line. */ + p = file_gline(sp, ep, smp->lno, &len); + + /* + * Special case if we're printing the info/mode line. Skip printing + * the leading number, as well as other minor setup. If painting the + * line between two screens, it's always in reverse video. The only + * time this code paints the mode line is when the user is entering + * text for a ":" command, so we can put the code here instead of + * dealing with the empty line logic below. This is a kludge, but it's + * pretty much confined to this module. + * + * Set the number of screens to skip until a character is displayed. + * Left-right screens are special, because we don't bother building + * a buffer to be skipped over. + * + * Set the number of columns for this screen. + */ + cols_per_screen = sp->cols; + if (is_infoline = ISINFOLINE(sp, smp)) { + listset = 0; + if (O_ISSET(sp, O_LEFTRIGHT)) + skip_screens = 0; + else + skip_screens = smp->off - 1; + } else { + listset = O_ISSET(sp, O_LIST); + skip_screens = smp->off - 1; + + /* + * If O_NUMBER is set and it's line number 1 or the line exists + * and this is the first screen of a folding line or any left- + * right line, display the line number. + */ + if (O_ISSET(sp, O_NUMBER)) { + cols_per_screen -= O_NUMBER_LENGTH; + if ((smp->lno == 1 || p != NULL) && skip_screens == 0) { + (void)snprintf(nbuf, + sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + } + } + + /* + * Special case non-existent lines and the first line of an empty + * file. In both cases, the cursor position is 0, but corrected + * for the O_NUMBER field if it was displayed. + */ + if (p == NULL || len == 0) { + /* Fill in the cursor. */ + if (yp != NULL && smp->lno == sp->lno) { + *yp = smp - HMAP; + *xp = sp->cols - cols_per_screen; + } + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + + /* Set line cacheing information. */ + smp->c_sboff = smp->c_eboff = 0; + smp->c_scoff = smp->c_eclen = 0; + + if (p == NULL) { + if (smp->lno != 1) + ADDCH(listset && skip_screens == 0 ? '$' : '~'); + } else if (listset && skip_screens == 0) + ADDCH('$'); + + clrtoeol(); + MOVEA(sp, oldy, oldx); + return (0); + } + + /* + * If we wrote a line that's this or a previous one, we can do this + * much more quickly -- we cached the starting and ending positions + * of that line. The way it works is we keep information about the + * lines displayed in the SMAP. If we're painting the screen in + * the forward, this saves us from reformatting the physical line for + * every line on the screen. This wins big on binary files with 10K + * lines. + * + * Test for the first screen of the line, then the current screen line, + * then the line behind us, then do the hard work. Note, it doesn't + * do us any good to have a line in front of us -- it would be really + * hard to try and figure out tabs in the reverse direction, i.e. how + * many spaces a tab takes up in the reverse direction depends on + * what characters preceded it. + */ + if (smp->off == 1) { + smp->c_sboff = offset_in_line = 0; + smp->c_scoff = offset_in_char = 0; + p = &p[offset_in_line]; + } else if (is_cached) { + offset_in_line = smp->c_sboff; + offset_in_char = smp->c_scoff; + p = &p[offset_in_line]; + } else if (smp != HMAP && + SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) { + if (tsmp->c_eclen != tsmp->c_ecsize) { + offset_in_line = tsmp->c_eboff; + offset_in_char = tsmp->c_eclen; + } else { + offset_in_line = tsmp->c_eboff + 1; + offset_in_char = 0; + } + + /* Put starting info for this line in the cache. */ + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + p = &p[offset_in_line]; + } else { + offset_in_line = 0; + offset_in_char = 0; + + /* This is the loop that skips through screens. */ + if (skip_screens == 0) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + } else for (scno = 0; offset_in_line < len; ++offset_in_line) { + scno += chlen = + (ch = *(u_char *)p++) == '\t' && !listset ? + TAB_OFF(sp, scno) : cname[ch].len; + if (scno < cols_per_screen) + continue; + /* + * Reset cols_per_screen to second and subsequent line + * length. + */ + scno -= cols_per_screen; + cols_per_screen = sp->cols; + + /* + * If crossed the last skipped screen boundary, start + * displaying the characters. + */ + if (--skip_screens) + continue; + + /* Put starting info for this line in the cache. */ + if (scno) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char = chlen - scno; + --p; + } else { + smp->c_sboff = ++offset_in_line; + smp->c_scoff = 0; + } + break; + } + } + + /* + * Set the number of characters to skip before reaching the cursor + * character. Offset by 1 and use 0 as a flag value. Svi_line is + * called repeatedly with a valid pointer to a cursor position. + * Don't fill anything in unless it's the right line and the right + * character, and the right part of the character... + */ + if (yp == NULL || + smp->lno != sp->lno || sp->cno < offset_in_line || + offset_in_line + cols_per_screen < sp->cno) { + cno_cnt = 0; + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } else + cno_cnt = (sp->cno - offset_in_line) + 1; + + /* This is the loop that actually displays characters. */ + for (is_partial = 0, scno = 0; + offset_in_line < len; ++offset_in_line, offset_in_char = 0) { + if ((ch = *(u_char *)p++) == '\t' && !listset) { + scno += chlen = TAB_OFF(sp, scno) - offset_in_char; + is_tab = 1; + } else { + scno += chlen = cname[ch].len - offset_in_char; + is_tab = 0; + } + + /* + * Only display up to the right-hand column. Set a flag if + * the entire character wasn't displayed for use in setting + * the cursor. If reached the end of the line, set the cache + * info for the screen. Don't worry about there not being + * characters to display on the next screen, its lno/off won't + * match up in that case. + */ + if (scno >= cols_per_screen) { + smp->c_ecsize = chlen; + chlen -= scno - cols_per_screen; + smp->c_eclen = chlen; + smp->c_eboff = offset_in_line; + if (scno > cols_per_screen) + is_partial = 1; + + /* Terminate the loop. */ + offset_in_line = len; + } + + /* + * If the caller wants the cursor value, and this was the + * cursor character, set the value. There are two ways to + * put the cursor on a character -- if it's normal display + * mode, it goes on the last column of the character. If + * it's input mode, it goes on the first. In normal mode, + * set the cursor only if the entire character was displayed. + */ + if (cno_cnt && + --cno_cnt == 0 && (F_ISSET(sp, S_INPUT) || !is_partial)) { + *yp = smp - HMAP; + if (F_ISSET(sp, S_INPUT)) + *xp = scno - chlen; + else + *xp = scno - 1; + if (O_ISSET(sp, O_NUMBER) && + !is_infoline && smp->off == 1) + *xp += O_NUMBER_LENGTH; + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } + + /* If the line is on the screen, don't display anything. */ + if (is_cached) + continue; + + /* + * Display the character. If it's a tab and tabs aren't some + * ridiculous length, do it fast. (We do tab expansion here + * because curses doesn't have a way to set the tab length.) + */ + if (is_tab) { + if (chlen <= sizeof(TABSTR) - 1) { + ADDNSTR(TABSTR, chlen); + } else + while (chlen--) + ADDCH(TABCH); + } else + ADDNSTR(cname[ch].name + offset_in_char, chlen); + } + + if (scno < cols_per_screen) { + /* If didn't paint the whole line, update the cache. */ + smp->c_ecsize = smp->c_eclen = cname[ch].len; + smp->c_eboff = len - 1; + + /* + * If not the info/mode line, and O_LIST set, and at the + * end of the line, and the line ended on this screen, + * add a trailing $. + */ + if (listset) { + ++scno; + ADDCH('$'); + } + + /* If still didn't paint the whole line, clear the rest. */ + if (scno < cols_per_screen) + clrtoeol(); + } + +ret: MOVEA(sp, oldy, oldx); + return (0); +} + +/* + * svi_number -- + * Repaint the numbers on all the lines. + */ +int +svi_number(sp, ep) + SCR *sp; + EXF *ep; +{ + SMAP *smp; + recno_t lno; + size_t oldy, oldx; + char *p, nbuf[10]; + + /* + * Try and avoid getting the last line in the file, by getting the + * line after the last line in the screen -- if it exists, we know + * we have to to number all the lines in the screen. Get the one + * after the last instead of the last, so that the info line doesn't + * fool us. + * + * If that test fails, we have to check each line for existence. + * + * XXX + * The problem is that file_lline will lie, and tell us that the + * info line is the last line in the file. + */ + if ((p = file_gline(sp, ep, TMAP->lno - 1, NULL)) != NULL) + lno = TMAP->lno + 1; + + getyx(stdscr, oldy, oldx); + for (smp = HMAP; smp <= TMAP; ++smp) { + if (smp->off != 1) + continue; + if (ISINFOLINE(sp, smp)) + break; + if (smp->lno != 1) + if (p != NULL) { + if (smp->lno > lno) + break; + } else { + if ((p = + file_gline(sp, ep, smp->lno, NULL)) == NULL) + break; + p = NULL; + } + MOVE(sp, smp - HMAP, 0); + (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + MOVEA(sp, oldy, oldx); + return (0); +} diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c new file mode 100644 index 0000000000..353c08178e --- /dev/null +++ b/usr.bin/vi/svi/svi_refresh.c @@ -0,0 +1,839 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_refresh.c 8.43 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static int svi_modeline __P((SCR *, EXF *)); +static int svi_msgflush __P((SCR *)); + +int +svi_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + u_int paintbits; + + /* + * 1: Resize the screen. + * + * Notice that a resize is requested, and set up everything so that + * the file gets reinitialized. Done here, instead of in the vi loop + * because there may be other initialization that other screens need + * to do. The actual changing of the row/column values was done by + * calling the ex options code which put them into the environment, + * which is used by curses. Stupid, but ugly. + */ + if (F_ISSET(sp, S_RESIZE)) { + /* Reinitialize curses. */ + if (svi_curses_end(sp) || svi_curses_init(sp)) + return (1); + + /* Lose any svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* + * Fill the map, incidentally losing any svi_line() + * cached information. + */ + if (sp->s_fill(sp, ep, sp->lno, P_FILL)) + return (1); + F_CLR(sp, S_RESIZE | S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: S_REFRESH + * + * If S_REFRESH is set in the current screen, repaint everything + * that we can find. + */ + if (F_ISSET(sp, S_REFRESH)) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp) + F_SET(tsp, S_REDRAW); + /* + * 3: Related or dirtied screens, or screens with messages. + * + * If related screens share a view into a file, they may have been + * modified as well. Refresh any screens with paint or dirty bits + * set, or where messages are waiting. Finally, if we refresh any + * screens other than the current one, the cursor will be trashed. + */ + paintbits = S_REDRAW | S_REFORMAT | S_REFRESH; + if (O_ISSET(sp, O_NUMBER)) + paintbits |= S_RENUMBER; + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp && + (F_ISSET(tsp, paintbits) || + F_ISSET(SVP(tsp), SVI_SCREENDIRTY) || + tsp->msgq.lh_first != NULL && + !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) { + (void)svi_paint(tsp, tsp->ep); + F_CLR(SVP(tsp), SVI_SCREENDIRTY); + F_SET(SVP(sp), SVI_CUR_INVALID); + } + + /* + * 4: Refresh the current screen. + * + * Always refresh the current screen, it may be a cursor movement. + * Also, always do it last -- that way, S_REFRESH can be set in + * the current screen only, and the screen won't flash. + */ + F_CLR(sp, SVI_SCREENDIRTY); + return (svi_paint(sp, ep)); +} + +/* + * svi_paint -- + * This is the guts of the vi curses screen code. The idea is that + * the SCR structure passed in contains the new coordinates of the + * screen. What makes this hard is that we don't know how big + * characters are, doing input can put the cursor in illegal places, + * and we're frantically trying to avoid repainting unless it's + * absolutely necessary. If you change this code, you'd better know + * what you're doing. It's subtle and quick to anger. + */ +int +svi_paint(sp, ep) + SCR *sp; + EXF *ep; +{ + CHNAME const *cname; + SMAP *smp, tmp; + SVI_PRIVATE *svp; + recno_t lastline, lcnt; + size_t cwtotal, cnt, len, x, y; + int ch, didpaint; + char *p; + +#define LNO sp->lno +#define OLNO svp->olno +#define CNO sp->cno +#define OCNO svp->ocno +#define SCNO svp->sc_col + + didpaint = 0; + svp = SVP(sp); + + /* + * 1: Reformat the lines. + * + * If the lines themselves have changed (:set list, for example), + * fill in the map from scratch. Adjust the screen that's being + * displayed if the leftright flag is set. + */ + if (F_ISSET(sp, S_REFORMAT)) { + /* Toss svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* Toss svi_line() cached information. */ + if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP)) + return (1); + if (O_ISSET(sp, O_LEFTRIGHT) && + (cnt = svi_screens(sp, ep, LNO, &CNO)) != 1) + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + F_CLR(sp, S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: Line movement. + * + * Line changes can cause the top line to change as well. As + * before, if the movement is large, the screen is repainted. + * + * 2a: Tiny screens. + * + * Tiny screens cannot be permitted into the "scrolling" parts of + * the smap code for two reasons. If the screen size is 1 line, + * HMAP == TMAP and the code will quickly drop core. If the screen + * size is 2, none of the divisions by 2 will work, and scrolling + * won't work. In fact, because no line change will be less than + * HALFTEXT(sp), we always ending up "filling" the map, with a + * P_MIDDLE flag, which isn't what the user wanted. Tiny screens + * can go into the "fill" portions of the smap code, however. + */ + if (sp->t_rows <= 2) { + if (LNO < HMAP->lno) { + if (svi_sm_fill(sp, ep, LNO, P_TOP)) + return (1); + } else if (LNO > TMAP->lno) + if (svi_sm_fill(sp, ep, LNO, P_BOTTOM)) + return (1); + if (sp->t_rows == 1) { + HMAP->off = svi_screens(sp, ep, LNO, &CNO); + goto paint; + } + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * 2b: Small screens. + * + * Users can use the window, w300, w1200 and w9600 options to make + * the screen artificially small. The behavior of these options + * in the historic vi wasn't all that consistent, and, in fact, it + * was never documented how various screen movements affected the + * screen size. Generally, one of three things would happen: + * 1: The screen would expand in size, showing the line + * 2: The screen would scroll, showing the line + * 3: The screen would compress to its smallest size and + * repaint. + * In general, scrolling didn't cause compression (200^D was handled + * the same as ^D), movement to a specific line would (:N where N + * was 1 line below the screen caused a screen compress), and cursor + * movement would scroll if it was 11 lines or less, and compress if + * it was more than 11 lines. (And, no, I have no idea where the 11 + * comes from.) + * + * What we do is try and figure out if the line is less than half of + * a full screen away. If it is, we expand the screen if there's + * room, and then scroll as necessary. The alternative is to compress + * and repaint. + * + * !!! + * This code is a special case from beginning to end. Unfortunately, + * home modems are still slow enough that it's worth having. + * + * XXX + * If the line a really long one, i.e. part of the line is on the + * screen but the column offset is not, we'll end up in the adjust + * code, when we should probably have compressed the screen. + */ + if (ISSMALLSCREEN(sp)) + if (LNO < HMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + } + else + goto small_fill; + } else if (LNO > TMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, TMAP + 1)) + return (1); + ++TMAP; + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + else { +small_fill: MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; sp->t_rows > sp->t_minrows; + --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + if (svi_sm_fill(sp, ep, LNO, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + } + + /* + * 3a: Line down. + */ + if (LNO >= HMAP->lno) { + if (LNO <= TMAP->lno) + goto adjust; + + /* + * If less than half a screen away, scroll down until the + * line is on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1up(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than a full screen from the bottom of the file, put + * the last line of the file on the bottom of the screen. The + * calculation is safe because we know there's at least one + * full screen of lines, otherwise couldn't have gotten here. + */ + if (file_lline(sp, ep, &lastline)) + return (1); + tmp.lno = LNO; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows); + if (lcnt < sp->t_rows) { + if (svi_sm_fill(sp, ep, lastline, P_BOTTOM)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * If more than a full screen from the last line of the file, + * put the new line in the middle of the screen. + */ + goto middle; + } + + /* + * 3b: Line up. + * + * If less than half a screen away, scroll up until the line is + * the first line on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1down(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than half a screen from the top of the file, put the first + * line of the file at the top of the screen. Otherwise, put the line + * in the middle of the screen. + */ + tmp.lno = 1; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + if (svi_sm_fill(sp, ep, 1, P_TOP)) + return (1); + } else +middle: if (svi_sm_fill(sp, ep, LNO, P_MIDDLE)) + return (1); + F_SET(sp, S_REDRAW); + + /* + * At this point we know part of the line is on the screen. Since + * scrolling is done using logical lines, not physical, all of the + * line may not be on the screen. While that's not necessarily bad, + * if the part the cursor is on isn't there, we're going to lose. + * This can be tricky; if the line covers the entire screen, lno + * may be the same as both ends of the map, that's why we test BOTH + * the top and the bottom of the map. This isn't a problem for + * left-right scrolling, the cursor movement code handles the problem. + * + * There's a performance issue here if editing *really* long lines. + * This gets to the right spot by scrolling, and, in a binary, by + * scrolling hundreds of lines. If the adjustment looks like it's + * going to be a serious problem, refill the screen and repaint. + */ +adjust: if (!O_ISSET(sp, O_LEFTRIGHT) && + (LNO == HMAP->lno || LNO == TMAP->lno)) { + cnt = svi_screens(sp, ep, LNO, &CNO); + if (LNO == HMAP->lno && cnt < HMAP->off) + if ((HMAP->off - cnt) > HALFTEXT(sp)) { + HMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_TOP); + F_SET(sp, S_REDRAW); + } else + while (cnt < HMAP->off) + if (svi_sm_1down(sp, ep)) + return (1); + if (LNO == TMAP->lno && cnt > TMAP->off) + if ((cnt - TMAP->off) > HALFTEXT(sp)) { + TMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM); + F_SET(sp, S_REDRAW); + } else + while (cnt > TMAP->off) + if (svi_sm_1up(sp, ep)) + return (1); + } + + /* If the screen needs to be repainted, skip cursor optimization. */ + if (F_ISSET(sp, S_REDRAW)) + goto paint; + + /* + * 4: Cursor movements. + * + * Decide cursor position. If the line has changed, the cursor has + * moved over a tab, or don't know where the cursor was, reparse the + * line. Note, if we think that the cursor "hasn't moved", reparse + * the line. This is 'cause if it hasn't moved, we've almost always + * lost track of it. + * + * Otherwise, we've just moved over fixed-width characters, and can + * calculate the left/right scrolling and cursor movement without + * reparsing the line. Note that we don't know which (if any) of + * the characters between the old and new cursor positions changed. + * + * XXX + * With some work, it should be possible to handle tabs quickly, at + * least in obvious situations, like moving right and encountering + * a tab, without reparsing the whole line. + */ + + /* If the line we're working with has changed, reparse. */ + if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) { + F_CLR(SVP(sp), SVI_CUR_INVALID); + goto slow; + } + + /* Otherwise, if nothing's changed, go fast. */ + if (CNO == OCNO) + goto fast; + + /* + * Get the current line. If this fails, we either have an empty + * file and can just repaint, or there's a real problem. This + * isn't a performance issue because there aren't any ways to get + * here repeatedly. + */ + if ((p = file_gline(sp, ep, LNO, &len)) == NULL) { + if (file_lline(sp, ep, &lastline)) + return (1); + if (lastline == 0) + goto slow; + GETLINE_ERR(sp, LNO); + return (1); + } + +#ifdef DEBUG + /* This is just a test. */ + if (CNO >= len && len != 0) { + msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)", + tail(__FILE__), __LINE__, CNO, len); + return (1); + } +#endif + /* + * The basic scheme here is to look at the characters in between + * the old and new positions and decide how big they are on the + * screen, and therefore, how many screen positions to move. + */ + cname = sp->gp->cname; + if (CNO < OCNO) { + /* + * 4a: Cursor moved left. + * + * Point to the old character. The old cursor position can + * be past EOL if, for example, we just deleted the rest of + * the line. In this case, since we don't know the width of + * the characters we traversed, we have to do it slowly. + */ + p += OCNO; + cnt = (OCNO - CNO) + 1; + if (OCNO >= len) + goto slow; + + /* + * Quit sanity check -- it's hard to figure out exactly when + * we cross a screen boundary as we do in the cursor right + * movement. If cnt is so large that we're going to cross the + * boundary no matter what, stop now. + */ + if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt) + goto lscreen; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. + */ + for (cwtotal = 0; cnt--; cwtotal += cname[ch].len) + if ((ch = *(u_char *)p--) == '\t') + goto slow; + + /* + * Decrement the screen cursor by the total width of the + * characters minus 1. + */ + cwtotal -= 1; + + /* + * If we're moving left, and there's a wide character in the + * current position, go to the end of the character. + */ + if (cname[ch].len > 1) + cwtotal -= cname[ch].len - 1; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO < cwtotal) { +lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) { + for (smp = HMAP; smp <= TMAP; ++smp) + --smp->off; + goto paint; + } + goto slow; + } + SCNO -= cwtotal; + } else { + /* + * 4b: Cursor moved right. + * + * Point to the first character to the right. + */ + p += OCNO + 1; + cnt = CNO - OCNO; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. If we cross a + * screen boundary, we can quit. + */ + for (cwtotal = SCNO; cnt--;) { + if ((ch = *(u_char *)p++) == '\t') + goto slow; + if ((cwtotal += cname[ch].len) >= SCREEN_COLS(sp)) + break; + } + + /* + * Increment the screen cursor by the total width of the + * characters. + */ + SCNO = cwtotal; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO >= SCREEN_COLS(sp)) { + if (O_ISSET(sp, O_LEFTRIGHT)) { + SCNO -= SCREEN_COLS(sp); + for (smp = HMAP; smp <= TMAP; ++smp) + ++smp->off; + goto paint; + } + goto slow; + } + } + + /* + * 4c: Fast cursor update. + * + * Retrieve the current cursor position, and correct it + * for split screens. + */ +fast: getyx(stdscr, y, x); + y -= sp->woff; + goto number; + + /* + * 4d: Slow cursor update. + * + * Walk through the map and find the current line. If doing left-right + * scrolling and the cursor movement has changed the screen displayed, + * scroll the screen left or right, unless we're updating the info line + * in which case we just scroll that one line. Then update the screen + * lines for this file line until we have a new screen cursor position. + */ +slow: for (smp = HMAP; smp->lno != LNO; ++smp); + if (O_ISSET(sp, O_LEFTRIGHT)) { + cnt = svi_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp); + if (cnt != HMAP->off) { + if (ISINFOLINE(sp, smp)) + smp->off = cnt; + else + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + goto paint; + } + } + for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) { + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + if (y != -1) + break; + } + goto number; + + /* + * 5: Repaint the entire screen. + * + * Lost big, do what you have to do. We flush the cache as S_REDRAW + * gets set when the screen isn't worth fixing, and it's simpler to + * repaint. So, don't trust anything that we think we know about it. + */ +paint: for (smp = HMAP; smp <= TMAP; ++smp) + SMAP_FLUSH(smp); + for (smp = HMAP; smp <= TMAP; ++smp) + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + /* + * If it's a small screen and we're redrawing, clear the unused lines, + * ex may have overwritten them. + */ + if (F_ISSET(sp, S_REDRAW)) { + if (ISSMALLSCREEN(sp)) + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + F_CLR(sp, S_REDRAW); + } + + didpaint = 1; + + /* + * 6: Repaint the line numbers. + * + * If O_NUMBER is set and the S_RENUMBER bit is set, and we didn't + * repaint the screen, repaint all of the line numbers, they've + * changed. + */ +number: if (O_ISSET(sp, O_NUMBER) && F_ISSET(sp, S_RENUMBER) && !didpaint) { + if (svi_number(sp, ep)) + return (1); + F_CLR(sp, S_RENUMBER); + } + + /* + * 7: Refresh the screen. + * + * If the screen was corrupted, refresh it. + */ + if (F_ISSET(sp, S_REFRESH)) { + wrefresh(curscr); + F_CLR(sp, S_REFRESH); + } + + if (F_ISSET(sp, S_BELLSCHED)) + svi_bell(sp); + /* + * If the bottom line isn't in use by the colon command: + * + * Display any messages. Don't test S_UPDATE_MODE. The + * message printing routine set it to avoid anyone else + * destroying the message we're about to display. + * + * If the bottom line isn't in use by anyone, put out the + * standard status line. + */ + if (!F_ISSET(SVP(sp), SVI_INFOLINE)) + if (sp->msgq.lh_first != NULL && + !F_ISSET(sp->msgq.lh_first, M_EMPTY)) + svi_msgflush(sp); + else if (!F_ISSET(sp, S_UPDATE_MODE)) + svi_modeline(sp, ep); + + /* Update saved information. */ + OCNO = CNO; + OLNO = LNO; + + /* Place the cursor. */ + MOVE(sp, y, SCNO); + + /* Flush it all out. */ + refresh(); + + return (0); +} + +/* + * svi_msgflush -- + * Flush any accumulated messages. + */ +static int +svi_msgflush(sp) + SCR *sp; +{ + CH ikey; + CHAR_T ch; + CHNAME const *cname; + MSG *mp; + size_t chlen, len; + char *p; + +#define MCONTMSG " [More ...]" + + /* Display the messages. */ + cname = sp->gp->cname; + for (mp = sp->msgq.lh_first, p = NULL; + mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) { + p = mp->mbuf; + +lcont: /* Move to the message line and clear it. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* + * Turn on standout mode if requested, or, if we've split + * the screen and need a divider. + */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standout(); + + /* + * Print up to the "more" message. Avoid the last character + * in the last line, some hardware doesn't like it. + */ + if (svi_ncols(sp, p, mp->len, NULL) < sp->cols - 1) + len = sp->cols - 1; + else + len = (sp->cols - sizeof(MCONTMSG)) - 1; + for (;; ++p) { + if (!mp->len) + break; + ch = *(u_char *)p; + chlen = cname[ch].len; + if (chlen >= len) + break; + len -= chlen; + --mp->len; + ADDNSTR(cname[ch].name, chlen); + } + + /* + * If more, print continue message. If user key fails, + * keep showing the messages anyway. + */ + if (mp->len || (mp->q.le_next != NULL && + !F_ISSET(mp->q.le_next, M_EMPTY))) { + ADDNSTR(MCONTMSG, sizeof(MCONTMSG) - 1); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + break; + if (ikey.value == K_CR || + ikey.value == K_NL || ikey.ch == ' ') + break; + svi_bell(sp); + } + } + + /* Turn off standout mode. */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standend(); + + if (mp->len) + goto lcont; + + refresh(); + F_SET(mp, M_EMPTY); + } + return (0); +} + +#define RULERSIZE 15 +#define MODESIZE (RULERSIZE + 15) + +/* + * svi_modeline -- + * Update the mode line. + */ +static int +svi_modeline(sp, ep) + SCR *sp; + EXF *ep; +{ + char *s, buf[RULERSIZE]; + + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* Display a dividing line if not the bottom screen. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) + svi_divider(sp); + + /* Display the ruler. */ + if (O_ISSET(sp, O_RULER) && sp->cols > RULERSIZE + 2) { + MOVE(sp, INFOLINE(sp), sp->cols / 2 - RULERSIZE / 2); + clrtoeol(); + (void)snprintf(buf, + sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1); + ADDSTR(buf); + } + + /* Show the modified bit. */ + if (O_ISSET(sp, O_SHOWDIRTY) && + F_ISSET(ep, F_MODIFIED) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 9); + ADDSTR("*"); + } + + /* + * Show the mode. Leave the last character blank, in case it's a + * really dumb terminal with hardware scroll. Second, don't try + * to *paint* the last character, SunOS 4.1.1 and Ultrix 4.2 curses + * won't let you paint the last character in the screen. + */ + if (O_ISSET(sp, O_SHOWMODE) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 8); + s = F_ISSET(sp, S_INPUT) ? " Input" : "Command"; + ADDSTR(s); + } + + return (0); +} + +/* + * svi_divider -- + * Draw a dividing line between the screens. + */ +int +svi_divider(sp) + SCR *sp; +{ + size_t len; + +#define DIVIDESTR "+=+=+=+=+=+=+=+" + len = sizeof(DIVIDESTR) - 1 > sp->cols ? + sp->cols : sizeof(DIVIDESTR) - 1; + ADDNSTR(DIVIDESTR, len); + return (0); +} diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c new file mode 100644 index 0000000000..7daa972c85 --- /dev/null +++ b/usr.bin/vi/svi/svi_relative.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_relative.c 8.7 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include + +#include + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_column -- + * Return the logical column of the cursor. + */ +int +svi_column(sp, ep, cp) + SCR *sp; + EXF *ep; + size_t *cp; +{ + size_t col; + + col = SVP(sp)->sc_col; + if (O_ISSET(sp, O_NUMBER)) + col -= O_NUMBER_LENGTH; + *cp = col; + return (0); +} + +/* + * svi_relative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. If it's not easy, uses the underlying routine that + * really figures it out. It's broken into two parts because the + * svi_lrelative routine handles "logical" offsets, which nobody + * but the screen routines understand. + */ +size_t +svi_relative(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + size_t cno; + + /* First non-blank character. */ + if (sp->rcmflags == RCM_FNB) { + cno = 0; + (void)nonblank(sp, ep, lno, &cno); + return (cno); + } + + /* First character is easy, and common. */ + if (sp->rcmflags != RCM_LAST && sp->rcm == 0) + return (0); + + return (svi_lrelative(sp, ep, lno, 1)); +} + +/* + * svi_lrelative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. The offset is for the commands that move logical + * distances, i.e. if it's a logical scroll the closest physical + * distance is based on the logical line, not the physical line. + */ +size_t +svi_lrelative(sp, ep, lno, off) + SCR *sp; + EXF *ep; + recno_t lno; + size_t off; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (len == 0) + return (0); + + /* Last character is easy, and common. */ + if (sp->rcmflags == RCM_LAST) + return (len - 1); + + /* Discard logical lines. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, p = lp, llen = len; --off;) { + for (; len && scno < sp->cols; --len) + SCNO_INCREMENT; + if (len == 0) + return (llen - 1); + scno -= sp->cols; + } + + /* Step through the line until reach the right character. */ + while (len--) { + SCNO_INCREMENT; + if (scno >= sp->rcm) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == sp->rcm) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} + +/* + * svi_chposition -- + * Return the physical column from the line that will display a + * character closest to the specified column. + */ +size_t +svi_chposition(sp, ep, lno, cno) + SCR *sp; + EXF *ep; + recno_t lno; + size_t cno; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &llen)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (llen == 0) + return (0); + + /* Step through the line until reach the right character. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, len = llen, p = lp; len--;) { + SCNO_INCREMENT; + if (scno >= cno) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == cno) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c new file mode 100644 index 0000000000..16e2c36d86 --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_screen.c 8.57 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static void d_to_h __P((SCR *, char *)); +static int svi_initscr_kluge __P((SCR *, struct termios *)); +static void svi_keypad __P((SCR *, int)); +static void svi_keypad_pc __P((int)); + +/* + * svi_screen_init -- + * Initialize a screen. + */ +int +svi_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = svi_bell; + sp->s_bg = svi_bg; + sp->s_busy = svi_busy; + sp->s_change = svi_change; + sp->s_chposition = svi_chposition; + sp->s_clear = svi_clear; + sp->s_column = svi_column; + sp->s_confirm = svi_confirm; + sp->s_down = svi_sm_down; + sp->s_edit = svi_screen_edit; + sp->s_end = svi_screen_end; + sp->s_ex_cmd = svi_ex_cmd; + sp->s_ex_run = svi_ex_run; + sp->s_ex_write = svi_ex_write; + sp->s_fg = svi_fg; + sp->s_fill = svi_sm_fill; + sp->s_get = svi_get; + sp->s_key_read = sex_key_read; + sp->s_optchange = svi_optchange; + sp->s_position = svi_sm_position; + sp->s_rabs = svi_rabs; + sp->s_refresh = svi_refresh; + sp->s_relative = svi_relative; + sp->s_rrel = svi_rrel; + sp->s_split = svi_split; + sp->s_suspend = svi_suspend; + sp->s_up = svi_sm_up; + + return (0); +} + +/* + * svi_screen_copy -- + * Copy to a new screen. + */ +int +svi_screen_copy(orig, sp) + SCR *orig, *sp; +{ + SVI_PRIVATE *osvi, *nsvi; + + /* Create the private screen structure. */ + CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE)); + sp->svi_private = nsvi; + +/* INITIALIZED AT SCREEN CREATE. */ + /* Lose svi_screens() cached information. */ + nsvi->ss_lno = OOBLNO; + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + } else { + osvi = SVP(orig); + nsvi->srows = osvi->srows; + if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + F_SET(nsvi, F_ISSET(osvi, SVI_NO_VBELL)); + } + return (0); +} + +/* + * svi_screen_end -- + * End a screen. + */ +int +svi_screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + + /* + * XXX + * If this is the last screen, end curses + * while we still have screen information. + */ + if (sp->q.cqe_prev == (void *)&sp->gp->dq && + sp->q.cqe_next == (void *)&sp->gp->dq && + sp->gp->hq.cqh_first == (void *)&sp->gp->hq && + svi_curses_end(sp)) + rval = 1; + + /* Free screen map. */ + if (HMAP != NULL) + FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP)); + + /* Free visual bell string. */ + if (SVP(sp)->VB != NULL) + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + + /* Free private memory. */ + FREE(SVP(sp), sizeof(SVI_PRIVATE)); + sp->svi_private = NULL; + + return (rval); +} + +/* + * We use a single curses "window" for each vi screen. The model would be + * simpler with two windows (one for the text, and one for the modeline) + * because scrolling the text window down would work correctly then, not + * affecting the mode line. As it is we have to play games to make it look + * right. The reason for this choice is that it would be difficult for + * curses to optimize the movement, i.e. detect that the downward scroll + * isn't going to change the modeline, set the scrolling region on the + * terminal and only scroll the first part of the text window. (Even if + * curses did detect it, the set-scrolling-region terminal commands can't + * be used by curses because it's indeterminate where the cursor ends up + * after they are sent.) + */ +/* + * svi_screen_edit -- + * Main vi curses screen loop. + */ +int +svi_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + int force; + + /* Get refresh to init curses. */ + F_SET(sp, S_RESIZE); + + for (;;) { + /* Reset the cursor. */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * Run vi. If vi fails, svi data structures may be + * corrupted, be extremely careful what you free up. + */ + if (vi(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); /* End the file. */ + (void)svi_curses_end(sp); /* End curses. */ + (void)screen_end(sp); /* End the screen. */ + return (1); + } + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force))/* End the file. */ + break; + (void)svi_join(sp, &tsp); /* Find a new screen. */ + if (tsp == NULL) + (void)svi_swap(sp, &tsp, NULL); + (void)screen_end(sp); /* End the screen. */ + if ((sp = tsp) == NULL) /* Switch. */ + return (0); + break; + case 0: /* Exit vi mode. */ + (void)svi_curses_end(sp); /* End curses. */ + d_to_h(sp, "Exit from vi"); + return (0); + case S_FSWITCH: /* File switch. */ + F_CLR(sp, S_FSWITCH); + F_SET(sp, S_REFORMAT); + break; + case S_SSWITCH: /* Screen switch. */ + F_CLR(sp, S_SSWITCH); + sp = sp->nextdisp; + break; + default: + abort(); + } + } + /* NOTREACHED */ +} + +/* + * svi_curses_init -- + * Initialize curses. + */ +int +svi_curses_init(sp) + SCR *sp; +{ + struct termios t; + int ixoff, ixon; + char *p, kbuf[2048]; + + /* + * Vi wants raw mode, excepting flow control characters. Both + * cbreak and raw modes have problems for us. In cbreak mode, + * we have to turn all the signals off explicitly. In raw mode, + * we have to turn flow control back on. Raw chosen for no strong + * reason. In both cases we have to periodically turn various + * signals on. + */ + if (tcgetattr(STDIN_FILENO, &t)) { + msgq(sp, M_SYSERR, "tcgetattr"); + return (1); + } + ixon = t.c_iflag & IXON; + ixoff = t.c_iflag & IXOFF; + + /* + * The initscr() in SunOS curses flushes the terminal driver queue. + * I have no idea if this stark raving insanity appears elsewhere, + * but since the SunOS curses is likely derived from the System III + * or System V versions, here's the workaround. + */ + if (svi_initscr_kluge(sp, &t)) + return (1); + + /* + * Start the screen. Initscr() doesn't provide useful error values + * or messages. Generally, either malloc failed or the terminal + * was unknown or lacked some necesssary feature. Try and guess so + * the user isn't even more pissed off because of the error message. + */ + errno = 0; + if (initscr() == NULL) { + if (errno) + msgq(sp, M_SYSERR, "initscr failed"); + else + msgq(sp, M_ERR, "Error: initscr failed."); + if ((p = getenv("TERM")) == NULL) + msgq(sp, M_ERR, + "Error: no terminal environment variable set."); + else if (tgetent(kbuf, p) != 1) + msgq(sp, M_ERR, +"Error: %s: unknown terminal type, or terminal lacking necessary features.", p); + else + msgq(sp, M_ERR, + "Error: %s: terminal type lacking necessary features.", p); + return (1); + } + + /* + * !!! + * If raw isn't turning off echo and newlines, something's wrong. + * However, just in case... + */ + noecho(); /* No character echo. */ + nonl(); /* No CR/NL translation. */ + raw(); /* No special characters. */ + idlok(stdscr, 1); /* Use hardware insert/delete line. */ + + /* + * Vi wants the cursor keys in application mode. The historic version + * of curses had no way to do this, and the newer versions (System V) + * only enable it through the wgetch() interface. Do it roughly, here. + */ + svi_keypad(sp, 1); + + /* + * XXX + * Major compatibility kluge. When we call the curses raw() routine, + * XON/XOFF flow control is turned off. Old terminals like to have + * it, so if it's originally set for the tty, we turn it back on. For + * some unknown reason, this causes System V curses to NOT correctly + * restore the terminal modes when SIGTSTP is received. + */ + if ((ixon || ixoff) && + !tcgetattr(STDIN_FILENO, &sp->gp->s5_curses_botch)) { + t = sp->gp->s5_curses_botch; + if (ixon) + t.c_iflag |= IXON; + if (ixoff) + t.c_iflag |= IXOFF; + F_SET(sp->gp, G_CURSES_S5CB); + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + } + + /* + * The first screen in the list gets it all. All other screens + * are hidden and lose their maps. + */ + d_to_h(sp, "Window resize"); + + /* Initialize terminal values. */ + SVP(sp)->srows = O_VAL(sp, O_LINES); + + /* + * Initialize screen values. + * + * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * Setup: + * t_minrows is the minimum rows to display + * t_maxrows is the maximum rows to display (rows - 1) + * t_rows is the rows currently being displayed + */ + sp->rows = SVP(sp)->srows; + sp->cols = O_VAL(sp, O_COLUMNS); + sp->woff = 0; + sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > sp->rows - 1) { + sp->t_minrows = sp->t_rows = sp->rows - 1; + msgq(sp, M_INFO, + "Windows option value is too large, max is %u", sp->t_rows); + } + sp->t_maxrows = sp->rows - 1; + + /* Create the screen map. */ + if ((HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + memset(HMAP, 0, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + F_SET(sp->gp, G_CURSES_INIT); /* Curses initialized. */ + F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */ + return (0); +} + +/* + * svi_rrel -- + * Change the relative size of the current screen. + */ +int +svi_rrel(sp, count) + SCR *sp; + long count; +{ + /* Can't grow beyond the size of the window. */ + if (count > O_VAL(sp, O_WINDOW)) + count = O_VAL(sp, O_WINDOW); + + sp->t_minrows = sp->t_rows = count; + if (sp->t_rows > sp->rows - 1) + sp->t_minrows = sp->t_rows = sp->rows - 1; + TMAP = HMAP + (sp->t_rows - 1); + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_curses_end -- + * Move to the bottom of the screen, end curses. + */ +int +svi_curses_end(sp) + SCR *sp; +{ + /* We get called before anything has been initialized. */ + if (!F_ISSET(sp->gp, G_CURSES_INIT)) + return (0); + F_CLR(sp->gp, G_CURSES_INIT); + + /* Move to the bottom of the screen. */ + if (move(INFOLINE(sp), 0) == OK) { + clrtoeol(); + refresh(); + } + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + F_CLR(sp->gp, G_CURSES_S5CB); + + svi_keypad(sp, 0); + endwin(); + return (0); +} + +/* + * svi_keypad -- + * Put the keypad/cursor arrows into or out of application mode. + */ +static void +svi_keypad(sp, on) + SCR *sp; + int on; +{ + char *sbp, *t, kbuf[2048], sbuf[128]; + + if (tgetent(kbuf, O_STR(sp, O_TERM)) != 1) + return; + sbp = sbuf; + if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL) + return; + (void)tputs(t, 0, svi_keypad_pc); +} + +/* + * svi_keypad_pc -- + * putchar routine for tputs(). + */ +static void +svi_keypad_pc(argch) + int argch; +{ + char ch; + + ch = argch; + (void)write(STDOUT_FILENO, &ch, sizeof(ch)); +} + +/* + * svi_initscr_kluge -- + * Read all of the waiting keys before calling initscr(). + */ +static int +svi_initscr_kluge(sp, tp) + SCR *sp; + struct termios *tp; +{ + struct termios rawt; + int rval; + + /* + * Turn off canonical input processing so that we get partial + * lines as well as complete ones. Also, set the MIN/TIME + * values -- System V and SMI systems overload VMIN and VTIME, + * such that VMIN is the same as the VEOF element, and VTIME is + * the same as the VEOL element. This means, that if VEOF was + * ^D, the default VMIN is 4. Majorly stupid. + */ + rawt = *tp; + rawt.c_cc[VMIN] = 1; + rawt.c_cc[VTIME] = 0; + rawt.c_lflag &= ~ICANON; + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, &rawt)) + return (1); + rval = term_key_queue(sp); + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, tp) || rval) + return (1); + return (0); +} + +/* + * d_to_h -- + * Move all but the current screen to the hidden queue. + */ +static void +d_to_h(sp, emsg) + SCR *sp; + char *emsg; +{ + SCR *tsp; + int hidden; + + for (hidden = 0; + (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) { + if (_HMAP(tsp) != NULL) + FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP)); + CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q); + } + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + if (hidden > 1) + msgq(sp, M_INFO, + "%s backgrounded %d screens; use :display to list the screens.", + emsg, hidden - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h new file mode 100644 index 0000000000..4bf754473d --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.h @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)svi_screen.h 8.30 (Berkeley) 12/22/93 + */ + +/* + * Structure for mapping lines to the screen. An SMAP is an array, with one + * structure element per screen line, which holds information describing the + * physical line which is displayed in the screen line. The first two fields + * (lno and off) are all that are necessary to describe a line. The rest of + * the information is useful to keep information from being re-calculated. + * + * Lno is the line number. Off is the screen offset into the line. For + * example, the pair 2:1 would be the first screen of line 2, and 2:2 would + * be the second. If doing left-right scrolling, all of the offsets will be + * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc. If doing the + * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1, + * etc. + * + * The SMAP is always as large as the physical screen, plus a slot for the + * info line, so that there is room to add any screen into another one at + * screen exit. + */ +typedef struct _smap { + recno_t lno; /* 1-N: Physical file line number. */ + size_t off; /* 1-N: Screen offset in the line. */ + + /* svi_line() cache information. */ + size_t c_sboff; /* 0-N: offset of first character byte. */ + size_t c_eboff; /* 0-N: offset of last character byte. */ + u_char c_scoff; /* 0-N: offset into the first character. */ + u_char c_eclen; /* 1-N: columns from the last character. */ + u_char c_ecsize; /* 1-N: size of the last character. */ +} SMAP; + + /* Macros to flush/test cached information. */ +#define SMAP_CACHE(smp) ((smp)->c_ecsize != 0) +#define SMAP_FLUSH(smp) ((smp)->c_ecsize = 0) + +typedef struct _svi_private { +/* INITIALIZED AT SCREEN CREATE. */ + SMAP *h_smap; /* First slot of the line map. */ + SMAP *t_smap; /* Last slot of the line map. */ + + size_t exlinecount; /* Ex overwrite count. */ + size_t extotalcount; /* Ex overwrite count. */ + size_t exlcontinue; /* Ex line continue value. */ + + /* svi_screens() cache information. */ + recno_t ss_lno; /* 1-N: Line number. */ + size_t ss_screens; /* Return value. */ + + recno_t olno; /* 1-N: old cursor file line. */ + size_t ocno; /* 0-N: old file cursor column. */ + size_t sc_col; /* 0-N: LOGICAL screen column. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + size_t srows; /* 1-N: Rows in the terminal/window. */ + + char *VB; /* Visual bell termcap string. */ + +#define SVI_CUR_INVALID 0x001 /* Cursor position is unknown. */ +#define SVI_DIVIDER 0x002 /* Screen divider is displayed. */ +#define SVI_INFOLINE 0x004 /* The infoline is being used by v_ntext(). */ +#define SVI_NO_VBELL 0x008 /* No visual bell available. */ +#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */ + u_int flags; +} SVI_PRIVATE; + +#define SVP(sp) ((SVI_PRIVATE *)((sp)->svi_private)) +#define HMAP (SVP(sp)->h_smap) +#define TMAP (SVP(sp)->t_smap) +#define _HMAP(sp) (SVP(sp)->h_smap) +#define _TMAP(sp) (SVP(sp)->t_smap) + +/* + * One extra slot is always allocated for the map so that we can use + * it to do vi :colon command input; see svi_get(). + */ +#define SIZE_HMAP(sp) (SVP(sp)->srows + 1) + +#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */ +#define O_NUMBER_LENGTH 8 + /* Columns on a screen. */ +#define SCREEN_COLS(sp) \ + ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols)) + +#define HALFSCREEN(sp) ((sp)->t_maxrows / 2) /* Half the screen. */ +#define HALFTEXT(sp) ((sp)->t_rows / 2) /* Half the text. */ + +#define INFOLINE(sp) ((sp)->t_maxrows) /* Info line test, offset. */ +#define ISINFOLINE(sp, smp) (((smp) - HMAP) == INFOLINE(sp)) + + /* Small screen test. */ +#define ISSMALLSCREEN(sp) ((sp)->t_minrows != (sp)->t_maxrows) + + /* Next tab offset. */ +#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP)) + + +#define SCNO_INCREMENT /* Step through line. */\ + scno += (ch = *(u_char *)p++) == '\t' && !listset ? \ + TAB_OFF(sp, scno) : cname[ch].len + +/* Move in a screen (absolute), and fail if it doesn't work. */ +#define MOVEA(sp, lno, cno) { \ + if (move(lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), abs.", \ + tail(__FILE__), __LINE__, lno, cno); \ + return (1); \ + } \ +} + +/* Move in a window, and fail if it doesn't work. */ +#define MOVE(sp, lno, cno) { \ + size_t __lno = (sp)->woff + (lno); \ + if (move(__lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), o(%u).", \ + tail(__FILE__), __LINE__, lno, cno, sp->woff); \ + return (1); \ + } \ +} + +/* Add a character. */ +#define ADDCH(ch) { \ + int __ch = (ch); \ + ADDNSTR(cname[__ch].name, cname[__ch].len); \ +} + +/* Add a string len bytes long. */ +#define ADDNSTR(str, len) { \ + if (addnstr(str, len) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Add a string. */ +#define ADDSTR(str) { \ + if (addstr(str) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Public routines. */ +void svi_bell __P((SCR *)); +int svi_bg __P((SCR *)); +int svi_busy __P((SCR *, char const *)); +int svi_change __P((SCR *, EXF *, recno_t, enum operation)); +size_t svi_chposition __P((SCR *, EXF *, recno_t, size_t)); +int svi_column __P((SCR *, EXF *, size_t *)); +enum confirm + svi_confirm __P((SCR *, EXF *, MARK *, MARK *)); +int svi_clear __P((SCR *)); +int svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *)); +int svi_ex_run __P((SCR *, EXF *, MARK *)); +int svi_ex_write __P((void *, const char *, int)); +int svi_fg __P((SCR *, CHAR_T *)); +enum input + svi_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +int svi_optchange __P((SCR *, int)); +int svi_rabs __P((SCR *, long)); +int svi_refresh __P((SCR *, EXF *)); +size_t svi_relative __P((SCR *, EXF *, recno_t)); +int svi_rrel __P((SCR *, long)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_edit __P((SCR *, EXF *)); +int svi_screen_end __P((SCR *)); +int svi_sm_down __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_sm_fill __P((SCR *, EXF *, recno_t, enum position)); +int svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position)); +int svi_sm_up __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_split __P((SCR *, ARGS *[])); +int svi_suspend __P((SCR *)); +int svi_swap __P((SCR *, SCR **, char *)); + +/* Private routines. */ +int svi_curses_end __P((SCR *)); +int svi_curses_init __P((SCR *)); +int svi_divider __P((SCR *)); +int svi_init __P((SCR *)); +int svi_join __P((SCR *, SCR **)); +int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *)); +size_t svi_lrelative __P((SCR *, EXF *, recno_t, size_t)); +size_t svi_ncols __P((SCR *, u_char *, size_t, size_t *)); +int svi_number __P((SCR *, EXF *)); +int svi_paint __P((SCR *, EXF *)); +size_t svi_screens __P((SCR *, EXF *, recno_t, size_t *)); +int svi_sm_1down __P((SCR *, EXF *)); +int svi_sm_1up __P((SCR *, EXF *)); +int svi_sm_cursor __P((SCR *, EXF *, SMAP **)); +int svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *)); +recno_t svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t)); +int svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *)); + +/* Private debugging routines. */ +#ifdef DEBUG +int svi_gdbrefresh __P((void)); +#endif diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c new file mode 100644 index 0000000000..5bcb6e220f --- /dev/null +++ b/usr.bin/vi/svi/svi_smap.c @@ -0,0 +1,1041 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_smap.c 8.29 (Berkeley) 11/30/93"; +#endif /* not lint */ + +#include + +#include +#include +#include + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +static int svi_deleteln __P((SCR *, int)); +static int svi_insertln __P((SCR *, int)); +static int svi_sm_delete __P((SCR *, EXF *, recno_t)); +static int svi_sm_insert __P((SCR *, EXF *, recno_t)); +static int svi_sm_reset __P((SCR *, EXF *, recno_t)); + +/* + * svi_change -- + * Make a change to the screen. + */ +int +svi_change(sp, ep, lno, op) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; +{ + SMAP *p; + size_t oldy, oldx; + + /* Appending is the same as inserting, if the line is incremented. */ + if (op == LINE_APPEND) { + ++lno; + op = LINE_INSERT; + } + + /* Ignore the change if the line is after the map. */ + if (lno > TMAP->lno) + return (0); + + /* + * If the line is before the map, and it's a decrement, decrement + * the map. If it's an increment, increment the map. Otherwise, + * ignore it. + */ + if (lno < HMAP->lno) { + switch (op) { + case LINE_APPEND: + abort(); + /* NOTREACHED */ + case LINE_DELETE: + for (p = HMAP; p <= TMAP; ++p) + --p->lno; + if (sp->lno >= lno) + --sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + for (p = HMAP; p <= TMAP; ++p) + ++p->lno; + if (sp->lno >= lno) + ++sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + break; + } + return (0); + } + + F_SET(SVP(sp), SVI_SCREENDIRTY); + + /* Flush cached information from svi_screens(). */ + SVP(sp)->ss_lno = OOBLNO; + + /* Invalidate the cursor, if it's on this line. */ + if (sp->lno == lno) + F_SET(SVP(sp), SVI_CUR_INVALID); + + getyx(stdscr, oldy, oldx); + + switch (op) { + case LINE_DELETE: + if (svi_sm_delete(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + if (svi_sm_insert(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + if (svi_sm_reset(sp, ep, lno)) + return (1); + break; + default: + abort(); + } + + MOVEA(sp, oldy, oldx); + + return (0); +} + +/* + * svi_sm_fill -- + * Fill in the screen map, placing the specified line at the + * right position. There isn't any way to tell if an SMAP + * entry has been filled in, so this routine had better be + * called with P_FILL set before anything else is done. + * + * !!! + * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP + * slot is already filled in, P_BOTTOM means that the TMAP slot is + * already filled in, and we just finish up the job. + */ +int +svi_sm_fill(sp, ep, lno, pos) + SCR *sp; + EXF *ep; + recno_t lno; + enum position pos; +{ + SMAP *p, tmp; + + /* Flush all cached information from the SMAP. */ + for (p = HMAP; p <= TMAP; ++p) + SMAP_FLUSH(p); + + switch (pos) { + case P_FILL: + tmp.lno = 1; + tmp.off = 1; + + /* See if less than half a screen from the top. */ + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + lno = 1; + goto top; + } + + /* See if less than half a screen from the bottom. */ + if (file_lline(sp, ep, &tmp.lno)) + return (1); + tmp.off = svi_screens(sp, ep, tmp.lno, NULL); + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + TMAP->lno = tmp.lno; + TMAP->off = tmp.off; + goto bottom; + } + goto middle; + case P_TOP: + if (lno != OOBLNO) { +top: HMAP->lno = lno; + HMAP->off = 1; + } + /* If we fail, just punt. */ + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_MIDDLE: + /* If we fail, guess that the file is too small. */ +middle: p = HMAP + (TMAP - HMAP) / 2; + for (p->lno = lno, p->off = 1; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + + /* If we fail, just punt. */ + p = HMAP + (TMAP - HMAP) / 2; + for (; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_BOTTOM: + if (lno != OOBLNO) { + TMAP->lno = lno; + TMAP->off = svi_screens(sp, ep, lno, NULL); + } + /* If we fail, guess that the file is too small. */ +bottom: for (p = TMAP; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + break; + } + return (0); + + /* + * Try and put *something* on the screen. If this fails, + * we have a serious hard error. + */ +err: HMAP->lno = 1; + HMAP->off = 1; + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + return (1); + return (0); +} + +/* + * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the + * screen only contains one line, or, if the line is the entire screen, this + * gets fairly exciting. Skip the fun and simply return if there's only one + * line in the screen, or just call fill. Fill may not be entirely accurate, + * i.e. we may be painting the screen with something not even close to the + * cursor, but it's not like we're into serious performance issues here, and + * the refresh routine will fix it for us. + */ +#define TOO_WEIRD { \ + if (cnt_orig >= sp->t_rows) { \ + if (cnt_orig == 1) \ + return (0); \ + if (file_gline(sp, ep, lno, NULL) == NULL) \ + if (file_lline(sp, ep, &lno)) \ + return (1); \ + F_SET(sp, S_REDRAW); \ + return (svi_sm_fill(sp, ep, lno, P_TOP)); \ + } \ +} + +/* + * svi_sm_delete -- + * Delete a line out of the SMAP. + */ +static int +svi_sm_delete(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig; + + /* + * Find the line in the map, and count the number of screen lines + * which display any part of the deleted line. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 1, t = p + 1; + t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + + TOO_WEIRD; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, cnt_orig)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Decrement the line numbers for the rest of the map. */ + for (t = TMAP - cnt_orig; p <= t; ++p) + --p->lno; + + /* Display the new lines. */ + for (p = TMAP - cnt_orig;;) { + if (p < TMAP && svi_sm_next(sp, ep, p, p + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++p, NULL, NULL)) + return (1); + if (p == TMAP) + break; + } + return (0); +} + +/* + * svi_sm_insert -- + * Insert a line into the SMAP. + */ +static int +svi_sm_insert(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt; + + /* + * Find the line in the map, find out how many screen lines + * needed to display the line. + */ + for (p = HMAP; p->lno != lno; ++p); + cnt_orig = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (cnt_orig > cnt) + cnt_orig = cnt; + + /* Push down that many lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, cnt_orig)) + return (1); + + /* Shift the screen map down. */ + memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Increment the line numbers for the rest of the map. */ + for (t = p + cnt_orig; t <= TMAP; ++t) + ++t->lno; + + /* Fill in the SMAP for the new lines, and display. */ + for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + return (0); +} + +/* + * svi_sm_reset -- + * Reset a line in the SMAP. + */ +static int +svi_sm_reset(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt_new, cnt, diff; + + /* + * See if the number of on-screen rows taken up by the old display + * for the line is the same as the number needed for the new one. + * If so, repaint, otherwise do it the hard way. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + cnt_new = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + if (cnt_orig == cnt_new) { + do { + SMAP_FLUSH(p); + if (svi_line(sp, ep, p, NULL, NULL)) + return (1); + } while (++p < t); + return (0); + } + + if (cnt_orig < cnt_new) { + /* Get the difference. */ + diff = cnt_new - cnt_orig; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (diff > cnt) + diff = cnt; + + /* Push down the extra lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, diff)) + return (1); + + /* Shift the screen map down. */ + memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + } else { + /* Get the difference. */ + diff = cnt_orig - cnt_new; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, diff)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + + /* Display the new lines at the bottom of the screen. */ + for (t = TMAP - diff;;) { + if (t < TMAP && svi_sm_next(sp, ep, t, t + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++t, NULL, NULL)) + return (1); + if (t == TMAP) + break; + } + } + return (0); +} + +/* + * svi_sm_up -- + * Scroll the SMAP up count logical lines. + */ +int +svi_sm_up(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap, tmp; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* + * Check to see if movement is possible. Lots of checks... + * + * Find out if it's possible to move past the end of the map. If + * that's okay because we think that we can move the cursor down + * in the map, check to make sure that the map isn't mostly empty. + */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (tmp.lno > TMAP->lno && + !file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + if (!cursor_move || ignore_cursor || p == TMAP) { + v_eof(sp, ep, NULL); + return (1); + } + if (svi_sm_next(sp, ep, p, &tmp)) + return (1); + if (!file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + v_eof(sp, ep, NULL); + return (1); + } + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows; + --count, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (TMAP->lno != tmp.lno && + !file_gline(sp, ep, tmp.lno, NULL)) + break; + *++TMAP = tmp; + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* Decide what would show up on the screen. */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + + /* If the line doesn't exist, we're done. */ + if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL)) + break; + + /* Scroll the screen cursor up one logical line. */ + if (svi_sm_1up(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p > HMAP) + --p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* + * If we didn't move enough, head toward EOF. Check to make + * sure the lines actually, if the file is smaller than the + * screen they may not. + */ + for (; count; --count, ++p) + if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL)) + break; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_eof(sp, ep, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the top. */ + if (sp->lno < HMAP->lno) + p = HMAP; + } + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1up -- + * Scroll the SMAP up one. + */ +int +svi_sm_1up(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Delete the top line of the screen. Shift the screen map up. + * Display a new line at the bottom of the screen. + */ + MOVE(sp, 0, 0); + if (svi_deleteln(sp, 1)) + return (1); + + /* One-line screens can fail. */ + if (HMAP == TMAP) { + if (svi_sm_next(sp, ep, TMAP, TMAP)) + return (1); + } else { + memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_next(sp, ep, TMAP - 1, TMAP)) + return (1); + } + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_deleteln -- + * Delete a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_deleteln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + deleteln(); + MOVE(sp, INFOLINE(sp) - 1, 0); + insertln(); + MOVEA(sp, oldy, oldx); + } + return (0); +} + +/* + * svi_sm_down -- + * Scroll the SMAP down count logical lines. + */ +int +svi_sm_down(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* Check to see if movement is possible. */ + if (HMAP->lno == 1 && HMAP->off == 1 && + (!cursor_move || ignore_cursor || p == HMAP)) { + v_sof(sp, NULL); + return (1); + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows && + (HMAP->lno > 1 || HMAP->off > 1); + --count, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move) + ++p; + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* If the line doesn't exist, we're done. */ + if (HMAP->lno == 1 && HMAP->off == 1) + break; + + /* Scroll the screen and cursor down one logical line. */ + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p < TMAP) + ++p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* If we didn't move enough, move to SOF. */ + if (count) + p = HMAP; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_sof(sp, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the bottom. */ + if (sp->lno > TMAP->lno) + p = TMAP; + } + + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1down -- + * Scroll the SMAP down one. + */ +int +svi_sm_1down(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Clear the bottom line of the screen, insert a line at the top + * of the screen. Shift the screen map down, display a new line + * at the top of the screen. + */ + MOVE(sp, sp->t_rows, 0); + clrtoeol(); + MOVE(sp, 0, 0); + if (svi_insertln(sp, 1)) + return (1); + memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_prev(sp, ep, HMAP + 1, HMAP)) + return (1); + /* svi_sm_prev() flushed the cache. */ + if (svi_line(sp, ep, HMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_insertln -- + * Insert a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_insertln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + MOVE(sp, INFOLINE(sp) - 1, 0); + deleteln(); + MOVEA(sp, oldy, oldx); + insertln(); + } + return (0); +} + +/* + * svi_sm_next -- + * Fill in the next entry in the SMAP. + */ +int +svi_sm_next(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + size_t lcnt; + + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno + 1; + t->off = p->off; + } else { + lcnt = svi_screens(sp, ep, p->lno, NULL); + if (lcnt == p->off) { + t->lno = p->lno + 1; + t->off = 1; + } else { + t->lno = p->lno; + t->off = p->off + 1; + } + } + return (0); +} + +/* + * svi_sm_prev -- + * Fill in the previous entry in the SMAP. + */ +int +svi_sm_prev(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno - 1; + t->off = p->off; + } else if (p->off != 1) { + t->lno = p->lno; + t->off = p->off - 1; + } else { + t->lno = p->lno - 1; + t->off = svi_screens(sp, ep, t->lno, NULL); + } + return (t->lno == 0); +} + +/* + * svi_sm_cursor -- + * Return the SMAP entry referenced by the cursor. + */ +int +svi_sm_cursor(sp, ep, smp) + SCR *sp; + EXF *ep; + SMAP **smp; +{ + SMAP *p; + + /* See if the cursor is not in the map. */ + if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) { + *smp = NULL; + return (0); + } + + /* Find the first occurence of the line. */ + for (p = HMAP; p->lno != sp->lno; ++p); + + /* Fill in the map information until we find the right line. */ + for (; p <= TMAP; ++p) { + /* Short lines are common and easy to detect. */ + if (p != TMAP && (p + 1)->lno != p->lno) { + *smp = p; + return (0); + } + if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL)) + return (1); + if (p->c_eboff >= sp->cno) { + *smp = p; + return (0); + } + } + + /* It was past the end of the map after all. */ + *smp = NULL; + return (0); +} + +/* + * svi_sm_position -- + * Return the line/column of the top, middle or last line on the screen. + * (The vi H, M and L commands.) Here because only the screen routines + * know what's really out there. + */ +int +svi_sm_position(sp, ep, rp, cnt, pos) + SCR *sp; + EXF *ep; + MARK *rp; + u_long cnt; + enum position pos; +{ + SMAP *smp; + recno_t last; + + switch (pos) { + case P_TOP: + if (cnt > TMAP - HMAP) + goto err; + smp = HMAP + cnt; + break; + case P_MIDDLE: + if (cnt > (TMAP - HMAP) / 2) + goto err; + smp = (HMAP + (TMAP - HMAP) / 2) + cnt; + goto eof; + case P_BOTTOM: + if (cnt > TMAP - HMAP) { +err: msgq(sp, M_BERR, "Movement past the end-of-screen."); + return (1); + } + smp = TMAP - cnt; +eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) { + if (file_lline(sp, ep, &last)) + return (1); + for (; smp->lno > last && smp > HMAP; --smp); + } + break; + default: + abort(); + } + + if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL)) + return (1); + rp->lno = smp->lno; + rp->cno = smp->c_sboff; + + return (0); +} + +/* + * svi_sm_nlines -- + * Return the number of screen lines from an SMAP entry to the + * start of some file line, less than a maximum value. + */ +recno_t +svi_sm_nlines(sp, ep, from_sp, to_lno, max) + SCR *sp; + EXF *ep; + SMAP *from_sp; + recno_t to_lno; + size_t max; +{ + recno_t lno, lcnt; + + if (O_ISSET(sp, O_LEFTRIGHT)) + return (from_sp->lno > to_lno ? + from_sp->lno - to_lno : to_lno - from_sp->lno); + + if (from_sp->lno == to_lno) + return (from_sp->off - 1); + + if (from_sp->lno > to_lno) { + lcnt = from_sp->off - 1; /* Correct for off-by-one. */ + for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } else { + lno = from_sp->lno; + lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1; + for (; ++lno < to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } + return (lcnt); +} diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c new file mode 100644 index 0000000000..f6be03d383 --- /dev/null +++ b/usr.bin/vi/svi/svi_split.c @@ -0,0 +1,581 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_split.c 8.29 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_split -- + * Split the screen. + */ +int +svi_split(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + MSG *mp, *next; + SCR *tsp, saved_sp; + SVI_PRIVATE saved_svp; + SMAP *smp; + size_t cnt, half; + int issmallscreen, splitup; + + /* Check to see if it's possible. */ + half = sp->rows / 2; + if (half < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen must be larger than %d to split", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Get a new screen. */ + if (screen_init(sp, &tsp, 0)) + return (1); + MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + if (_HMAP(tsp) == NULL) + return (1); + + /* + * We're about to modify the current screen. Save the contents + * in case something goes horribly, senselessly wrong. + */ + saved_sp = *sp; + saved_svp = *SVP(sp); + +/* INITIALIZED AT SCREEN CREATE. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * Set a flag so we know to fix the screen up later. + */ + issmallscreen = ISSMALLSCREEN(sp); + + /* + * Split the screen, and link the screens together. If the cursor + * is in the top half of the current screen, the new screen goes + * under the current screen. Else, it goes above the current screen. + * + * The columns in the screen don't change. + */ + tsp->cols = sp->cols; + + cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP; + if (cnt <= half) { /* Parent is top half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff + half; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->t_maxrows = sp->rows - 1; + + splitup = 0; + } else { /* Parent is bottom half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->woff += tsp->rows; + sp->t_maxrows = sp->rows - 1; + + /* Shift the parent's map down. */ + memmove(_HMAP(sp), + _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP)); + + splitup = 1; + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The child may have different screen options sizes than the + * parent, so use them. Make sure that the text counts aren't + * larger than the new screen sizes. + */ + if (issmallscreen) { + /* Fix the text line count for the parent. */ + if (splitup) + sp->t_rows -= tsp->rows; + + /* Fix the parent screen. */ + if (sp->t_rows > sp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > sp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + + /* Fix the child screen. */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->t_maxrows) + tsp->t_rows = tsp->t_maxrows; + if (tsp->t_minrows > tsp->t_maxrows) + tsp->t_minrows = tsp->t_maxrows; + + /* + * If we split up, i.e. the child is on top, lines that + * were painted in the parent may not be painted in the + * child. Clear any lines not being used in the child + * screen. + * + */ + if (splitup) + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } else { + sp->t_minrows = sp->t_rows = sp->rows - 1; + + /* + * The new screen may be a small screen, even though the + * parent was not. Don't complain if O_WINDOW is too large, + * we're splitting the screen so the screen is much smaller + * than normal. Clear any lines not being used in the child + * screen. + */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->rows - 1) + tsp->t_minrows = tsp->t_rows = tsp->rows - 1; + else + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } + + /* Adjust the ends of both maps. */ + _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1); + _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1); + + /* + * In any case, if the size of the scrolling region hasn't been + * modified by the user, reset it so it's reasonable for the split + * screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) { + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2; + } + + /* + * If files specified, build the file list, else, link to the + * current file. + */ + if (argv == NULL) { + if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL) + goto err; + } else + for (; (*argv)->len != 0; ++argv) + if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL) + goto err; + + /* Set up the argument and current FREF pointers. */ + if ((tsp->frp = file_first(tsp)) == NULL) { + msgq(sp, M_ERR, "No files in the file list."); + goto err; + } + + tsp->a_frp = tsp->frp; + + /* + * Copy the file state flags, start the file. Fill the child's + * screen map. If the file is unchanged, keep the screen and + * cursor the same. + */ + if (argv == NULL) { + tsp->ep = sp->ep; + ++sp->ep->refcnt; + + tsp->frp->flags = sp->frp->flags; + tsp->frp->lno = sp->lno; + tsp->frp->cno = sp->cno; + F_SET(tsp->frp, FR_CURSORSET); + + /* Copy the parent's map into the child's map. */ + memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP)); + } else { + if (file_init(tsp, tsp->frp, NULL, 0)) + goto err; + (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP); + } + + /* Everything's initialized, put the screen on the displayed queue.*/ + if (splitup) { + /* Link in before the parent. */ + CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q); + } else { + /* Link in after the parent. */ + CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q); + } + + /* Clear the current information lines in both screens. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + MOVE(tsp, INFOLINE(tsp), 0); + clrtoeol(); + + /* Redraw the status line for the parent screen. */ + (void)status(sp, sp->ep, sp->lno, 0); + + /* Save the parent screen's cursor information. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + /* Completely redraw the child screen. */ + F_SET(tsp, S_REDRAW); + + /* Switch screens. */ + sp->nextdisp = tsp; + F_SET(sp, S_SSWITCH); + return (0); + + /* Recover the original screen. */ +err: *sp = saved_sp; + *SVP(sp) = saved_svp; + + /* Copy any (probably error) messages in the new screen. */ + for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + free(mp->mbuf); + free(mp); + } + + /* Free the new screen. */ + free(_HMAP(tsp)); + free(SVP(tsp)); + FREE(tsp, sizeof(SCR)); + return (1); +} + +/* + * svi_bg -- + * Background the screen, and switch to the next one. + */ +int +svi_bg(csp) + SCR *csp; +{ + SCR *sp; + + /* Try and join with another screen. */ + if ((svi_join(csp, &sp))) + return (1); + if (sp == NULL) { + msgq(csp, M_ERR, + "You may not background your only displayed screen."); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + return (0); +} + +/* + * svi_join -- + * Join the screen into a related screen, if one exists, + * and return that screen. + */ +int +svi_join(csp, nsp) + SCR *csp, **nsp; +{ + SCR *sp; + size_t cnt; + + /* + * If a split screen, add space to parent/child. Make no effort + * to clean up the screen's values. If it's not exiting, we'll + * get it when the user asks to show it again. + */ + if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) { + if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) { + *nsp = NULL; + return (0); + } + sp->woff = csp->woff; + } + sp->rows += csp->rows; + if (ISSMALLSCREEN(sp)) { + sp->t_maxrows += csp->rows; + for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } else { + sp->t_maxrows += csp->rows; + sp->t_rows = sp->t_minrows = sp->t_maxrows; + TMAP = HMAP + (sp->t_rows - 1); + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + } + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + *nsp = sp; + return (0); +} + +/* + * svi_fg -- + * Background the current screen, and foreground a new one. + */ +int +svi_fg(csp, name) + SCR *csp; + CHAR_T *name; +{ + SCR *sp; + + if (svi_swap(csp, &sp, name)) + return (1); + if (sp == NULL) { + if (name == NULL) + msgq(csp, M_ERR, "There are no background screens."); + else + msgq(csp, M_ERR, + "There's no background screen editing a file named %s.", + name); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + return (0); +} + +/* + * svi_swap -- + * Swap the current screen with a hidden one. + */ +int +svi_swap(csp, nsp, name) + SCR *csp, **nsp; + char *name; +{ + SCR *sp; + int issmallscreen; + + /* Find the screen, or, if name is NULL, the first screen. */ + for (sp = csp->gp->hq.cqh_first; + sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next) + if (name == NULL || !strcmp(FILENAME(sp->frp), name)) + break; + if (sp == (void *)&csp->gp->hq) { + *nsp = NULL; + return (0); + } + *nsp = sp; + + /* Save the old screen's cursor information. */ + csp->frp->lno = csp->lno; + csp->frp->cno = csp->cno; + F_SET(csp->frp, FR_CURSORSET); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + /* Initialize terminal information. */ + SVP(sp)->srows = SVP(csp)->srows; + + issmallscreen = ISSMALLSCREEN(sp); + + /* Initialize screen information. */ + sp->rows = csp->rows; + sp->cols = csp->cols; + sp->woff = csp->woff; + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The new screens may have different screen options sizes than the + * old one, so use them. Make sure that text counts aren't larger + * than the new screen sizes. + */ + if (issmallscreen) { + sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > csp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > csp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + } else + sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1; + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + /* + * Don't change the screen's cursor information other than to + * note that the cursor is wrong. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * The HMAP may be NULL, if the screen got resized and + * a bunch of screens had to be hidden. + */ + if (HMAP == NULL) + MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + /* Fill the map. */ + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + + /* + * The new screen replaces the old screen in the parent/child list. + * We insert the new screen after the old one. If we're exiting, + * the exit will delete the old one, if we're foregrounding, the fg + * code will move the old one to the hidden queue. + */ + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q); + + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_rabs -- + * Change the absolute size of the current screen. + */ +int +svi_rabs(sp, count) + SCR *sp; + long count; +{ + SCR *g, *s; + + /* + * Figure out which screens will grow, which will shrink, and + * make sure it's possible. + */ + if (count == 0) + return (0); + if (count < 0) { + count = -count; + s = sp; + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + goto toosmall; + if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) { + if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq) + goto toobig; + g->woff -= count; + } else + s->woff += count; + } else { + g = sp; + if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq) + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + s = NULL; + else + s->woff += count; + else + s = NULL; + if (s == NULL) { + if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) { +toobig: msgq(sp, M_BERR, "The screen cannot %s.", + count < 0 ? "shrink" : "grow"); + return (1); + } + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) { +toosmall: msgq(sp, M_BERR, + "The screen can only shrink to %d rows.", + MINIMUM_SCREEN_ROWS); + return (1); + } + g->woff -= count; + } + } + + /* Update the screens. */ + g->rows += count; + g->t_rows += count; + if (g->t_minrows == g->t_maxrows) + g->t_minrows += count; + g->t_maxrows += count; + _TMAP(g) = _HMAP(g) + (g->t_rows - 1); + (void)status(g, g->ep, g->lno, 0); + F_SET(g, S_REDRAW); + + s->rows -= count; + s->t_rows -= count; + s->t_maxrows -= count; + if (s->t_minrows > s->t_maxrows) + s->t_minrows = s->t_maxrows; + _TMAP(s) = _HMAP(s) + (s->t_rows - 1); + (void)status(s, s->ep, s->lno, 0); + F_SET(s, S_REDRAW); + + return (0); +} diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c new file mode 100644 index 0000000000..3a751b5b96 --- /dev/null +++ b/usr.bin/vi/svi/svi_util.c @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_util.c 8.26 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "vi.h" +#include "../vi/vcmd.h" +#include "excmd.h" +#include "svi_screen.h" + +/* + * svi_screens -- + * Return the number of screens required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_screens(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + size_t cols, len, screens; + char *p; + + /* + * Check for single cached value. The cache is because, if + * the line is large, this routine gets called repeatedly. + * One other hack, lots of time the user is on column one, + * which is an easy one. + */ + if (cnop == NULL) { + if (SVP(sp)->ss_lno == lno) + return (SVP(sp)->ss_screens); + } else if (*cnop == 0) + return (1); + + /* Get a copy of the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0) + return (1); + + /* Figure out how many columns the line/column needs. */ + cols = svi_ncols(sp, p, len, cnop); + + /* Leading number if O_NUMBER option set. */ + if (O_ISSET(sp, O_NUMBER)) + cols += O_NUMBER_LENGTH; + + /* Trailing '$' if O_LIST option set. */ + if (O_ISSET(sp, O_LIST) && cnop == NULL) + cols += sp->gp->cname['$'].len; + + screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0)); + if (cnop == NULL) { + SVP(sp)->ss_lno = lno; + SVP(sp)->ss_screens = screens; + } + return (screens); +} + +/* + * svi_ncols -- + * Return the number of columns required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_ncols(sp, p, len, cnop) + SCR *sp; + u_char *p; + size_t len, *cnop; +{ + CHNAME const *cname; + size_t cno_cnt, scno; + int ch, listset; + + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + + if (cnop == NULL) + for (scno = 0; len; --len) + SCNO_INCREMENT; + else + for (cno_cnt = *cnop, scno = 0; len; --len) { + SCNO_INCREMENT; + if (cno_cnt == 0) + break; + --cno_cnt; + } + return (scno); +} + +/* + * bell_putchar -- + * Functional version of putchar, for tputs. + */ +static void +bell_putchar(ch) + int ch; +{ + (void)putchar(ch); +} + +/* + * vbell -- + * Set up the visual bell information. Broken out into a + * separate routine so don't allocate 4K every time we beep. + */ +static int +vbell(sp) + SCR *sp; +{ + size_t len; + char *s, *t, b1[2048], b2[2048]; + + /* Get the termcap information. */ + s = O_STR(sp, O_TERM); + if (tgetent(b1, s) != 1) { + msgq(sp, M_ERR, "No termcap entry for %s", s); + return (1); + } + + /* Get the visual bell string. */ + t = b2; + if (tgetstr("vb", &t) == NULL) { + msgq(sp, M_VINFO, + "No visual bell for %s terminal type", s); + return (1); + } + len = t - b2; + MALLOC_RET(sp, s, char *, len); + memmove(s, b2, len); + if (SVP(sp)->VB != NULL) + free(SVP(sp)->VB); + SVP(sp)->VB = t; + return (0); +} + +/* + * svi_bell -- + * Ring the bell. + */ +void +svi_bell(sp) + SCR *sp; +{ + if (O_ISSET(sp, O_FLASH) && !F_ISSET(SVP(sp), SVI_NO_VBELL)) + if (SVP(sp)->VB != NULL) { + (void)tputs(SVP(sp)->VB, 1, bell_putchar); + (void)fflush(stdout); + } else { + if (vbell(sp)) + F_SET(SVP(sp), SVI_NO_VBELL); + svi_bell(sp); + } + else + (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */ + F_CLR(sp, S_BELLSCHED); +} + +/* + * svi_optchange -- + * Screen specific "option changed" routine. + */ +int +svi_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_TERM: + /* Toss any saved visual bell information. */ + if (SVP(sp)->VB != NULL) { + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + SVP(sp)->VB = NULL; + } + F_CLR(SVP(sp), SVI_NO_VBELL); + F_SET(sp, S_RESIZE); + break; + case O_WINDOW: + if (svi_rrel(sp, O_VAL(sp, O_WINDOW))) + return (1); + break; + } + + (void)v_optchange(sp, opt); + (void)ex_optchange(sp, opt); + + return (0); +} + +/* + * svi_busy -- + * Put the cursor somewhere so the user will think we're busy. + */ +int +svi_busy(sp, msg) + SCR *sp; + char const *msg; +{ + MOVE(sp, INFOLINE(sp), 0); + if (msg) { + ADDSTR(msg); + clrtoeol(); + } + refresh(); + F_SET(SVP(sp), SVI_CUR_INVALID); + return (0); +} + +/* + * svi_clear -- + * Clear from the row down to the end of the screen. + */ +int +svi_clear(sp) + SCR *sp; +{ + size_t oldy, oldx, row; + + getyx(stdscr, oldy, oldx); + for (row = SVP(sp)->srows - 1; row >= oldy; --row) { + MOVEA(sp, row, 0); + clrtoeol(); + } + MOVEA(sp, oldy, oldx); + refresh(); + return (0); +} + +/* + * svi_suspend -- + * Suspend the svi screen; don't kill the process group, curses is + * expected to do that for us. + */ +int +svi_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + } + + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(getpid(), SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + + return (rval); +} + +/* + * svi_gdbrefresh -- + * Stub routine so can flush out screen changes using gdb. + */ +#ifdef DEBUG +int +svi_gdbrefresh() +{ + refresh(); + return (0); +} +#endif diff --git a/usr.bin/vi/term.c b/usr.bin/vi/term.c new file mode 100644 index 0000000000..aa6fd79b90 --- /dev/null +++ b/usr.bin/vi/term.c @@ -0,0 +1,687 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)term.c 8.40 (Berkeley) 1/8/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "vi.h" +#include "seq.h" + +static int keycmp __P((const void *, const void *)); + +/* + * If we're reading less than 20 characters, up the size of the tty buffer. + * This shouldn't ever happen, other than the first time through, but it's + * possible if a map is large enough. + */ +#define term_read_grow(sp, tty) \ + (tty)->len - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty) +static int __term_read_grow __P((SCR *, IBUF *)); + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. + */ +typedef struct _tklist { + char *ts; /* Key's termcap string. */ + char *output; /* Corresponding vi command. */ + char *name; /* Name. */ +} TKLIST; +static TKLIST const tklist[] = { + {"kA", "O", "insert line"}, + {"kD", "x", "delete character"}, + {"kd", "j", "cursor down"}, + {"kE", "D", "delete to eol"}, + {"kF", "\004", "scroll down"}, + {"kH", "$", "go to eol"}, + {"kh", "^", "go to sol"}, + {"kI", "i", "insert at cursor"}, + {"kL", "dd", "delete line"}, + {"kl", "h", "cursor left"}, + {"kN", "\006", "page down"}, + {"kP", "\002", "page up"}, + {"kR", "\025", "scroll up"}, + {"kS", "dG", "delete to end of screen"}, + {"kr", "l", "cursor right"}, + {"ku", "k", "cursor up"}, + {NULL}, +}; + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET. + */ +typedef struct _keylist { + u_char value; /* Special value. */ + CHAR_T ch; /* Key. */ +} KEYLIST; +static KEYLIST keylist[] = { + {K_CARAT, '^'}, + {K_CNTRLR, '\022'}, + {K_CNTRLT, '\024'}, + {K_CNTRLZ, '\032'}, + {K_COLON, ':'}, + {K_CR, '\r'}, + {K_ESCAPE, '\033'}, + {K_FORMFEED, '\f'}, + {K_NL, '\n'}, + {K_RIGHTBRACE, '}'}, + {K_RIGHTPAREN, ')'}, + {K_TAB, '\t'}, + {K_VEOF, '\004'}, + {K_VERASE, '\b'}, + {K_VINTR, '\003'}, + {K_VKILL, '\025'}, + {K_VLNEXT, '\026'}, + {K_VWERASE, '\027'}, + {K_ZERO, '0'}, +}; + +/* + * term_init -- + * Initialize the special key lookup table, and the special keys + * defined by the terminal's termcap entry. + */ +int +term_init(sp) + SCR *sp; +{ + extern CHNAME const asciiname[]; /* XXX */ + GS *gp; + KEYLIST *kp; + TKLIST const *tkp; + cc_t ch; + int cnt; + char *sbp, *t, buf[2 * 1024], sbuf[128]; + + /* + * XXX + * 8-bit, ASCII only, for now. Recompilation should get you + * any 8-bit character set, as long as nul isn't a character. + */ + gp = sp->gp; + gp->cname = asciiname; /* XXX */ + + /* Set keys found in the termios structure. */ +#define TERMSET(name, val) { \ + if ((ch = gp->original_termios.c_cc[name]) != _POSIX_VDISABLE) \ + for (kp = keylist;; ++kp) \ + if (kp->value == (val)) { \ + kp->ch = ch; \ + break; \ + } \ +} +/* + * VEOF, VERASE, VKILL are required by POSIX 1003.1-1990, + * VWERASE is a 4.4BSD extension. + */ +#ifdef VEOF + TERMSET(VEOF, K_VEOF); +#endif +#ifdef VERASE + TERMSET(VERASE, K_VERASE); +#endif +#ifdef VINTR + TERMSET(VINTR, K_VINTR); +#endif +#ifdef VKILL + TERMSET(VKILL, K_VKILL); +#endif +#ifdef VWERASE + TERMSET(VWERASE, K_VWERASE); +#endif + + /* Sort the special key list. */ + qsort(keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + + /* Initialize the fast lookup table. */ + CALLOC_RET(sp, + gp->special_key, u_char *, MAX_FAST_KEY + 1, sizeof(u_char)); + for (gp->max_special = 0, kp = keylist, + cnt = sizeof(keylist) / sizeof(keylist[0]); cnt--; ++kp) { + if (gp->max_special < kp->value) + gp->max_special = kp->value; + if (kp->ch <= MAX_FAST_KEY) + gp->special_key[kp->ch] = kp->value; + } + + /* Set key sequences found in the termcap entry. */ + switch (tgetent(buf, O_STR(sp, O_TERM))) { + case -1: + msgq(sp, M_ERR, + "tgetent: %s: %s.", O_STR(sp, O_TERM), strerror(errno)); + return (0); + case 0: + msgq(sp, M_ERR, + "%s: unknown terminal type.", O_STR(sp, O_TERM)); + return (0); + } + + for (tkp = tklist; tkp->name != NULL; ++tkp) { + sbp = sbuf; + if ((t = tgetstr(tkp->ts, &sbp)) == NULL) + continue; + if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), + tkp->output, strlen(tkp->output), SEQ_COMMAND, 0)) + return (1); + } + return (0); +} + +/* + * term_push -- + * Push keys onto the front of a buffer. + * + * There is a single input buffer in ex/vi. Characters are read onto the + * end of the buffer by the terminal input routines, and pushed onto the + * front of the buffer various other functions in ex/vi. Each key has an + * associated flag value, which indicates if it has already been quoted, + * if it is the result of a mapping or an abbreviation. + */ +int +term_push(sp, s, len, cmap, flags) + SCR *sp; + CHAR_T *s; /* Characters. */ + size_t len; /* Number of chars. */ + u_int cmap; /* Map count. */ + u_int flags; /* CH_* flags. */ +{ + IBUF *tty; + size_t nlen; + + /* If we have room, stuff the keys into the buffer. */ + tty = sp->gp->tty; + if (len <= tty->next || + (tty->ch != NULL && tty->cnt == 0 && len <= tty->len)) { + if (tty->cnt != 0) + tty->next -= len; + tty->cnt += len; + memmove(tty->ch + tty->next, s, len * sizeof(CHAR_T)); + memset(tty->chf + tty->next, flags, len); + memset(tty->cmap + tty->next, cmap, len); + return (0); + } + + /* Get enough space plus a little extra. */ + nlen = tty->cnt + len; + if (nlen > tty->len) { + size_t olen; + + nlen += 64; + olen = tty->len; + BINC_RET(sp, tty->ch, olen, nlen * sizeof(tty->ch[0])); + olen = tty->len; + BINC_RET(sp, tty->chf, olen, nlen * sizeof(tty->chf[0])); + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + } + + /* + * If there are currently characters in the queue, shift them up, + * leaving some extra room. + */ +#define TERM_PUSH_SHIFT 30 + if (tty->cnt) { + memmove(tty->ch + TERM_PUSH_SHIFT + len, + tty->ch + tty->next, tty->cnt * sizeof(tty->ch[0])); + memmove(tty->chf + TERM_PUSH_SHIFT + len, + tty->chf + tty->next, tty->cnt * sizeof(tty->chf[0])); + memmove(tty->cmap + TERM_PUSH_SHIFT + len, + tty->cmap + tty->next, tty->cnt * sizeof(tty->cmap[0])); + } + + /* Put the new characters into the queue. */ + tty->next = TERM_PUSH_SHIFT; + tty->cnt += len; + memmove(tty->ch + TERM_PUSH_SHIFT, s, len * sizeof(tty->ch[0])); + memset(tty->chf + TERM_PUSH_SHIFT, flags, len * sizeof(tty->chf[0])); + memset(tty->cmap + TERM_PUSH_SHIFT, cmap, len * sizeof(tty->cmap[0])); + return (0); +} + +/* + * Remove characters from the queue, simultaneously clearing the + * flag and map counts. + */ +#define QREM_HEAD(q, len) { \ + size_t __off = (q)->next; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ + else \ + (q)->next += len; \ +} +#define QREM_TAIL(q, len) { \ + size_t __off = (q)->next + (q)->cnt - 1; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ +} + +/* + * term_key -- + * Get the next key. + * + * !!! + * The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea + * of mapping keys is that one or more keystrokes act like a function key. + * What's going on is that vi is reading a number, and the character following + * the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the + * user is entering the z command, a valid command is "z40+", and we don't want + * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it + * into "z40xxx". However, if the user enters "35x", we want to put all of the + * characters through the mapping code. + * + * Historical practice is a bit muddled here. (Surprise!) It always permitted + * mapping digits as long as they weren't the first character of the map, e.g. + * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9 + * (the digit 0 was a special case as it doesn't indicate the start of a count) + * as the first character of the map, but then ignored those mappings. While + * it's probably stupid to map digits, vi isn't your mother. + * + * The way this works is that the TXT_MAPNODIGIT causes term_key to return the + * end-of-digit without "looking" at the next character, i.e. leaving it as the + * user entered it. Presumably, the next term_key call will tell us how the + * user wants it handled. + * + * There is one more complication. Users might map keys to digits, and, as + * it's described above, the commands "map g 1G|d2g" would return the keys + * "d21G", when the user probably wanted "d21G". + * So, if a map starts off with a digit we continue as before, otherwise, we + * pretend that we haven't mapped the character and return . + * + * Now that that's out of the way, let's talk about Energizer Bunny macros. + * It's easy to create macros that expand to a loop, e.g. map x 3x. It's + * fairly easy to detect this example, because it's all internal to term_key. + * If we're expanding a macro and it gets big enough, at some point we can + * assume it's looping and kill it. The examples that are tough are the ones + * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion + * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then + * find the looping macro again. There is no way that we can detect this + * without doing a full parse of the command, because the character that might + * cause the loop (in this case 'x') may be a literal character, e.g. the map + * map x "ayy"xyy"byy is perfectly legal and won't cause a loop. + * + * Historic vi tried to detect looping macros by disallowing obvious cases in + * the map command, maps that that ended with the same letter as they started + * (which wrongly disallowed "map x 'x"), and detecting macros that expanded + * too many times before keys were returned to the command parser. It didn't + * get many (most?) of the tricky cases right, however, and it was certainly + * possible to create macros that ran forever. And, even if it did figure out + * what was going on, the user was usually tossed into ex mode. Finally, any + * changes made before vi realized that the macro was recursing were left in + * place. This implementation counts how many times each input character has + * been mapped. If it reaches some arbitrary value, we flush all mapped keys + * and return an error. + * + * XXX + * The final issue is recovery. It would be possible to undo all of the work + * that was done by the macro if we entered a record into the log so that we + * knew when the macro started, and, in fact, this might be worth doing at some + * point. Given that this might make the log grow unacceptably (consider that + * cursor keys are done with maps), for now we leave any changes made in place. + */ +enum input +term_key(sp, chp, flags) + SCR *sp; + CH *chp; + u_int flags; +{ + enum input rval; + struct timeval t, *tp; + CHAR_T ch; + GS *gp; + IBUF *tty; + SEQ *qp; + int cmap, ispartial, nr; + + gp = sp->gp; + tty = gp->tty; + + /* + * If the queue is empty, read more keys in. Since no timeout is + * requested, s_key_read will either return an error or will read + * some number of characters. + */ +loop: if (tty->cnt == 0) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + /* + * If there's something on the mode line that we wanted + * the user to see, they just entered a character so we + * can presume they saw it. + */ + if (F_ISSET(sp, S_UPDATE_MODE)) + F_CLR(sp, S_UPDATE_MODE); + } + + /* If the key is mappable and should be mapped, look it up. */ + if (!(tty->chf[tty->next] & CH_NOMAP) && + LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) { + /* Set up timeout value. */ + if (O_ISSET(sp, O_TIMEOUT)) { + tp = &t; + t.tv_sec = O_VAL(sp, O_KEYTIME) / 10; + t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L; + } else + tp = NULL; + + /* Get the next key. */ +newmap: ch = tty->ch[tty->next]; + if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch)) + goto nomap; + + /* Search the map. */ +remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt, + LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, + &ispartial); + + /* + * If get a partial match, read more characters and retry + * the map. If no characters read, return the characters + * unmapped. + */ + if (ispartial) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, tp)) + return (rval); + if (nr) + goto remap; + goto nomap; + } + + /* If no map, return the character. */ + if (qp == NULL) + goto nomap; + + /* + * If looking for the end of a digit string, and the first + * character of the map is it, pretend we haven't seen the + * character. + */ + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(qp->output[0])) + goto not_digit_ch; + + /* + * Only permit a character to be remapped a certain number + * of times before we figure that it's not going to finish. + */ + if ((cmap = tty->cmap[tty->next]) > MAX_MAP_COUNT) { + term_map_flush(sp, "Character remapped too many times"); + return (INP_ERR); + } + + /* Delete the mapped characters from the queue. */ + QREM_HEAD(tty, qp->ilen); + + /* If remapping characters, push the character on the queue. */ + if (O_ISSET(sp, O_REMAP)) { + if (term_push(sp, qp->output, qp->olen, ++cmap, 0)) + return (INP_ERR); + goto newmap; + } + + /* Else, push the characters on the queue and return one. */ + if (term_push(sp, qp->output, qp->olen, 0, CH_NOMAP)) + return (INP_ERR); + } + +nomap: ch = tty->ch[tty->next]; + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) { +not_digit_ch: chp->ch = NOT_DIGIT_CH; + chp->value = 0; + chp->flags = 0; + return (INP_OK); + } + + /* Fill in the return information. */ + chp->ch = ch; + chp->flags = tty->chf[tty->next]; + chp->value = term_key_val(sp, ch); + + /* Delete the character from the queue. */ + QREM_HEAD(tty, 1); + + /* + * O_BEAUTIFY eliminates all control characters except + * escape, form-feed, newline and tab. + */ + if (isprint(ch) || + !LF_ISSET(TXT_BEAUTIFY) || !O_ISSET(sp, O_BEAUTIFY) || + chp->value == K_ESCAPE || chp->value == K_FORMFEED || + chp->value == K_NL || chp->value == K_TAB) + return (INP_OK); + + goto loop; +} + +/* + * term_ab_flush -- + * Flush any abbreviated keys. + */ +void +term_ab_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !(tty->chf[tty->next] & CH_ABBREVIATED)) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->chf[tty->next] & CH_ABBREVIATED); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} +/* + * term_map_flush -- + * Flush any mapped keys. + */ +void +term_map_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !tty->cmap[tty->next]) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->cmap[tty->next]); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} + +/* + * term_user_key -- + * Get the next key, but require the user enter one. + */ +enum input +term_user_key(sp, chp) + SCR *sp; + CH *chp; +{ + enum input rval; + IBUF *tty; + int nr; + + /* + * Read any keys the user has waiting. Make the race + * condition as short as possible. + */ + if (rval = term_key_queue(sp)) + return (rval); + + /* Wait and read another key. */ + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + + /* Fill in the return information. */ + tty = sp->gp->tty; + chp->ch = tty->ch[tty->next + (tty->cnt - 1)]; + chp->flags = 0; + chp->value = term_key_val(sp, chp->ch); + + QREM_TAIL(tty, 1); + return (INP_OK); +} + +/* + * term_key_queue -- + * Read the keys off of the terminal queue until it's empty. + */ +int +term_key_queue(sp) + SCR *sp; +{ + enum input rval; + struct timeval t; + IBUF *tty; + int nr; + + t.tv_sec = 0; + t.tv_usec = 0; + for (tty = sp->gp->tty;;) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, &t)) + return (rval); + if (nr == 0) + break; + } + return (INP_OK); +} + +/* + * term_key_ch -- + * Fill in the key for a value. + */ +int +term_key_ch(sp, val, chp) + SCR *sp; + int val; + CHAR_T *chp; +{ + KEYLIST *kp; + + for (kp = keylist;; ++kp) + if (kp->value == val) { + *chp = kp->ch; + return (0); + } + return (1); +} + +/* + * __term_key_val -- + * Fill in the value for a key. This routine is the backup + * for the term_key_val() macro. + */ +int +__term_key_val(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + KEYLIST k, *kp; + + k.ch = ch; + kp = bsearch(&k, keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + return (kp == NULL ? 0 : kp->value); +} + +/* + * __term_read_grow -- + * Grow the terminal queue. This routine is the backup for + * the term_read_grow() macro. + */ +static int +__term_read_grow(sp, tty) + SCR *sp; + IBUF *tty; +{ + size_t alen, len, nlen; + + nlen = tty->len + 64; + alen = tty->len - (tty->next + tty->cnt); + + len = tty->len; + BINC_RET(sp, tty->ch, len, nlen * sizeof(tty->ch[0])); + memset(tty->ch + tty->next + tty->cnt, 0, alen * sizeof(tty->ch[0])); + + len = tty->len; + BINC_RET(sp, tty->chf, len, nlen * sizeof(tty->chf[0])); + memset(tty->chf + tty->next + tty->cnt, 0, alen * sizeof(tty->chf[0])); + + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + memset(tty->cmap + + tty->next + tty->cnt, 0, alen * sizeof(tty->cmap[0])); + return (0); +} + +static int +keycmp(ap, bp) + const void *ap, *bp; +{ + return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch); +} diff --git a/usr.bin/vi/term.h b/usr.bin/vi/term.h new file mode 100644 index 0000000000..67f7f5d282 --- /dev/null +++ b/usr.bin/vi/term.h @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)term.h 8.26 (Berkeley) 1/7/94 + */ + +/* Structure to return a character and associated information. */ +struct _ch { + CHAR_T ch; /* Character. */ + +#define K_CARAT 1 +#define K_CNTRLR 2 +#define K_CNTRLT 3 +#define K_CNTRLZ 4 +#define K_COLON 5 +#define K_CR 6 +#define K_ESCAPE 7 +#define K_FORMFEED 8 +#define K_NL 9 +#define K_RIGHTBRACE 10 +#define K_RIGHTPAREN 11 +#define K_TAB 12 +#define K_VEOF 13 +#define K_VERASE 14 +#define K_VINTR 15 +#define K_VKILL 16 +#define K_VLNEXT 17 +#define K_VWERASE 18 +#define K_ZERO 19 + u_char value; /* Special character flag values. */ + +#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */ +#define CH_NOMAP 0x02 /* Do not attempt to map the character. */ +#define CH_QUOTED 0x04 /* Character is already quoted. */ + u_char flags; +}; + +/* + * Structure for the key input buffer. + * + * MAX_MAP_COUNT was chosen based on the vi maze script, which remaps + * characters roughly 250 times. + */ +struct _ibuf { + CHAR_T *ch; /* Array of characters. */ + u_char *chf; /* Array of character flags (CH_*). */ +#define MAX_MAP_COUNT 270 /* Maximum times a character can remap. */ + u_char *cmap; /* Number of times character has been mapped. */ + + size_t cnt; /* Count of remaining characters. */ + size_t len; /* Array length. */ + size_t next; /* Offset of next array entry. */ +}; + /* Return if more keys in queue. */ +#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt) +#define MAPPED_KEYS_WAITING(sp) \ + (KEYS_WAITING(sp) && sp->gp->tty->cmap[sp->gp->tty->next]) + +/* + * Structure to name a character. Used both as an interface to the + * screen and to name objects named by characters in error messages. + */ +struct _chname { + char *name; /* Character name. */ + u_char len; /* Length of the character name. */ +}; + +/* + * Routines that return a key as a side-effect return: + * + * INP_OK Returning a character; must be 0. + * INP_EOF EOF. + * INP_ERR Error. + * + * The vi structure depends on the key routines being able to return INP_EOF + * multiple times without failing -- eventually enough things will end due to + * INP_EOF that vi will reach the command level for the screen, at which point + * the exit flags will be set and vi will exit. + */ +enum input { INP_OK=0, INP_EOF, INP_ERR }; + +/* + * Routines that return a confirmation return: + * + * CONF_NO User answered no. + * CONF_QUIT User answered quit, eof or an error. + * CONF_YES User answered yes. + */ +enum confirm { CONF_NO, CONF_QUIT, CONF_YES }; + +/* + * Ex/vi commands are generally separated by whitespace characters. We + * can't use the standard isspace(3) macro because it returns true for + * characters like ^K in the ASCII character set. The 4.4BSD isblank(3) + * macro does exactly what we want, but it's not portable yet. + * + * XXX + * Note side effect, ch is evaluated multiple times. + */ +#ifndef isblank +#define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +/* Various special characters, messages. */ +#define CURSOR_CH ' ' /* Cursor character. */ +#define END_CH '$' /* End of a range. */ +#define HEX_CH 'x' /* Leading hex number. */ +#define NOT_DIGIT_CH 'a' /* A non-isdigit() character. */ +#define NO_CH 'n' /* No. */ +#define QUIT_CH 'q' /* Quit. */ +#define YES_CH 'y' /* Yes. */ +#define CONFSTRING "confirm? [ynq]" +#define CONTMSG "Enter return to continue: " +#define CONTMSG_I "Enter return to continue [q to quit]: " + +/* Flags describing how input is handled. */ +#define TXT_AICHARS 0x000001 /* Leading autoindent chars. */ +#define TXT_ALTWERASE 0x000002 /* Option: altwerase. */ +#define TXT_APPENDEOL 0x000004 /* Appending after EOL. */ +#define TXT_AUTOINDENT 0x000008 /* Autoindent set this line. */ +#define TXT_BEAUTIFY 0x000010 /* Only printable characters. */ +#define TXT_BS 0x000020 /* Backspace returns the buffer. */ +#define TXT_CNTRLT 0x000040 /* Control-T is an indent special. */ +#define TXT_CR 0x000080 /* CR returns the buffer. */ +#define TXT_EMARK 0x000100 /* End of replacement mark. */ +#define TXT_ESCAPE 0x000200 /* Escape returns the buffer. */ +#define TXT_INFOLINE 0x000400 /* Editing the info line. */ +#define TXT_MAPCOMMAND 0x000800 /* Apply the command map. */ +#define TXT_MAPINPUT 0x001000 /* Apply the input map. */ +#define TXT_MAPNODIGIT 0x002000 /* Return to a digit. */ +#define TXT_NLECHO 0x004000 /* Echo the newline. */ +#define TXT_OVERWRITE 0x008000 /* Overwrite characters. */ +#define TXT_PROMPT 0x010000 /* Display a prompt. */ +#define TXT_RECORD 0x020000 /* Record for replay. */ +#define TXT_REPLACE 0x040000 /* Replace; don't delete overwrite. */ +#define TXT_REPLAY 0x080000 /* Replay the last input. */ +#define TXT_RESOLVE 0x100000 /* Resolve the text into the file. */ +#define TXT_SHOWMATCH 0x200000 /* Option: showmatch. */ +#define TXT_TTYWERASE 0x400000 /* Option: ttywerase. */ +#define TXT_WRAPMARGIN 0x800000 /* Option: wrapmargin. */ + +#define TXT_VALID_EX \ + (TXT_BEAUTIFY | TXT_CR | TXT_NLECHO | TXT_PROMPT) + +/* Support keyboard routines. */ +int __term_key_val __P((SCR *, ARG_CHAR_T)); +void term_ab_flush __P((SCR *, char *)); +int term_init __P((SCR *)); +enum input term_key __P((SCR *, CH *, u_int)); +int term_key_ch __P((SCR *, int, CHAR_T *)); +int term_key_queue __P((SCR *)); +void term_map_flush __P((SCR *, char *)); +int term_push __P((SCR *, CHAR_T *, size_t, u_int, u_int)); +enum input term_user_key __P((SCR *, CH *)); +int term_waiting __P((SCR *)); diff --git a/usr.bin/vi/timer.c b/usr.bin/vi/timer.c new file mode 100644 index 0000000000..e2bcfb1c88 --- /dev/null +++ b/usr.bin/vi/timer.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)timer.c 8.7 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include + +#include "vi.h" + +static void busy_handler __P((int)); + +/* + * XXX + * There are two uses of timers in nvi. The first is to push the recovery + * information out to disk at periodic intervals. The second is to display + * a "busy" message if an operation takes too long. Rather than solve this + * in a general fashion, we depend on the fact that only a single screen in + * a window is active at a time, and that there are only two parts of the + * systems that use timers. + * + * It would be nice to reimplement this with multiple timers, a la POSIX + * 1003.1, but not many systems offer them yet. + */ + +/* + * busy_on -- + * Display a message if too much time passes. + */ +void +busy_on(sp, seconds, msg) + SCR *sp; + int seconds; + char const *msg; +{ + struct itimerval value; + struct sigaction act; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* Turn off the current timer, saving its current value. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &sp->time_value)) + return; + + /* + * Decrement the original timer by the number of seconds + * we're going to wait. + */ + if (sp->time_value.it_value.tv_sec > seconds) + sp->time_value.it_value.tv_sec -= seconds; + else + sp->time_value.it_value.tv_sec = 1; + + /* Reset the handler, saving its current value. */ + act.sa_handler = busy_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, &sp->time_handler); + + /* Reset the timer. */ + value.it_value.tv_sec = seconds; + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + + sp->time_msg = msg; + F_SET(sp, S_TIMER_SET); +} + +/* + * busy_off -- + * Reset the timer handlers. + */ +void +busy_off(sp) + SCR *sp; +{ + struct itimerval ovalue, value; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* If the timer flag isn't set, it must have fired. */ + if (!F_ISSET(sp, S_TIMER_SET)) + return; + + /* Ignore it if first on one of following system calls. */ + F_CLR(sp, S_TIMER_SET); + + /* Turn off the current timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &ovalue)) + return; + + /* If the timer wasn't running, we're done. */ + if (sp->time_handler.sa_handler == SIG_DFL) + return; + + /* + * Increment the old timer by the number of seconds + * remaining in the new one. + */ + sp->time_value.it_value.tv_sec += ovalue.it_value.tv_sec; + + /* Reset the handler to the original handler. */ + (void)sigaction(SIGALRM, &sp->time_handler, NULL); + + /* Reset the timer. */ + (void)setitimer(ITIMER_REAL, &sp->time_value, NULL); +} + +/* + * busy_handler -- + * Display a message when the timer goes off, and restore the + * timer to its original values. + */ +static void +busy_handler(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_TIMER_SET)) { + sp->s_busy(sp, sp->time_msg); + busy_off(sp); + } +} diff --git a/usr.bin/vi/trace.c b/usr.bin/vi/trace.c new file mode 100644 index 0000000000..f97e47bdc7 --- /dev/null +++ b/usr.bin/vi/trace.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trace.c 8.1 (Berkeley) 6/9/93 + */ + +#ifdef DEBUG +#include + +#include "vi.h" + +#ifdef __STDC__ +#include +#else +#include +#endif + +void +#ifdef __STDC__ +TRACE(SCR *sp, const char *fmt, ...) +#else +TRACE(sp, fmt, va_alist) + SCR *sp; + char *fmt; + va_dcl +#endif +{ + FILE *tfp; + va_list ap; + + if ((tfp = sp->gp->tracefp) == NULL) + return; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(tfp, fmt, ap); + va_end(ap); + + (void)fflush(tfp); +} +#endif diff --git a/usr.bin/vi/util.c b/usr.bin/vi/util.c new file mode 100644 index 0000000000..ab7161012a --- /dev/null +++ b/usr.bin/vi/util.c @@ -0,0 +1,570 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)util.c 8.34 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#include "vi.h" + +/* + * msgq -- + * Display a message. + */ +void +#ifdef __STDC__ +msgq(SCR *sp, enum msgtype mt, const char *fmt, ...) +#else +msgq(sp, mt, fmt, va_alist) + SCR *sp; + enum msgtype mt; + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char msgbuf[1024]; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + /* + * It's possible to enter msg when there's no screen to hold + * the message. Always check sp before using it, and, if it's + * NULL, use __global_list. + */ + switch (mt) { + case M_BERR: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) { + F_SET(sp, S_BELLSCHED); + return; + } + mt = M_ERR; + break; + case M_VINFO: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) + return; + mt = M_INFO; + /* FALLTHROUGH */ + case M_INFO: + if (F_ISSET(sp, S_EXSILENT)) + return; + break; + case M_ERR: + case M_SYSERR: + break; + default: + abort(); + } + + /* Length is the min length of the message or the buffer. */ + if (mt == M_SYSERR) + if (sp->if_name != NULL) + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno, + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s%s%s.", + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else { + len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf), + "%s, %d: ", sp->if_name, sp->if_lno); + len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap); + } + + /* + * If len >= the size, some characters were discarded. + * Ignore trailing nul. + */ + if (len >= sizeof(msgbuf)) + len = sizeof(msgbuf) - 1; + + msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len); +} + +/* + * msg_app -- + * Append a message into the queue. This can fail, but there's + * nothing we can do if it does. + */ +void +msg_app(gp, sp, inv_video, p, len) + GS *gp; + SCR *sp; + int inv_video; + char *p; + size_t len; +{ + static int reenter; /* STATIC: Re-entrancy check. */ + MSG *mp, *nmp; + + /* + * It's possible to reenter msg when it allocates space. + * We're probably dead anyway, but no reason to drop core. + */ + if (reenter) + return; + reenter = 1; + + /* + * Find an empty structure, or allocate a new one. Use the + * screen structure if possible, otherwise the global one. + */ + if (sp != NULL) { + if ((mp = sp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&sp->msgq, mp, q); + goto store; + } + } else if ((mp = gp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&gp->msgq, mp, q); + goto store; + } + while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL) + mp = mp->q.le_next; + if (!F_ISSET(mp, M_EMPTY)) { + CALLOC(sp, nmp, MSG *, 1, sizeof(MSG)); + if (nmp == NULL) + goto ret; + LIST_INSERT_AFTER(mp, nmp, q); + mp = nmp; + } + + /* Get enough memory for the message. */ +store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len)) + goto ret; + + /* Store the message. */ + memmove(mp->mbuf, p, len); + mp->len = len; + mp->flags = inv_video ? M_INV_VIDEO : 0; + +ret: reenter = 0; +} + +/* + * msgrpt -- + * Report on the lines that changed. + * + * !!! + * Historic vi documentation (USD:15-8) claimed that "The editor will also + * always tell you when a change you make affects text which you cannot see." + * This isn't true -- edit a large file and do "100d|1". We don't implement + * this semantic as it would require that we track each line that changes + * during a command instead of just keeping count. + */ +int +msg_rpt(sp, is_message) + SCR *sp; + int is_message; +{ + static const char *const action[] = { + "added", "changed", "copied", "deleted", "joined", "moved", + "put", "left shifted", "right shifted", "yanked", NULL, + }; + recno_t total; + u_long rval; + int first, cnt; + size_t blen, len; + const char *const *ap; + char *bp, *p, number[40]; + + if (F_ISSET(sp, S_EXSILENT)) + return (0); + + if ((rval = O_VAL(sp, O_REPORT)) == 0) + goto norpt; + + GET_SPACE_RET(sp, bp, blen, 512); + p = bp; + + total = 0; + for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt) + if (sp->rptlines[cnt] != 0) { + total += sp->rptlines[cnt]; + len = snprintf(number, sizeof(number), + "%s%lu line%s %s", first ? "" : "; ", + sp->rptlines[cnt], + sp->rptlines[cnt] > 1 ? "s" : "", *ap); + memmove(p, number, len); + p += len; + first = 0; + } + + /* + * If nothing to report, return. Note that the number of lines + * must be > than the user's value, not >=. This is historic + * practice and means that users cannot report on single line + * changes. + */ + if (total > rval) { + *p = '\0'; + + if (is_message) + msgq(sp, M_INFO, "%s", bp); + else + ex_printf(EXCOOKIE, "%s\n", bp); + } + + FREE_SPACE(sp, bp, blen); + + /* Clear after each report. */ +norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines)); + return (0); +} + +/* + * binc -- + * Increase the size of a buffer. + */ +int +binc(sp, argp, bsizep, min) + SCR *sp; /* MAY BE NULL */ + void *argp; + size_t *bsizep, min; +{ + void *bpp; + size_t csize; + + /* If already larger than the minimum, just return. */ + csize = *bsizep; + if (min && csize >= min) + return (0); + + csize += MAX(min, 256); + bpp = *(char **)argp; + + /* For non-ANSI C realloc implementations. */ + if (bpp == NULL) + bpp = malloc(csize * sizeof(CHAR_T)); + else + bpp = realloc(bpp, csize * sizeof(CHAR_T)); + if (bpp == NULL) { + msgq(sp, M_SYSERR, NULL); + *bsizep = 0; + return (1); + } + *(char **)argp = bpp; + *bsizep = csize; + return (0); +} + +/* + * nonblank -- + * Set the column number of the first non-blank character + * including or after the starting column. On error, set + * the column to 0, it's safest. + */ +int +nonblank(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + char *p; + size_t cnt, len, off; + + /* Default. */ + off = *cnop; + *cnop = 0; + + /* Get the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + return (0); + GETLINE_ERR(sp, lno); + return (1); + } + + /* Set the offset. */ + if (len == 0 || off >= len) + return (0); + + for (cnt = off, p = &p[off], + len -= off; len && isblank(*p); ++cnt, ++p, --len); + + /* Set the return. */ + *cnop = len ? cnt : cnt - 1; + return (0); +} + +/* + * tail -- + * Return tail of a path. + */ +char * +tail(path) + char *path; +{ + char *p; + + if ((p = strrchr(path, '/')) == NULL) + return (path); + return (p + 1); +} + +/* + * set_window_size -- + * Set the window size, the row may be provided as an argument. + */ +int +set_window_size(sp, set_row, ign_env) + SCR *sp; + u_int set_row; + int ign_env; +{ + struct winsize win; + size_t col, row; + int user_set; + ARGS *argv[2], a, b; + char *s, buf[2048]; + + /* + * Get the screen rows and columns. If the values are wrong, it's + * not a big deal -- as soon as the user sets them explicitly the + * environment will be set and the screen package will use the new + * values. + * + * Try TIOCGWINSZ. + */ + row = col = 0; +#ifdef TIOCGWINSZ + if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { + row = win.ws_row; + col = win.ws_col; + } +#endif + + /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */ + if (row == 0 || col == 0) { + s = NULL; + if (F_ISSET(&sp->opts[O_TERM], OPT_SET)) + s = O_STR(sp, O_TERM); + else + s = getenv("TERM"); + if (s != NULL && tgetent(buf, s) == 1) { + if (row == 0) + row = tgetnum("li"); + if (col == 0) + col = tgetnum("co"); + } + } + /* If nothing else, well, it's probably a VT100. */ + if (row == 0) + row = 24; + if (col == 0) + col = 80; + + /* + * POSIX 1003.2 requires the environment to override, however, + * if we're here because of a signal, we don't want to use the + * old values. + */ + if (!ign_env) { + if ((s = getenv("LINES")) != NULL) + row = strtol(s, NULL, 10); + if ((s = getenv("COLUMNS")) != NULL) + col = strtol(s, NULL, 10); + } + + /* But, if we got an argument for the rows, use it. */ + if (set_row) + row = set_row; + + a.bp = buf; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b;; + + /* + * Tell the options code that the screen size has changed. + * Since the user didn't do the set, clear the set bits. + */ + user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "lines=%u", row); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_LINES], OPT_SET); + user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "columns=%u", col); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_COLUMNS], OPT_SET); + return (0); +} + +/* + * set_alt_name -- + * Set the alternate file name. + * + * Swap the alternate file name. It's a routine because I wanted some place + * to hang this comment. The alternate file name (normally referenced using + * the special character '#' during file expansion) is set by many + * operations. In the historic vi, the commands "ex", and "edit" obviously + * set the alternate file name because they switched the underlying file. + * Less obviously, the "read", "file", "write" and "wq" commands set it as + * well. In this implementation, some new commands have been added to the + * list. Where it gets interesting is that the alternate file name is set + * multiple times by some commands. If an edit attempt fails (for whatever + * reason, like the current file is modified but as yet unwritten), it is + * set to the file name that the user was unable to edit. If the edit + * succeeds, it is set to the last file name that was edited. Good fun. + * + * If the user edits a temporary file, there are time when there isn't an + * alternative file name. A name argument of NULL turns it off. + */ +void +set_alt_name(sp, name) + SCR *sp; + char *name; +{ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + if (name == NULL) + sp->alt_name = NULL; + else if ((sp->alt_name = strdup(name)) == NULL) + msgq(sp, M_SYSERR, NULL); +} + +/* + * baud_from_bval -- + * Return the baud rate using the standard defines. + */ +u_long +baud_from_bval(sp) + SCR *sp; +{ + speed_t v; + + switch (v = cfgetospeed(&sp->gp->original_termios)) { + case B50: + return (50); + case B75: + return (75); + case B110: + return (110); + case B134: + return (134); + case B150: + return (150); + case B200: + return (200); + case B300: + return (300); + case B600: + return (600); + case B1200: + return (1200); + case B1800: + return (1800); + case B2400: + return (2400); + case B4800: + return (4800); + case B0: /* Hangup -- ignore. */ + case B9600: + return (9600); + case B19200: + return (19200); + case B38400: + return (38400); + default: + /* + * EXTA and EXTB aren't required by POSIX 1003.1, and + * are almost certainly the same as some of the above + * values, so they can't be part of the case statement. + */ +#ifdef EXTA + if (v == EXTA) + return (19200); +#endif +#ifdef EXTB + if (v == EXTB) + return (38400); +#endif + msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v); + return (9600); + } +} + +/* + * v_strdup -- + * Strdup for wide character strings with an associated length. + */ +CHAR_T * +v_strdup(sp, str, len) + SCR *sp; + CHAR_T *str; + size_t len; +{ + CHAR_T *copy; + + MALLOC(sp, copy, CHAR_T *, len); + if (copy == NULL) + return (NULL); + memmove(copy, str, len * sizeof(CHAR_T)); + return (copy); +} diff --git a/usr.bin/vi/vi.h b/usr.bin/vi/vi.h new file mode 100644 index 0000000000..c36dc8b010 --- /dev/null +++ b/usr.bin/vi/vi.h @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vi.h 8.33 (Berkeley) 1/9/94 + */ + +/* System includes. */ +#include /* Required by screen.h. */ +#include /* Required by screen.h. */ + +#include /* Required by screen.h. */ +#include /* Required by screen.h. */ +#include /* Required by screen.h. */ +#include /* Required by screen.h. */ +#include /* Required by gs.h. */ + +/* + * Required by screen.h. This is the first include that can pull + * in "compat.h". Should be after every other system include. + */ +#include + +/* + * Forward structure declarations. Not pretty, but the include files + * are far too interrelated for a clean solution. + */ +typedef struct _cb CB; +typedef struct _ch CH; +typedef struct _chname CHNAME; +typedef struct _excmdarg EXCMDARG; +typedef struct _exf EXF; +typedef struct _fref FREF; +typedef struct _gs GS; +typedef struct _ibuf IBUF; +typedef struct _mark MARK; +typedef struct _msg MSG; +typedef struct _option OPTION; +typedef struct _optlist OPTLIST; +typedef struct _scr SCR; +typedef struct _script SCRIPT; +typedef struct _seq SEQ; +typedef struct _tag TAG; +typedef struct _tagf TAGF; +typedef struct _text TEXT; + +/* + * Fundamental character types. + * + * CHAR_T An integral type that can hold any character. + * ARG_CHAR_T The type of a CHAR_T when passed as an argument using + * traditional promotion rules. It should also be able + * to be compared against any CHAR_T for equality without + * problems. + * MAX_CHAR_T The maximum value of any character. + * + * If no integral type can hold a character, don't even try the port. + */ +typedef u_char CHAR_T; +typedef u_int ARG_CHAR_T; +#define MAX_CHAR_T 0xff + +/* The maximum number of columns any character can take up on a screen. */ +#define MAX_CHARACTER_COLUMNS 4 + +/* + * Local includes. + */ +#include /* Required by exf.h; includes compat.h. */ + +#include "search.h" /* Required by screen.h. */ +#include "args.h" /* Required by options.h. */ +#include "options.h" /* Required by screen.h. */ +#include "term.h" /* Required by screen.h. */ + +#include "msg.h" /* Required by gs.h. */ +#include "cut.h" /* Required by gs.h. */ +#include "gs.h" /* Required by screen.h. */ +#include "screen.h" /* Required by exf.h. */ +#include "mark.h" /* Required by exf.h. */ +#include "exf.h" +#include "log.h" +#include "mem.h" + +#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */ +#define EXCOOKIE sp +int ex_fflush __P((SCR *)); +int ex_printf __P((SCR *, const char *, ...)); +FILE *fwopen __P((SCR *, void *)); +#else +#define EXCOOKIE sp->stdfp +#define ex_fflush fflush +#define ex_printf fprintf +#endif + +/* Macros to set/clear/test flags. */ +#define F_SET(p, f) (p)->flags |= (f) +#define F_CLR(p, f) (p)->flags &= ~(f) +#define F_ISSET(p, f) ((p)->flags & (f)) + +#define LF_INIT(f) flags = (f) +#define LF_SET(f) flags |= (f) +#define LF_CLR(f) flags &= ~(f) +#define LF_ISSET(f) (flags & (f)) + +/* + * XXX + * MIN/MAX have traditionally been in . Don't + * try to get them from there, it's just not worth the effort. + */ +#ifndef MAX +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Function prototypes that don't seem to belong anywhere else. */ +u_long baud_from_bval __P((SCR *)); +char *charname __P((SCR *, ARG_CHAR_T)); +void busy_off __P((SCR *)); +void busy_on __P((SCR *, int, char const *)); +int nonblank __P((SCR *, EXF *, recno_t, size_t *)); +void set_alt_name __P((SCR *, char *)); +int set_window_size __P((SCR *, u_int, int)); +int status __P((SCR *, EXF *, recno_t, int)); +char *tail __P((char *)); +CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t)); + +#ifdef DEBUG +void TRACE __P((SCR *, const char *, ...)); +#endif + +/* Digraphs (not currently real). */ +int digraph __P((SCR *, int, int)); +int digraph_init __P((SCR *)); +void digraph_save __P((SCR *, int)); diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c new file mode 100644 index 0000000000..ec76a0668c --- /dev/null +++ b/usr.bin/vi/xaw/xaw_screen.c @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)xaw_screen.c 8.4 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include "vi.h" + +/* + * xaw_init -- + * Athena widget screen initialization. + */ +int +xaw_screen_init(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "The Athena widget screen not yet implemented."); + return (1); +} + +/* + * xaw_screen_copy -- + * Copy to a new screen. + */ +int +xaw_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * xaw_screen_end -- + * End a screen. + */ +int +xaw_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * xaw -- + * Main vi Athena widget screen loop. + */ +int +xaw(sp, ep, spp) + SCR *sp, **spp; + EXF *ep; +{ + *spp = NULL; + return (0); +} -- 2.20.1