diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /usr.bin/vi | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'usr.bin/vi')
156 files changed, 39241 insertions, 0 deletions
diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile new file mode 100644 index 000000000000..fbb1588cebd8 --- /dev/null +++ b/usr.bin/vi/Makefile @@ -0,0 +1,99 @@ +# @(#)Makefile 8.26 (Berkeley) 1/12/94 + +PROG= vi +MAN1= vi.1 +BINDIR?= /usr/bin + +#CFLAGS=-g -DDEBUG +#CFLAGS+=-pg +CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/obj -I${.CURDIR}/include -I${.CURDIR}/nex -I${.CURDIR}/nvi +#STRIP= +.PATH: ${.CURDIR}/nex ${.CURDIR}/sex ${.CURDIR}/nvi ${.CURDIR}/svi \ + ${.CURDIR}/xaw +CLEANFILES+=ex + +# General sources. +SRCS= ascii.c cut.c delete.c exf.c line.c log.c main.c mark.c \ + options.c options_f.c screen.c search.c seq.c recover.c \ + term.c timer.c trace.c util.c + +# Ex source. +SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c \ + ex_bang.c ex_cd.c ex_delete.c ex_digraph.c ex_display.c \ + ex_edit.c ex_equal.c ex_exit.c ex_file.c ex_global.c ex_init.c \ + ex_join.c ex_map.c ex_mark.c ex_mkexrc.c ex_move.c ex_open.c \ + ex_preserve.c ex_print.c ex_put.c ex_read.c ex_screen.c \ + ex_script.c ex_set.c ex_shell.c ex_shift.c ex_source.c ex_stop.c \ + ex_subst.c ex_tag.c ex_undo.c ex_usage.c ex_util.c ex_version.c \ + ex_visual.c ex_write.c ex_yank.c ex_z.c excmd.c filter.c + +# Ex screen source. +SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \ + sex_util.c + +# Vi source. +SRCS+= getc.c v_again.c v_at.c v_ch.c v_delete.c v_ex.c v_exit.c \ + v_exmode.c v_filter.c v_increment.c v_init.c v_join.c v_left.c \ + v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \ + v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \ + v_sentence.c v_shift.c v_status.c v_stop.c v_switch.c v_tag.c \ + v_text.c v_ulcase.c v_undo.c v_util.c v_word.c v_xchar.c v_yank.c \ + v_z.c vcmd.c vi.c + +# Vi curses screen source. +SRCS+= svi_confirm.c svi_ex.c svi_get.c svi_line.c svi_refresh.c \ + svi_relative.c svi_screen.c svi_smap.c svi_split.c svi_util.c + +# Athena widget set screen source. +SRCS+= xaw_screen.c + +#LDADD+=-pg +DPADD+= ${LIBCURSES} ${LIBTERM} ${LIBUTIL} +LDADD+= -lcurses -ltermlib -lutil +SPECHDR=excmd.h options.h +CLEANFILES+=${SPECHDR} +DPSRCS+=${SPECHDR} +LINKS= ${BINDIR}/vi ${BINDIR}/ex ${BINDIR}/vi ${BINDIR}/view + +all: vi vi.1 + +warn:: ${SRCS} + -(cd ${.CURDIR} && \ + gcc -Wall -O -DDEBUG -Iobj -Invi -Inex -I. ${.ALLSRC} \ + -lcurses -ltermlib 2>&1 | \ + sed -e "/warning: .*sccsid.*defined but not used/d" \ + -e "/warning: suggest parentheses around/d" \ + -e "/In function /d" \ + -e "/At top level:/d" \ + -e "/warning: .*inline call to/d" \ + -e "/warning: comparison is always 1 due /d") > \ + ${.CURDIR}/WARN.OUT + +options.h: options.h.stub options.c # Makefile + rm -f options.h + cp ${.CURDIR}/options.h.stub options.h + chmod 664 options.h + (echo '/^\/\* O_[0-9A-Z_]*/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}'; \ + echo 'END {'; \ + echo 'printf("#define O_OPTIONCOUNT %d\n", cnt)'; \ + echo '}') > /tmp/__vi.options.h + awk -f /tmp/__vi.options.h ${.CURDIR}/options.c >> options.h + rm -f /tmp/__vi.options.h + +excmd.h: excmd.h.stub excmd.c # Makefile + rm -f excmd.h + cp ${.CURDIR}/nex/excmd.h.stub excmd.h + chmod 664 excmd.h + (echo '/^\/\* C_[0-9A-Z_]* \*\/$$/ {'; \ + echo 'printf("#define %s %d\n", $$2, cnt++)'; \ + echo 'next'; \ + echo '}') > /tmp/__vi.excmd.h + awk -f /tmp/__vi.excmd.h ${.CURDIR}/nex/excmd.c >> excmd.h + rm -f /tmp/__vi.excmd.h + +.include <bsd.prog.mk> + +.depend: ${SPECHDR} diff --git a/usr.bin/vi/README b/usr.bin/vi/README new file mode 100644 index 000000000000..a33bfc6f04c3 --- /dev/null +++ b/usr.bin/vi/README @@ -0,0 +1,193 @@ +# @(#)README 8.40 (Berkeley) 1/12/94 + +This is the README for version 1.01 of nvi, a freely redistributable +replacement for the vi and ex text editors. It can be retrieved via +anonymous ftp from ftp.uu.net, or from ftp.cs.berkeley.edu. In the +latter, it is in the directory ucb/4bsd, and is named nvi.tar.Z. + +If you have any questions or problems with nvi, please send them to +me by electronic mail at one of the following addresses: + + uunet!bostic + bostic@cs.berkeley.edu + +Keith Bostic + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +o Redistribution: + +Nvi is copyrighted by the The Regents of the University of California, +but may be freely redistributed (or used to line your birdcage) under +the following conditions: + +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +o Credit where it's due: + + This software was originally derived from software contributed + to the University of California, Berkeley by Steve Kirkendall, + the author of the vi clone elvis. Without his work, this work + would have been far more difficult. + + POSIX 1003.2 style regular expression support is courtesy of + Henry Spencer, for which I am *very* grateful. + + The curses library was originally done by Ken Arnold. Scrolling + and general reworking for 4.4BSD was done by Elan Amir. + +o From the original vi acknowledgements, by William Joy and Mark Horton: + + Bruce Englar encouraged the early development of this display + editor. Peter Kessler helped bring sanity to version 2's + command layout. Bill Joy wrote versions 1 and 2.0 through 2.7, + and created the framework that users see in the present editor. + Mark Horton added macros and other features and made the editor + work on a large number of terminals and Unix systems. + +o And... + + The financial support of UUNET Communications Services is gratefully + acknowledged. + +=-=-=-=-=-=-=-=-=-=-= +o Comments: + +This software is beta software, although it's pretty stable. Almost +of the necessary functionality for ex/vi is in it, the only missing +pieces are fairly obscure. + +Code fixes are very much appreciated, of course, but if you can't +provide them, please send me as much information as you can as to how +to reproduce the bug, and I'll try to fix it here. In particular, the +screen routines are nasty stuff, and you probably don't want to mess +with them. Stack traces of core dumps are sometimes helpful, but an +example file with a set of keystrokes that causes the problem is far +better. + +Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left in +during initial development to make things easier. Wide character support +will be integrated at the same time it is made fully 8-bit clean. + +=-=-=-=-=-=-=-=-=-=-= +o New features: + +There aren't a lot of new features in nex/nvi, but there are a few things +you might like: + + o 8-bit clean data, practically infinite lines/files. + ^Vx[0-9A-Fa-f]* in input mode will insert any + legal character value. + o Split screens: + :sp[lit] [file ...] splits the screen. + ^W switches between screens. + :resize count grows/shrinks the current screen. + o Background/foreground screens + :bg backgrounds the current screen. + :di[splay] s[creens] lists the hidden screens. + :fg [file] foregrounds the specified (or next) screen. + o Shell screens: + :sc[ript] [file ...] runs a shell in the screen. + Carriage return sends a line to the shell. + o Buffer, screens, tags display: + :di[splay] b[uffers] displays the current cut buffers. + :di[splay] s[creens] displays the hidden screen names. + :di[splay] t[ags] displays the current tags stack. + o Tag stacks: + ^T returns to previous tag location. + :tagpop [number | file] returns to previous tag location, + or, optionally tag #N, or the tag in a specific file. + :tagtop returns to first tag location. + o Infinite undo: + A '.' command immediately after a 'u' command continues + either forward or backward depending on whether the 'u' + command was an undo or a redo. + o Usage information: + :exu[sage] [cmd] for ex commands. + :viu[sage] [key] for vi commands. + :help + o Extended RE expressions: + :set extended turns on extended RE's, so you can + do "/in|or" and search for the next occurrence of + more than one expression. + o Word search: + ^A searches for the word referenced by the cursor. + o Number increment: + # increments the number referenced by the cursor. + o Previous file: + :prev[ious][!] edits the previous file from the + argument list. + +=-=-=-=-=-=-=-=-=-=-= +o Porting information: + +The directory PORT has directories per machine/OS combination, with +V7-style Makefiles which build nvi. See the file PORT/README for +more detailed information. + +=-=-=-=-=-=-=-=-=-=-= +o Directories: + +The main directory, nvi, contains source files for pieces of code that +are shared by all the editors, like searching and logging code or code +translating line numbers into requests to the dbopen(3) database code. +It also has the code for adding, deleting, and changing "records" in +the underlying database. + +nvi/docs: + The nvi/docs directory has technical information about data + structures and some of the trickier parts of vi like quoting, + key mapping, input queues, and executing buffers, and a + description of how nvi does edit session recovery. + +nvi/ex: + The nvi/ex directory is the ex source code. Because vi has the + colon command, lots of this code is used by vi. Generally, if + functionality is shared by both ex and vi, it's in nvi/ex, if + it's vi only, it's in nvi/vi. Files are generally named by the + command(s) they support, but occasionally with a name that + describes their functionality. + +nvi/sex: + The nvi/sex directory is the screen support for the ex editor. + +nvi/svi: + The nvi/svi directory is the screen support for a curses based + vi editor. + +nvi/vi: + The nvi/vi directory is the vi source code. + +nvi/xaw: + Place reserved for an X11 (Athena Widget) screen. diff --git a/usr.bin/vi/args.h b/usr.bin/vi/args.h new file mode 100644 index 000000000000..4d437442d866 --- /dev/null +++ b/usr.bin/vi/args.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)args.h 8.3 (Berkeley) 12/19/93 + */ + +/* + * Structure for building "argc/argv" vector of arguments. + * + * !!! + * All arguments are nul terminated as well as having an associated length. + * The argument vector is NOT necessarily NULL terminated. The proper way + * to check the number of arguments is to use the argc value in the EXCMDARG + * structure or to walk the array until an ARGS structure with a length of 0 + * is found. + */ +typedef struct _args { + CHAR_T *bp; /* Argument. */ + size_t blen; /* Buffer length. */ + size_t len; /* Argument length. */ + +#define A_ALLOCATED 0x01 /* If allocated space. */ + u_char flags; +} ARGS; diff --git a/usr.bin/vi/ascii.c b/usr.bin/vi/ascii.c new file mode 100644 index 000000000000..7a7102574165 --- /dev/null +++ b/usr.bin/vi/ascii.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ascii.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" + +CHNAME const asciiname[UCHAR_MAX + 1] = { + {"^@", 2}, {"^A", 2}, {"^B", 2}, {"^C", 2}, + {"^D", 2}, {"^E", 2}, {"^F", 2}, {"^G", 2}, + {"^H", 2}, {"^I", 2}, {"^J", 2}, {"^K", 2}, + {"^L", 2}, {"^M", 2}, {"^N", 2}, {"^O", 2}, + {"^P", 2}, {"^Q", 2}, {"^R", 2}, {"^S", 2}, + {"^T", 2}, {"^U", 2}, {"^V", 2}, {"^W", 2}, + {"^X", 2}, {"^Y", 2}, {"^Z", 2}, {"^[", 2}, + {"^\\", 2}, {"^]", 2}, {"^^", 2}, {"^_", 2}, + {" ", 1}, {"!", 1}, {"\"", 1}, {"#", 1}, + {"$", 1}, {"%", 1}, {"&", 1}, {"'", 1}, + {"(", 1}, {")", 1}, {"*", 1}, {"+", 1}, + {",", 1}, {"-", 1}, {".", 1}, {"/", 1}, + {"0", 1}, {"1", 1}, {"2", 1}, {"3", 1}, + {"4", 1}, {"5", 1}, {"6", 1}, {"7", 1}, + {"8", 1}, {"9", 1}, {":", 1}, {";", 1}, + {"<", 1}, {"=", 1}, {">", 1}, {"?", 1}, + {"@", 1}, {"A", 1}, {"B", 1}, {"C", 1}, + {"D", 1}, {"E", 1}, {"F", 1}, {"G", 1}, + {"H", 1}, {"I", 1}, {"J", 1}, {"K", 1}, + {"L", 1}, {"M", 1}, {"N", 1}, {"O", 1}, + {"P", 1}, {"Q", 1}, {"R", 1}, {"S", 1}, + {"T", 1}, {"U", 1}, {"V", 1}, {"W", 1}, + {"X", 1}, {"Y", 1}, {"Z", 1}, {"[", 1}, + {"\\", 1}, {"]", 1}, {"^", 1}, {"_", 1}, + {"`", 1}, {"a", 1}, {"b", 1}, {"c", 1}, + {"d", 1}, {"e", 1}, {"f", 1}, {"g", 1}, + {"h", 1}, {"i", 1}, {"j", 1}, {"k", 1}, + {"l", 1}, {"m", 1}, {"n", 1}, {"o", 1}, + {"p", 1}, {"q", 1}, {"r", 1}, {"s", 1}, + {"t", 1}, {"u", 1}, {"v", 1}, {"w", 1}, + {"x", 1}, {"y", 1}, {"z", 1}, {"{", 1}, + {"|", 1}, {"}", 1}, {"~", 1}, {"^?", 2}, + {"0x80", 4}, {"0x81", 4}, {"0x82", 4}, {"0x83", 4}, + {"0x84", 4}, {"0x85", 4}, {"0x86", 4}, {"0x87", 4}, + {"0x88", 4}, {"0x89", 4}, {"0x8a", 4}, {"0x8b", 4}, + {"0x8c", 4}, {"0x8d", 4}, {"0x8e", 4}, {"0x8f", 4}, + {"0x90", 4}, {"0x91", 4}, {"0x92", 4}, {"0x93", 4}, + {"0x94", 4}, {"0x95", 4}, {"0x96", 4}, {"0x97", 4}, + {"0x98", 4}, {"0x99", 4}, {"0x9a", 4}, {"0x9b", 4}, + {"0x9c", 4}, {"0x9d", 4}, {"0x9e", 4}, {"0x9f", 4}, + {"\xa0", 1}, {"\xa1", 1}, {"\xa2", 1}, {"\xa3", 1}, + {"\xa4", 1}, {"\xa5", 1}, {"\xa6", 1}, {"\xa7", 1}, + {"\xa8", 1}, {"\xa9", 1}, {"\xaa", 1}, {"\xab", 1}, + {"\xac", 1}, {"\xad", 1}, {"\xae", 1}, {"\xaf", 1}, + {"\xb0", 1}, {"\xb1", 1}, {"\xb2", 1}, {"\xb3", 1}, + {"\xb4", 1}, {"\xb5", 1}, {"\xb6", 1}, {"\xb7", 1}, + {"\xb8", 1}, {"\xb9", 1}, {"\xba", 1}, {"\xbb", 1}, + {"\xbc", 1}, {"\xbd", 1}, {"\xbe", 1}, {"\xbf", 1}, + {"\xc0", 1}, {"\xc1", 1}, {"\xc2", 1}, {"\xc3", 1}, + {"\xc4", 1}, {"\xc5", 1}, {"\xc6", 1}, {"\xc7", 1}, + {"\xc8", 1}, {"\xc9", 1}, {"\xca", 1}, {"\xcb", 1}, + {"\xcc", 1}, {"\xcd", 1}, {"\xce", 1}, {"\xcf", 1}, + {"\xd0", 1}, {"\xd1", 1}, {"\xd2", 1}, {"\xd3", 1}, + {"\xd4", 1}, {"\xd5", 1}, {"\xd6", 1}, {"\xd7", 1}, + {"\xd8", 1}, {"\xd9", 1}, {"\xda", 1}, {"\xdb", 1}, + {"\xdc", 1}, {"\xdd", 1}, {"\xde", 1}, {"\xdf", 1}, + {"\xe0", 1}, {"\xe1", 1}, {"\xe2", 1}, {"\xe3", 1}, + {"\xe4", 1}, {"\xe5", 1}, {"\xe6", 1}, {"\xe7", 1}, + {"\xe8", 1}, {"\xe9", 1}, {"\xea", 1}, {"\xeb", 1}, + {"\xec", 1}, {"\xed", 1}, {"\xee", 1}, {"\xef", 1}, + {"\xf0", 1}, {"\xf1", 1}, {"\xf2", 1}, {"\xf3", 1}, + {"\xf4", 1}, {"\xf5", 1}, {"\xf6", 1}, {"\xf7", 1}, + {"\xf8", 1}, {"\xf9", 1}, {"\xfa", 1}, {"\xfb", 1}, + {"\xfc", 1}, {"\xfd", 1}, {"\xfe", 1}, {"\xff", 1}, +}; + +char * +charname(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + return (sp->gp->cname[ch & UCHAR_MAX].name); +} diff --git a/usr.bin/vi/cut.c b/usr.bin/vi/cut.c new file mode 100644 index 000000000000..5a9b292ce175 --- /dev/null +++ b/usr.bin/vi/cut.c @@ -0,0 +1,526 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cut.c 8.20 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +static int cb_line __P((SCR *, EXF *, recno_t, size_t, size_t, TEXT **)); +static int cb_rotate __P((SCR *)); + +/* + * cut -- + * Put a range of lines/columns into a buffer. + * + * There are two buffer areas, both found in the global structure. The first + * is the linked list of all the buffers the user has named, the second is the + * default buffer storage. There is a pointer, too, which is the current + * default buffer, i.e. it may point to the default buffer or a named buffer + * depending on into what buffer the last text was cut. In both delete and + * yank operations, text is cut into either the buffer named by the user, or + * the default buffer. If it's a delete of information on more than a single + * line, the contents of the numbered buffers are rotated up one, the contents + * of the buffer named '9' are discarded, and the text is also cut into the + * buffer named '1'. + * + * In all cases, upper-case buffer names are the same as lower-case names, + * with the exception that they cause the buffer to be appended to instead + * of replaced. + * + * !!! + * The contents of the default buffer would disappear after most operations in + * historic vi. It's unclear that this is useful, so we don't bother. + * + * When users explicitly cut text into the numeric buffers, historic vi became + * genuinely strange. I've never been able to figure out what was supposed to + * happen. It behaved differently if you deleted text than if you yanked text, + * and, in the latter case, the text was appended to the buffer instead of + * replacing the contents. Hopefully it's not worth getting right. + */ +int +cut(sp, ep, cbp, namep, fm, tm, flags) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + int flags; + MARK *fm, *tm; +{ + CHAR_T name; + TEXT *tp; + recno_t lno; + size_t len; + int append, namedbuffer, setdefcb; + +#if defined(DEBUG) && 0 + TRACE(sp, "cut: from {%lu, %d}, to {%lu, %d}%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, + LF_ISSET(CUT_LINEMODE) ? " LINE MODE" : ""); +#endif + if (cbp == NULL) { + if (LF_ISSET(CUT_DELETE) && + (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) { + (void)cb_rotate(sp); + name = '1'; + goto defcb; + } + if (namep == NULL) { + cbp = sp->gp->dcb_store; + append = namedbuffer = 0; + setdefcb = 1; + } else { + name = *namep; +defcb: CBNAME(sp, cbp, name); + append = isupper(name); + namedbuffer = setdefcb = 1; + } + } else + append = namedbuffer = setdefcb = 0; + + /* + * If this is a new buffer, create it and add it into the list. + * Otherwise, if it's not an append, free its current contents. + */ + if (cbp == NULL) { + CALLOC(sp, cbp, CB *, 1, sizeof(CB)); + cbp->name = name; + CIRCLEQ_INIT(&cbp->textq); + if (namedbuffer) { + LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q); + } else + sp->gp->dcb_store = cbp; + } else if (!append) { + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + } + + /* In line mode, it's pretty easy, just cut the lines. */ + if (LF_ISSET(CUT_LINEMODE)) { + for (lno = fm->lno; lno <= tm->lno; ++lno) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + cbp->flags |= CB_LMODE; + } else { + /* Get the first line. */ + len = fm->lno < tm->lno ? 0 : tm->cno - fm->cno; + if (cb_line(sp, ep, fm->lno, fm->cno, len, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + + /* Get the intermediate lines. */ + for (lno = fm->lno; ++lno < tm->lno;) { + if (cb_line(sp, ep, lno, 0, 0, &tp)) + goto mem; + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + + /* Get the last line. */ + if (tm->lno > fm->lno && tm->cno > 0) { + if (cb_line(sp, ep, lno, 0, tm->cno, &tp)) { +mem: if (append) + msgq(sp, M_ERR, + "Contents of %s buffer lost.", + charname(sp, name)); + text_lfree(&cbp->textq); + cbp->len = 0; + cbp->flags = 0; + return (1); + } + CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q); + cbp->len += tp->len; + } + } + if (setdefcb) + sp->gp->dcbp = cbp; /* Repoint default buffer. */ + return (0); +} + +/* + * cb_rotate -- + * Rotate the numbered buffers up one. + */ +static int +cb_rotate(sp) + SCR *sp; +{ + CB *cbp, *del_cbp; + + del_cbp = NULL; + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) + switch(cbp->name) { + case '1': + cbp->name = '2'; + break; + case '2': + cbp->name = '3'; + break; + case '3': + cbp->name = '4'; + break; + case '4': + cbp->name = '5'; + break; + case '5': + cbp->name = '6'; + break; + case '6': + cbp->name = '7'; + break; + case '7': + cbp->name = '8'; + break; + case '8': + cbp->name = '9'; + break; + case '9': + del_cbp = cbp; + break; + } + if (del_cbp != NULL) { + LIST_REMOVE(del_cbp, q); + text_lfree(&del_cbp->textq); + FREE(del_cbp, sizeof(CB)); + } + return (0); +} + +/* + * cb_line -- + * Cut a portion of a single line. + */ +static int +cb_line(sp, ep, lno, fcno, clen, newp) + SCR *sp; + EXF *ep; + recno_t lno; + size_t fcno, clen; + TEXT **newp; +{ + TEXT *tp; + size_t len; + char *p; + + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + if ((*newp = tp = text_init(sp, NULL, 0, len)) == NULL) + return (1); + + /* + * A length of zero means to cut from the MARK to the end + * of the line. + */ + if (len != 0) { + if (clen == 0) + clen = len - fcno; + memmove(tp->lb, p + fcno, clen); + tp->len = clen; + } + return (0); +} + +/* + * text_init -- + * Allocate a new TEXT structure. + */ +TEXT * +text_init(sp, p, len, total_len) + SCR *sp; + const char *p; + size_t len, total_len; +{ + TEXT *tp; + + MALLOC(sp, tp, TEXT *, sizeof(TEXT)); + if (tp == NULL) + return (NULL); + /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */ + if (tp->lb_len = total_len) { + MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len); + if (tp->lb == NULL) { + free(tp); + return (NULL); + } + if (p != NULL && len != 0) + memmove(tp->lb, p, len); + } else + tp->lb = NULL; + tp->len = len; + tp->ai = tp->insert = tp->offset = tp->owrite = 0; + tp->wd = NULL; + tp->wd_len = 0; + return (tp); +} + +/* + * text_lfree -- + * Free a chain of text structures. + */ +void +text_lfree(headp) + TEXTH *headp; +{ + TEXT *tp; + + while ((tp = headp->cqh_first) != (void *)headp) { + CIRCLEQ_REMOVE(headp, tp, q); + text_free(tp); + } +} + +/* + * text_free -- + * Free a text structure. + */ +void +text_free(tp) + TEXT *tp; +{ + if (tp->lb != NULL) + FREE(tp->lb, tp->lb_len); + if (tp->wd != NULL) + FREE(tp->wd, tp->wd_len); + FREE(tp, sizeof(TEXT)); +} + +/* + * put -- + * Put text buffer contents into the file. + * + * !!! + * Historically, pasting into a file with no lines in vi would preserve + * the single blank line. This is almost certainly a result of the fact + * that historic vi couldn't deal with a file that had no lines in it. + * This implementation treats that as a bug, and does not retain the + * blank line. + */ +int +put(sp, ep, cbp, namep, cp, rp, append) + SCR *sp; + EXF *ep; + CB *cbp; + CHAR_T *namep; + MARK *cp, *rp; + int append; +{ + CHAR_T name; + TEXT *ltp, *tp; + recno_t lno; + size_t blen, clen, len; + char *bp, *p, *t; + + if (cbp == NULL) + if (namep == NULL) { + cbp = sp->gp->dcbp; + if (cbp == NULL) { + msgq(sp, M_ERR, "The default buffer is empty."); + return (1); + } + } else { + name = *namep; + CBNAME(sp, cbp, name); + if (cbp == NULL) { + msgq(sp, M_ERR, + "Buffer %s is empty.", charname(sp, name)); + return (1); + } + } + tp = cbp->textq.cqh_first; + + /* + * It's possible to do a put into an empty file, meaning that the + * cut buffer simply becomes the file. It's a special case so + * that we can ignore it in general. + * + * Historical practice is that the cursor ends up on the first + * non-blank character of the first line inserted. + */ + if (cp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) { + for (; tp != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->lno = 1; + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + } + + /* If a line mode buffer, append each new line into the file. */ + if (F_ISSET(cbp, CB_LMODE)) { + lno = append ? cp->lno : cp->lno - 1; + rp->lno = lno + 1; + for (; tp != (void *)&cbp->textq; ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + return (1); + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + goto ret; + } + + /* + * If buffer was cut in character mode, replace the current line with + * one built from the portion of the first line to the left of the + * split plus the first line in the CB. Append each intermediate line + * in the CB. Append a line built from the portion of the first line + * to the right of the split plus the last line in the CB. + * + * Get the first line. + */ + lno = cp->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tp->len + len + 1); + t = bp; + + /* Original line, left of the split. */ + if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) { + memmove(bp, p, clen); + p += clen; + t += clen; + } + + /* First line from the CB. */ + memmove(t, tp->lb, tp->len); + t += tp->len; + + /* Calculate length left in original line. */ + clen = len ? len - cp->cno - (append ? 1 : 0) : 0; + + /* + * If no more lines in the CB, append the rest of the original + * line and quit. Otherwise, build the last line before doing + * the intermediate lines, because the line changes will lose + * the cached line. + */ + if (tp->q.cqe_next == (void *)&cbp->textq) { + /* + * Historical practice is that if a non-line mode put + * is inside a single line, the cursor ends up on the + * last character inserted. + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + if (clen > 0) { + memmove(t, p, clen); + t += clen; + } + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + } else { + /* + * Have to build both the first and last lines of the + * put before doing any sets or we'll lose the cached + * line. Build both the first and last lines in the + * same buffer, so we don't have to have another buffer + * floating around. + * + * Last part of original line; check for space, reset + * the pointer into the buffer. + */ + ltp = cbp->textq.cqh_last; + len = t - bp; + ADD_SPACE_RET(sp, bp, blen, ltp->len + clen); + t = bp + len; + + /* Add in last part of the CB. */ + memmove(t, ltp->lb, ltp->len); + if (clen) + memmove(t + ltp->len, p, clen); + clen += ltp->len; + + /* + * Now: bp points to the first character of the first + * line, t points to the last character of the last + * line, t - bp is the length of the first line, and + * clen is the length of the last. Just figured you'd + * want to know. + * + * Output the line replacing the original line. + */ + if (file_sline(sp, ep, lno, bp, t - bp)) + goto mem; + + /* + * Historical practice is that if a non-line mode put + * covers multiple lines, the cursor ends up on the + * first character inserted. (Of course.) + */ + rp->lno = lno; + rp->cno = (t - bp) - 1; + + /* Output any intermediate lines in the CB. */ + for (tp = tp->q.cqe_next; + tp->q.cqe_next != (void *)&cbp->textq; + ++lno, tp = tp->q.cqe_next) + if (file_aline(sp, ep, 1, lno, tp->lb, tp->len)) + goto mem; + + if (file_aline(sp, ep, 1, lno, t, clen)) { +mem: FREE_SPACE(sp, bp, blen); + return (1); + } + } + FREE_SPACE(sp, bp, blen); + + /* Reporting... */ +ret: sp->rptlines[L_PUT] += lno - cp->lno; + + return (0); +} diff --git a/usr.bin/vi/cut.h b/usr.bin/vi/cut.h new file mode 100644 index 000000000000..8ade88f05633 --- /dev/null +++ b/usr.bin/vi/cut.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cut.h 8.11 (Berkeley) 1/11/94 + */ + +typedef struct _texth TEXTH; /* TEXT list head structure. */ +CIRCLEQ_HEAD(_texth, _text); + +/* Cut buffers. */ +struct _cb { + LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */ + TEXTH textq; /* Linked list of TEXT structures. */ + CHAR_T name; /* Cut buffer name. */ + size_t len; /* Total length of cut text. */ + +#define CB_LMODE 0x01 /* Cut was in line mode. */ + u_char flags; +}; + +/* Lines/blocks of text. */ +struct _text { /* Text: a linked list of lines. */ + CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */ + char *lb; /* Line buffer. */ + size_t lb_len; /* Line buffer length. */ + size_t len; /* Line length. */ + + /* These fields are used by the vi text input routine. */ + recno_t lno; /* 1-N: line number. */ + size_t ai; /* 0-N: autoindent bytes. */ + size_t insert; /* 0-N: bytes to insert (push). */ + size_t offset; /* 0-N: initial, unerasable bytes. */ + size_t owrite; /* 0-N: bytes to overwrite. */ + + /* These fields are used by the ex text input routine. */ + u_char *wd; /* Width buffer. */ + size_t wd_len; /* Width buffer length. */ +}; + +/* + * Get named buffer 'name'. + * Translate upper-case buffer names to lower-case buffer names. + */ +#define CBNAME(sp, cbp, name) { \ + CHAR_T __name; \ + __name = isupper(name) ? tolower(name) : (name); \ + for (cbp = sp->gp->cutq.lh_first; \ + cbp != NULL; cbp = cbp->q.le_next) \ + if (cbp->name == __name) \ + break; \ +} + +#define CUT_DELETE 0x01 /* Delete (rotate numeric buffers). */ +#define CUT_LINEMODE 0x02 /* Cut in line mode. */ +int cut __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); +int delete __P((SCR *, EXF *, MARK *, MARK *, int)); +int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int)); + +void text_free __P((TEXT *)); +TEXT *text_init __P((SCR *, const char *, size_t, size_t)); +void text_lfree __P((TEXTH *)); diff --git a/usr.bin/vi/delete.c b/usr.bin/vi/delete.c new file mode 100644 index 000000000000..e8c0a5d80e1f --- /dev/null +++ b/usr.bin/vi/delete.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)delete.c 8.7 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +/* + * delete -- + * Delete a range of text. + */ +int +delete(sp, ep, fm, tm, lmode) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + int lmode; +{ + recno_t lno; + size_t blen, len, tlen; + char *bp, *p; + int eof; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete: from %lu/%d to %lu/%d%s\n", + fm->lno, fm->cno, tm->lno, tm->cno, lmode ? " (LINE MODE)" : ""); +#endif + bp = NULL; + + /* Case 1 -- delete in line mode. */ + if (lmode) { + for (lno = tm->lno; lno >= fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + goto vdone; + } + + /* + * Case 2 -- delete to EOF. This is a special case because it's + * easier to pick it off than try and find it in the other cases. + */ + if (file_lline(sp, ep, &lno)) + return (1); + if (tm->lno >= lno) { + if (tm->lno == lno) { + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + eof = tm->cno >= len ? 1 : 0; + } else + eof = 1; + if (eof) { + for (lno = tm->lno; lno > fm->lno; --lno) { + if (file_dline(sp, ep, lno)) + return (1); + ++sp->rptlines[L_DELETED]; + } + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, fm->cno); + memmove(bp, p, fm->cno); + if (file_sline(sp, ep, fm->lno, bp, fm->cno)) + return (1); + goto done; + } + } + + /* Case 3 -- delete within a single line. */ + if (tm->lno == fm->lno) { + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + GET_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, fm->cno); + memmove(bp + fm->cno, p + tm->cno, len - tm->cno); + if (file_sline(sp, ep, fm->lno, bp, len - (tm->cno - fm->cno))) + goto err; + goto done; + } + + /* + * Case 4 -- delete over multiple lines. + * + * Figure out how big a buffer we need. + */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + tlen = len; + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + return (1); + } + + /* + * XXX + * We can overflow memory here, if (len + tlen) > SIZE_T_MAX. The + * only portable way I've found to test is to depend on the overflow + * being less than the value. + */ + tlen += len; + if (len > tlen) { + msgq(sp, M_ERR, "Error: line length overflow"); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, tlen); + + /* Copy the start partial line into place. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + goto err; + } + memmove(bp, p, fm->cno); + tlen = fm->cno; + + /* Copy the end partial line into place. */ + if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) { + GETLINE_ERR(sp, tm->lno); + goto err; + } + memmove(bp + tlen, p + tm->cno, len - tm->cno); + tlen += len - tm->cno; + + /* Set the current line. */ + if (file_sline(sp, ep, fm->lno, bp, tlen)) + goto err; + + /* Delete the last and intermediate lines. */ + for (lno = tm->lno; lno > fm->lno; --lno) + if (file_dline(sp, ep, lno)) + return (1); + + /* Reporting. */ +vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1; + +done: if (bp != NULL) + FREE_SPACE(sp, bp, blen); + + return (0); + + /* Free memory. */ +err: FREE_SPACE(sp, bp, blen); + + return (1); +} diff --git a/usr.bin/vi/exf.c b/usr.bin/vi/exf.c new file mode 100644 index 000000000000..0f9ac4ba0e4b --- /dev/null +++ b/usr.bin/vi/exf.c @@ -0,0 +1,690 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exf.c 8.65 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +/* + * We include <sys/file.h>, because the flock(2) #defines were + * found there on historical systems. We also include <fcntl.h> + * because the open(2) #defines are found there on newer systems. + */ +#include <sys/file.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +/* + * file_add -- + * Insert a file name into the FREF list, if it doesn't already + * appear in it. + * + * !!! + * The "if it doesn't already appear" changes vi's semantics slightly. If + * you do a "vi foo bar", and then execute "next bar baz", the edit of bar + * will reflect the line/column of the previous edit session. Historic nvi + * did not do this. The change is a logical extension of the change where + * vi now remembers the last location in any file that it has ever edited, + * not just the previously edited file. + */ +FREF * +file_add(sp, frp_append, name, ignore) + SCR *sp; + FREF *frp_append; + CHAR_T *name; + int ignore; +{ + FREF *frp; + char *p; + + /* + * Return it if it already exists. Note that we test against the + * user's current name, whatever that happens to be, including if + * it's a temporary file. If the user is trying to set an argument + * list, the ignore argument will be on -- if we're ignoring the + * file turn off the ignore bit, so it's back in the argument list. + */ + if (name != NULL) + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) { + if (!ignore) + F_CLR(frp, FR_IGNORE); + return (frp); + } + + /* Allocate and initialize the FREF structure. */ + CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); + if (frp == NULL) + return (NULL); + + /* + * If no file name specified, or if the file name is a request + * for something temporary, file_init() will allocate the file + * name. Temporary files are always ignored. + */ +#define TEMPORARY_FILE_STRING "/tmp" + if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && + (frp->name = strdup(name)) == NULL) { + FREE(frp, sizeof(FREF)); + msgq(sp, M_SYSERR, NULL); + return (NULL); + } + + /* Only the initial argument list is "remembered". */ + if (ignore) + F_SET(frp, FR_IGNORE); + + /* Append into the chain of file names. */ + if (frp_append != NULL) { + CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q); + } else + CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q); + + return (frp); +} + +/* + * file_first -- + * Return the first file name for editing, if any. + */ +FREF * +file_first(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the first file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_next -- + * Return the next file name, if any. + */ +FREF * +file_next(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_prev -- + * Return the previous file name, if any. + */ +FREF * +file_prev(sp, frp) + SCR *sp; + FREF *frp; +{ + while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq) + if (!F_ISSET(frp, FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_unedited -- + * Return if there are files that aren't ignored and are unedited. + */ +FREF * +file_unedited(sp) + SCR *sp; +{ + FREF *frp; + + /* Return the next file name. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + if (!F_ISSET(frp, FR_EDITED | FR_IGNORE)) + return (frp); + return (NULL); +} + +/* + * file_init -- + * Start editing a file, based on the FREF structure. If successsful, + * let go of any previous file. Don't release the previous file until + * absolutely sure we have the new one. + */ +int +file_init(sp, frp, rcv_name, force) + SCR *sp; + FREF *frp; + char *rcv_name; + int force; +{ + EXF *ep; + RECNOINFO oinfo; + struct stat sb; + size_t psize; + int fd; + char *p, *oname, tname[MAXPATHLEN]; + + /* + * Required ep initialization: + * Flush the line caches. + * Default recover mail file fd to -1. + * Set initial EXF flag bits. + */ + CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); + ep->c_lno = ep->c_nlines = OOBLNO; + ep->rcv_fd = -1; + LIST_INIT(&ep->marks); + F_SET(ep, F_FIRSTMODIFY); + + /* + * If no name or backing file, create a backing temporary file, saving + * the temp file name so can later unlink it. Repoint the name to the + * temporary name (we display it to the user until they rename it). + * There are some games we play with the FR_FREE_TNAME and FR_NONAME + * flags (see ex/ex_file.c) to make sure that the temporary memory gets + * free'd up. + */ + if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) { + (void)snprintf(tname, sizeof(tname), + "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY)); + if ((fd = mkstemp(tname)) == -1) { + msgq(sp, M_SYSERR, "Temporary file"); + goto err; + } + (void)close(fd); + if ((frp->tname = strdup(tname)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(tname); + goto err; + } + oname = frp->tname; + psize = 4 * 1024; + F_SET(frp, FR_NEWFILE); + } else { + /* Try to keep it at 10 pages or less per file. */ + if (sb.st_size < 40 * 1024) + psize = 4 * 1024; + else if (sb.st_size < 320 * 1024) + psize = 32 * 1024; + else + psize = 64 * 1024; + + frp->mtime = sb.st_mtime; + + if (!S_ISREG(sb.st_mode)) + msgq(sp, M_ERR, + "Warning: %s is not a regular file.", oname); + } + + /* Set up recovery. */ + memset(&oinfo, 0, sizeof(RECNOINFO)); + oinfo.bval = '\n'; /* Always set. */ + oinfo.psize = psize; + oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; + if (rcv_name == NULL) { + if (rcv_tmp(sp, ep, FILENAME(frp))) + msgq(sp, M_ERR, + "Modifications not recoverable if the system crashes."); + else + oinfo.bfname = ep->rcv_path; + } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err; + } else { + oinfo.bfname = ep->rcv_path; + F_SET(ep, F_MODIFIED | F_RCV_ON); + } + + /* Open a db structure. */ + if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, + O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) { + msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name); + goto err; + } + + /* Init file marks. */ + if (mark_init(sp, ep)) + goto err; + + /* Start logging. */ + if (log_init(sp, ep)) + goto err; + + /* + * The -R flag, or doing a "set readonly" during a session causes + * all files edited during the session (using an edit command, or + * even using tags) to be marked read-only. Changing the file name + * (see ex/ex_file.c), clears this flag. + * + * Otherwise, try and figure out if a file is readonly. This is a + * dangerous thing to do. The kernel is the only arbiter of whether + * or not a file is writeable, and the best that a user program can + * do is guess. Obvious loopholes are files that are on a file system + * mounted readonly (access catches this one on a few systems), or + * alternate protection mechanisms, ACL's for example, that we can't + * portably check. Lots of fun, and only here because users whined. + * + * !!! + * Historic vi displayed the readonly message if none of the file + * write bits were set, or if an an access(2) call on the path + * failed. This seems reasonable. If the file is mode 444, root + * users may want to know that the owner of the file did not expect + * it to be written. + * + * Historic vi set the readonly bit if no write bits were set for + * a file, even if the access call would have succeeded. This makes + * the superuser force the write even when vi expects that it will + * succeed. I'm less supportive of this semantic, but it's historic + * practice and the conservative approach to vi'ing files as root. + * + * It would be nice if there was some way to update this when the user + * does a "^Z; chmod ...". The problem is that we'd first have to + * distinguish between readonly bits set because of file permissions + * and those set for other reasons. That's not too hard, but deciding + * when to reevaluate the permissions is trickier. An alternative + * might be to turn off the readonly bit if the user forces a write + * and it succeeds. + * + * XXX + * Access(2) doesn't consider the effective uid/gid values. This + * probably isn't a problem for vi when it's running standalone. + */ + if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) && + (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || + access(FILENAME(frp), W_OK))) + F_SET(frp, FR_RDONLY); + else + F_CLR(frp, FR_RDONLY); + + /* + * Close the previous file; if that fails, close the new one + * and run for the border. + */ + if (sp->ep != NULL && file_end(sp, sp->ep, force)) { + (void)file_end(sp, ep, 1); + goto err; + } + + /* + * 4.4BSD supports locking in the open call, other systems don't. + * Since the user can't interrupt us between the open and here, + * it's a don't care. + * + * !!! + * We need to distinguish a lock not being available for the file + * from the file system not supporting locking. Assume that EAGAIN + * or EWOULDBLOCK is the former. There isn't a portable way to do + * this. + * + * XXX + * The locking is flock(2) style, not fcntl(2). The latter is known + * to fail badly on some systems, and its only advantage is that it + * occasionally works over NFS. + */ + if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB)) + if (errno == EAGAIN || errno == EWOULDBLOCK) { + msgq(sp, M_INFO, + "%s already locked, session is read-only", oname); + F_SET(frp, FR_RDONLY); + } else + msgq(sp, M_VINFO, "%s cannot be locked", oname); + + /* + * Set the previous file pointer and the alternate file name to be + * the file we're about to discard. + * + * !!! + * If the current file was a temporary file, the call to file_end() + * unlinked it and free'd the name. So, there is no previous file, + * and there is no alternate file name. This matches historical + * practice, although in historical vi it could only happen as the + * result of the initial command, i.e. if vi was execute without a + * file name. + */ + if (sp->frp != NULL) { + p = FILENAME(sp->frp); + if (p == NULL) + sp->p_frp = NULL; + else + sp->p_frp = sp->frp; + set_alt_name(sp, p); + } + + /* The new file has now been officially edited. */ + F_SET(frp, FR_EDITED); + + /* Switch... */ + ++ep->refcnt; + sp->ep = ep; + sp->frp = frp; + return (0); + +err: if (frp->tname != NULL) { + (void)unlink(frp->tname); + free(frp->tname); + frp->tname = NULL; + } + if (ep->rcv_path != NULL) { + free(ep->rcv_path); + ep->rcv_path = NULL; + } + FREE(ep, sizeof(EXF)); + return (1); +} + +/* + * file_end -- + * Stop editing a file. + */ +int +file_end(sp, ep, force) + SCR *sp; + EXF *ep; + int force; +{ + FREF *frp; + + /* + * + * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT! + * + * Save the cursor location. + * + * XXX + * It would be cleaner to do this somewhere else, but by the time + * ex or vi knows that we're changing files it's already happened. + */ + frp = sp->frp; + frp->lno = sp->lno; + frp->cno = sp->cno; + F_SET(frp, FR_CURSORSET); + + /* If multiply referenced, just decrement the count and return. */ + if (--ep->refcnt != 0) + return (0); + + /* Close the db structure. */ + if (ep->db->close != NULL && ep->db->close(ep->db) && !force) { + msgq(sp, M_ERR, + "%s: close: %s", FILENAME(frp), strerror(errno)); + ++ep->refcnt; + return (1); + } + + /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */ + + /* Stop logging. */ + (void)log_end(sp, ep); + + /* Free up any marks. */ + mark_end(sp, ep); + + /* + * Delete the recovery files, close the open descriptor, + * free recovery memory. + */ + if (!F_ISSET(ep, F_RCV_NORM)) { + if (ep->rcv_path != NULL && unlink(ep->rcv_path)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_path, strerror(errno)); + if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath)) + msgq(sp, M_ERR, + "%s: remove: %s", ep->rcv_mpath, strerror(errno)); + } + if (ep->rcv_fd != -1) + (void)close(ep->rcv_fd); + if (ep->rcv_path != NULL) + free(ep->rcv_path); + if (ep->rcv_mpath != NULL) + free(ep->rcv_mpath); + + /* + * Unlink any temporary file, file name. We also turn on the + * ignore bit at this point, because it was a "created" file, + * not an argument file. + */ + if (frp->tname != NULL) { + if (unlink(frp->tname)) + msgq(sp, M_ERR, + "%s: remove: %s", frp->tname, strerror(errno)); + free(frp->tname); + frp->tname = NULL; + + if (frp->name == NULL && frp->cname == NULL) + F_SET(frp, FR_IGNORE); + } + /* Free the EXF structure. */ + FREE(ep, sizeof(EXF)); + return (0); +} + +/* + * file_write -- + * Write the file to disk. Historic vi had fairly convoluted + * semantics for whether or not writes would happen. That's + * why all the flags. + */ +int +file_write(sp, ep, fm, tm, name, flags) + SCR *sp; + EXF *ep; + MARK *fm, *tm; + char *name; + int flags; +{ + struct stat sb; + FILE *fp; + FREF *frp; + MARK from, to; + u_long nlno, nch; + int fd, oflags, rval; + char *msg; + + /* + * Don't permit writing to temporary files. The problem is that + * if it's a temp file, and the user does ":wq", we write and quit, + * unlinking the temporary file. Not what the user had in mind + * at all. This test cannot be forced. + */ + frp = sp->frp; + if (name == NULL && frp->cname == NULL && frp->name == NULL) { + msgq(sp, M_ERR, "No filename to which to write."); + return (1); + } + + /* Can't write files marked read-only, unless forced. */ + if (!LF_ISSET(FS_FORCE) && + name == NULL && F_ISSET(frp, FR_RDONLY)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Read-only file, not written; use ! to override."); + else + msgq(sp, M_ERR, + "Read-only file, not written."); + return (1); + } + + /* If not forced, not appending, and "writeany" not set ... */ + if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { + /* Don't overwrite anything but the original file. */ + if (name != NULL) { + if (!stat(name, &sb)) + goto exists; + } else if (frp->cname != NULL && + !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) { + name = frp->cname; +exists: if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "%s exists, not written; use ! to override.", name); + else + msgq(sp, M_ERR, + "%s exists, not written.", name); + return (1); + } + + /* + * Don't write part of any existing file. Only test for the + * original file, the previous test catches anything else. + */ + if (!LF_ISSET(FS_ALL) && name == NULL && + frp->cname == NULL && !stat(frp->name, &sb)) { + if (LF_ISSET(FS_POSSIBLE)) + msgq(sp, M_ERR, + "Use ! to write a partial file."); + else + msgq(sp, M_ERR, "Partial file, not written."); + return (1); + } + } + + /* + * Figure out if the file already exists -- if it doesn't, we display + * the "new file" message. The stat might not be necessary, but we + * just repeat it because it's easier than hacking the previous tests. + * The information is only used for the user message and modification + * time test, so we can ignore the obvious race condition. + * + * If the user is overwriting a file other than the original file, and + * O_WRITEANY was what got us here (neither force nor append was set), + * display the "existing file" messsage. Since the FR_CHANGEWRITE flag + * is set on a successful write, the message only appears once when the + * user changes a file name. This is historic practice. + * + * One final test. If we're not forcing or appending, and we have a + * saved modification time, stop the user if it's been written since + * we last edited or wrote it, and make them force it. + */ + if (stat(name == NULL ? FILENAME(frp) : name, &sb)) + msg = ": new file"; + else { + msg = ""; + if (!LF_ISSET(FS_FORCE | FS_APPEND)) { + if (frp->mtime && sb.st_mtime > frp->mtime) { + msgq(sp, M_ERR, + "%s: file modified more recently than this copy%s.", + name == NULL ? frp->name : name, + LF_ISSET(FS_POSSIBLE) ? + "; use ! to override" : ""); + return (1); + } + if (name != NULL || + !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL) + msg = ": existing file"; + } + } + + /* We no longer care where the name came from. */ + if (name == NULL) + name = FILENAME(frp); + + /* Set flags to either append or truncate. */ + oflags = O_CREAT | O_WRONLY; + if (LF_ISSET(FS_APPEND)) + oflags |= O_APPEND; + else + oflags |= O_TRUNC; + + /* Open the file. */ + if ((fd = open(name, oflags, DEFFILEMODE)) < 0) { + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Use stdio for buffering. */ + if ((fp = fdopen(fd, "w")) == NULL) { + (void)close(fd); + msgq(sp, M_SYSERR, name); + return (1); + } + + /* Build fake addresses, if necessary. */ + if (fm == NULL) { + from.lno = 1; + from.cno = 0; + fm = &from; + if (file_lline(sp, ep, &to.lno)) + return (1); + to.cno = 0; + tm = &to; + } + + /* Write the file. */ + rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch); + + /* + * Save the new last modification time -- even if the write fails + * we re-init the time if we wrote anything. That way the user can + * clean up the disk and rewrite without having to force it. + */ + if (nlno || nch) + frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime; + + /* If the write failed, complain loudly. */ + if (rval) { + if (!LF_ISSET(FS_APPEND)) + msgq(sp, M_ERR, "%s: WARNING: file truncated!", name); + return (1); + } + + /* + * Once we've actually written the file, it doesn't matter that the + * file name was changed -- if it was, we've already whacked it. + */ + F_SET(frp, FR_CHANGEWRITE); + + /* If wrote the entire file, clear the modified bit. */ + if (LF_ISSET(FS_ALL)) + F_CLR(ep, F_MODIFIED); + + msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.", + name, msg, nlno, nlno == 1 ? "" : "s", nch); + + return (0); +} diff --git a/usr.bin/vi/exf.h b/usr.bin/vi/exf.h new file mode 100644 index 000000000000..5049f3ea2418 --- /dev/null +++ b/usr.bin/vi/exf.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exf.h 8.20 (Berkeley) 12/28/93 + */ + /* Undo direction. */ +/* + * exf -- + * The file structure. + */ +struct _exf { + int refcnt; /* Reference count. */ + + /* Underlying database state. */ + DB *db; /* File db structure. */ + char *c_lp; /* Cached line. */ + size_t c_len; /* Cached line length. */ + recno_t c_lno; /* Cached line number. */ + recno_t c_nlines; /* Cached lines in the file. */ + + DB *log; /* Log db structure. */ + char *l_lp; /* Log buffer. */ + size_t l_len; /* Log buffer length. */ + recno_t l_high; /* Log last + 1 record number. */ + recno_t l_cur; /* Log current record number. */ + MARK l_cursor; /* Log cursor position. */ + enum direction lundo; /* Last undo direction. */ + + LIST_HEAD(_markh, _mark) marks; /* Linked list of file MARK's. */ + + /* + * Paths for the recovery mail file and the vi recovery file and + * a file descriptor for the former. We keep a file descriptor + * to the recovery file open and locked, while the file is in use. + * This allows the recovery option to distinguish between files + * that are live, and those that should be recovered. + * + * F_RCV_ON is set as long as we believe that the file is recoverable. + * This doesn't mean that any initialization has been done, however. + * If F_RCV_NORM is not set and rcv_path and rcv_mpath are not NULL, + * they are unlinked on file exit. If not NULL they are free'd on file + * exit. On file exit, if rcv_fd is not -1, it is closed. + */ +#define RCV_PERIOD 120 /* Sync every two minutes. */ + char *rcv_path; /* Recover file name. */ + char *rcv_mpath; /* Recover mail file name. */ + int rcv_fd; /* Locked mail file descriptor. */ + +#define F_FIRSTMODIFY 0x001 /* File not yet modified. */ +#define F_MODIFIED 0x002 /* File is currently dirty. */ +#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */ +#define F_NOLOG 0x008 /* Logging turned off. */ +#define F_RCV_ALRM 0x010 /* File needs to be synced. */ +#define F_RCV_NORM 0x020 /* Don't delete recovery files. */ +#define F_RCV_ON 0x040 /* Recovery is possible. */ +#define F_UNDO 0x080 /* No change since last undo. */ + u_int flags; +}; + +/* Flags to file_write(). */ +#define FS_ALL 0x01 /* Write the entire file. */ +#define FS_APPEND 0x02 /* Append to the file. */ +#define FS_FORCE 0x04 /* Force is set. */ +#define FS_POSSIBLE 0x08 /* Force could be set. */ + +#define GETLINE_ERR(sp, lno) { \ + msgq((sp), M_ERR, \ + "Error: %s/%d: unable to retrieve line %u.", \ + tail(__FILE__), __LINE__, (lno)); \ +} + +/* FREF routines. */ +FREF *file_add __P((SCR *, FREF *, CHAR_T *, int)); +FREF *file_first __P((SCR *)); +FREF *file_next __P((SCR *, FREF *)); +FREF *file_prev __P((SCR *, FREF *)); +FREF *file_unedited __P((SCR *)); + +/* EXF routines. */ +int file_end __P((SCR *, EXF *, int)); +int file_init __P((SCR *, FREF *, char *, int)); +int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int)); + +/* Recovery routines. */ +void rcv_hup __P((void)); +int rcv_init __P((SCR *, EXF *)); +int rcv_list __P((SCR *)); +int rcv_read __P((SCR *, char *)); +int rcv_sync __P((SCR *, EXF *)); +void rcv_term __P((void)); +int rcv_tmp __P((SCR *, EXF *, char *)); + +/* DB interface routines */ +int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t)); +int file_dline __P((SCR *, EXF *, recno_t)); +char *file_gline __P((SCR *, EXF *, recno_t, size_t *)); +int file_iline __P((SCR *, EXF *, recno_t, char *, size_t)); +int file_lline __P((SCR *, EXF *, recno_t *)); +char *file_rline __P((SCR *, EXF *, recno_t, size_t *)); +int file_sline __P((SCR *, EXF *, recno_t, char *, size_t)); diff --git a/usr.bin/vi/gs.h b/usr.bin/vi/gs.h new file mode 100644 index 000000000000..86af7299b3c7 --- /dev/null +++ b/usr.bin/vi/gs.h @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)gs.h 8.26 (Berkeley) 1/9/94 + */ + +struct _gs { + CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */ + CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */ + + mode_t origmode; /* Original terminal mode. */ + struct termios + original_termios; /* Original terminal values. */ + struct termios + s5_curses_botch; /* System V curses workaround. */ + + MSGH msgq; /* User message list. */ + + char *tmp_bp; /* Temporary buffer. */ + size_t tmp_blen; /* Size of temporary buffer. */ + +#ifdef DEBUG + FILE *tracefp; /* Trace file pointer. */ +#endif + +/* INFORMATION SHARED BY ALL SCREENS. */ + IBUF *tty; /* Key input buffer. */ + + CB *dcbp; /* Default cut buffer pointer. */ + CB *dcb_store; /* Default cut buffer storage. */ + LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */ + +#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */ + LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */ + bitstr_t bit_decl(seqb, MAX_BIT_SEQ); + +#define term_key_val(sp, ch) \ + ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \ + (ch) > sp->gp->max_special ? 0 : __term_key_val(sp, ch)) +#define MAX_FAST_KEY 255 /* Max + 1 fast check character.*/ + CHAR_T max_special; /* Max special character. */ + u_char *special_key; /* Fast lookup table. */ + CHNAME const *cname; /* Display names of ASCII characters. */ + +#define G_ABBREV 0x00001 /* If have abbreviations. */ +#define G_BELLSCHED 0x00002 /* Bell scheduled. */ +#define G_CURSES_INIT 0x00004 /* Curses: initialized. */ +#define G_CURSES_S5CB 0x00008 /* Curses: s5_curses_botch set. */ +#define G_ISFROMTTY 0x00010 /* Reading from a tty. */ +#define G_RECOVER_SET 0x00020 /* Recover system initialized. */ +#define G_SETMODE 0x00040 /* Tty mode changed. */ +#define G_SIGALRM 0x00080 /* SIGALRM arrived. */ +#define G_SIGHUP 0x00100 /* SIGHUP arrived. */ +#define G_SIGTERM 0x00200 /* SIGTERM arrived. */ +#define G_SIGWINCH 0x00400 /* SIGWINCH arrived. */ +#define G_SLEEPING 0x00800 /* Asleep (die on signal). */ +#define G_SNAPSHOT 0x01000 /* Always snapshot files. */ +#define G_TMP_INUSE 0x02000 /* Temporary buffer in use. */ + u_int flags; +}; + +extern GS *__global_list; /* List of screens. */ diff --git a/usr.bin/vi/include/bitstring.h b/usr.bin/vi/include/bitstring.h new file mode 100644 index 000000000000..88437e7fb9f7 --- /dev/null +++ b/usr.bin/vi/include/bitstring.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Vixie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 + */ + +#ifndef _BITSTRING_H_ +#define _BITSTRING_H_ + +typedef unsigned char bitstr_t; + +/* internal macros */ + /* byte of the bitstring bit is in */ +#define _bit_byte(bit) \ + ((bit) >> 3) + + /* mask for the bit within its byte */ +#define _bit_mask(bit) \ + (1 << ((bit)&0x7)) + +/* external macros */ + /* bytes in a bitstring of nbits bits */ +#define bitstr_size(nbits) \ + ((((nbits) - 1) >> 3) + 1) + + /* allocate a bitstring */ +#define bit_alloc(nbits) \ + (bitstr_t *)calloc(1, \ + (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t)) + + /* allocate a bitstring on the stack */ +#define bit_decl(name, nbits) \ + (name)[bitstr_size(nbits)] + + /* is bit N of bitstring name set? */ +#define bit_test(name, bit) \ + ((name)[_bit_byte(bit)] & _bit_mask(bit)) + + /* set bit N of bitstring name */ +#define bit_set(name, bit) \ + (name)[_bit_byte(bit)] |= _bit_mask(bit) + + /* clear bit N of bitstring name */ +#define bit_clear(name, bit) \ + (name)[_bit_byte(bit)] &= ~_bit_mask(bit) + + /* clear bits start ... stop in bitstring */ +#define bit_nclear(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \ + (0xff << ((_stop&0x7) + 1))); \ + } else { \ + _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0; \ + _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \ + } \ +} + + /* set bits start ... stop in bitstring */ +#define bit_nset(name, start, stop) { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + register int _startbyte = _bit_byte(_start); \ + register int _stopbyte = _bit_byte(_stop); \ + if (_startbyte == _stopbyte) { \ + _name[_startbyte] |= ((0xff << (_start&0x7)) & \ + (0xff >> (7 - (_stop&0x7)))); \ + } else { \ + _name[_startbyte] |= 0xff << ((_start)&0x7); \ + while (++_startbyte < _stopbyte) \ + _name[_startbyte] = 0xff; \ + _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \ + } \ +} + + /* find first bit clear in name */ +#define bit_ffc(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte] != 0xff) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + + /* find first bit set in name */ +#define bit_ffs(name, nbits, value) { \ + register bitstr_t *_name = name; \ + register int _byte, _nbits = nbits; \ + register int _stopbyte = _bit_byte(_nbits), _value = -1; \ + for (_byte = 0; _byte <= _stopbyte; ++_byte) \ + if (_name[_byte]) { \ + _value = _byte << 3; \ + for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \ + ++_value, _stopbyte >>= 1); \ + break; \ + } \ + *(value) = _value; \ +} + +#endif /* !_BITSTRING_H_ */ diff --git a/usr.bin/vi/include/cdefs.h b/usr.bin/vi/include/cdefs.h new file mode 100644 index 000000000000..c4157bcd1a8d --- /dev/null +++ b/usr.bin/vi/include/cdefs.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.2 (Berkeley) 10/4/93 + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#if !defined(__GNUC__) && !defined(__cplusplus) +#define inline +#endif + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifdef __GNUC__ +#define const __const /* GCC: ANSI C with -traditional */ +#define inline __inline +#define signed __signed +#define volatile __volatile + +#else /* !__GNUC__ */ +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC has extensions for declaring functions as `pure' (always returns + * the same value given the same inputs, i.e., has no external state and + * no side effects) and `dead' (nonreturning). These mainly affect + * optimization and warnings. Unfortunately, GCC complains if these are + * used under strict ANSI mode (`gcc -ansi -pedantic'), hence we need to + * define them only if compiling without this. + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#else +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/usr.bin/vi/include/compat.h b/usr.bin/vi/include/compat.h new file mode 100644 index 000000000000..10406993014b --- /dev/null +++ b/usr.bin/vi/include/compat.h @@ -0,0 +1,241 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)compat.h 8.10 (Berkeley) 1/11/94 + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#include <sys/types.h> +#include <machine/limits.h> +#include <termios.h> +#include <errno.h> + +/* + * If your system doesn't typedef u_long, u_short, or u_char, change + * the 0 to a 1. + */ +#if 0 +typedef unsigned char u_char; /* 4.[34]BSD names. */ +typedef unsigned int u_int; +typedef unsigned long u_long; +typedef unsigned short u_short; +#endif + +/* If your system doesn't typedef size_t, change the 0 to a 1. */ +#if 0 +typedef unsigned int size_t; /* 4.[34]BSD names. */ +#endif + +/* + * If your system doesn't have the POSIX type for a signal mask, + * change the 0 to a 1. + */ +#if 0 /* POSIX 1003.1 signal mask type. */ +typedef unsigned int sigset_t; +#endif + +/* + * If your system's vsprintf returns a char *, not an int, + * change the 0 to a 1. + */ +#if 0 +#define VSPRINTF_CHARSTAR +#endif + +/* + * If you don't have POSIX 1003.1 signals, the signal code surrounding the + * temporary file creation is intended to block all of the possible signals + * long enough to create the file and unlink it. All of this stuff is + * intended to use old-style BSD calls to fake POSIX 1003.1 calls. + */ +#ifdef NO_POSIX_SIGNALS +#define sigemptyset(set) (*(set) = 0) +#define sigfillset(set) (*(set) = ~(sigset_t)0, 0) +#define sigaddset(set,signo) (*(set) |= sigmask(signo), 0) +#define sigdelset(set,signo) (*(set) &= ~sigmask(signo), 0) +#define sigismember(set,signo) ((*(set) & sigmask(signo)) != 0) + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +static int __sigtemp; /* For the use of sigprocmask */ + +/* Repeated test of oset != NULL is to avoid "*0". */ +#define sigprocmask(how, set, oset) \ + ((__sigtemp = \ + (((how) == SIG_BLOCK) ? \ + sigblock(0) | *(set) : \ + (((how) == SIG_UNBLOCK) ? \ + sigblock(0) & ~(*(set)) : \ + ((how) == SIG_SETMASK ? \ + *(set) : sigblock(0))))), \ + ((oset) ? (*(oset ? oset : set) = sigsetmask(__sigtemp)) : \ + sigsetmask(__sigtemp)), 0) +#endif + +/* + * If realloc(3) of a NULL pointer on your system isn't the same as + * a malloc(3) call, change the 0 to a 1, and add realloc.o to the + * MISC line in your Makefile. + */ +#if 0 +#define realloc __fix_realloc +#endif + +/* + * If your system doesn't have an include file with the appropriate + * byte order set, make sure you specify the correct one. + */ +#ifndef BYTE_ORDER +#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#define BYTE_ORDER LITTLE_ENDIAN /* Set for your system. */ +#endif + +#if defined(SYSV) || defined(SYSTEM5) +#define index(a, b) strchr(a, b) +#define rindex(a, b) strrchr(a, b) +#define bzero(a, b) memset(a, 0, b) +#define bcmp(a, b, n) memcmp(a, b, n) +#define bcopy(a, b, n) memmove(b, a, n) +#endif + +#if defined(BSD) || defined(BSD4_3) +#define strchr(a, b) index(a, b) +#define strrchr(a, b) rindex(a, b) +#define memcmp(a, b, n) bcmp(a, b, n) +#define memmove(a, b, n) bcopy(b, a, n) +#endif + +/* + * 32-bit machine. The db routines are theoretically independent of + * the size of u_shorts and u_longs, but I don't know that anyone has + * ever actually tried it. At a minimum, change the following #define's + * if you are trying to compile on a different type of system. + */ +#ifndef USHRT_MAX +#define USHRT_MAX 0xFFFF +#define ULONG_MAX 0xFFFFFFFF +#endif + +#ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */ +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 RE limit. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +/* + * If you can't provide lock values in the open(2) call. Note, this + * allows races to happen. + */ +#ifndef O_EXLOCK /* 4.4BSD extension. */ +#define O_EXLOCK 0 +#endif + +#ifndef O_SHLOCK /* 4.4BSD extension. */ +#define O_SHLOCK 0 +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ +#endif + +#ifndef WCOREDUMP /* 4.4BSD extension */ +#define WCOREDUMP(a) 0 +#endif + +#ifndef STDERR_FILENO +#define STDIN_FILENO 0 /* ANSI C #defines */ +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifndef SEEK_END +#define SEEK_SET 0 /* POSIX 1003.1 seek values */ +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */ +#define _POSIX_VDISABLE 0 /* Some systems used 0. */ +#endif + +#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) +#define S_ISSOCK(m) ((m & 0170000) == 0140000) +#endif + +#ifndef TCSASOFT /* 4.4BSD extension. */ +#define TCSASOFT 0 +#endif + +#ifndef _POSIX2_RE_DUP_MAX /* POSIX 1003.2 values. */ +#define _POSIX2_RE_DUP_MAX 255 +#endif + +#ifndef NULL /* ANSI C #defines NULL everywhere. */ +#define NULL 0 +#endif + +#ifndef MAX /* Usually found in <sys/param.h>. */ +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN /* Usually found in <sys/param.h>. */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Default file permissions. */ +#ifndef DEFFILEMODE /* 4.4BSD extension. */ +#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) +#endif + +#ifndef S_ISDIR /* POSIX 1003.1 file type tests. */ +#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */ +#endif + +/* The type of a va_list. */ +#ifndef _BSD_VA_LIST_ /* 4.4BSD #define. */ +#define _BSD_VA_LIST_ char * +#endif + +#endif /* !_COMPAT_H_ */ diff --git a/usr.bin/vi/include/err.h b/usr.bin/vi/include/err.h new file mode 100644 index 000000000000..b6d7e5f94b87 --- /dev/null +++ b/usr.bin/vi/include/err.h @@ -0,0 +1,16 @@ +#include <sys/cdefs.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void err __P((int, const char *, ...)); +void verr __P((int, const char *, va_list)); +void errx __P((int, const char *, ...)); +void verrx __P((int, const char *, va_list)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, va_list)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, va_list)); diff --git a/usr.bin/vi/include/file.h b/usr.bin/vi/include/file.h new file mode 100644 index 000000000000..680797fa6a52 --- /dev/null +++ b/usr.bin/vi/include/file.h @@ -0,0 +1,15 @@ +/* + * If we're doing flock(2) emulation, we need to get the LOCK_* #defines. + * This stub <sys/file.h> includes the real one, and, if they're not in + * it, we #define them here. + */ + +#include </usr/include/sys/file.h> + +#ifndef LOCK_SH +/* lock operations for flock(2) */ +#define LOCK_SH 0x01 /* shared file lock */ +#define LOCK_EX 0x02 /* exclusive file lock */ +#define LOCK_NB 0x04 /* don't block when locking */ +#define LOCK_UN 0x08 /* unlock file */ +#endif diff --git a/usr.bin/vi/include/glob.h b/usr.bin/vi/include/glob.h new file mode 100644 index 000000000000..b679f6aef62a --- /dev/null +++ b/usr.bin/vi/include/glob.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _GLOB_H_ +#define _GLOB_H_ + +#include <sys/cdefs.h> + +#include "compat.h" + +struct stat; +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_matchc; /* Count of paths matching pattern. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ + /* Copy of errfunc parameter to glob. */ + int (*gl_errfunc) __P((const char *, int)); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir) __P((void *)); + struct dirent *(*gl_readdir) __P((void *)); + void *(*gl_opendir) __P((const char *)); + int (*gl_lstat) __P((const char *, struct stat *)); + int (*gl_stat) __P((const char *, struct stat *)); +} glob_t; + +#define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */ +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_MARK 0x0008 /* Append / to matching directories. */ +#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +#define GLOB_NOSORT 0x0020 /* Don't sort. */ + +#ifndef _POSIX_SOURCE +#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ +#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +#endif + +#define GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define GLOB_ABEND (-2) /* Unignored error. */ + +__BEGIN_DECLS +int glob __P((const char *, int, int (*)(const char *, int), glob_t *)); +void globfree __P((glob_t *)); +__END_DECLS + +#endif /* !_GLOB_H_ */ diff --git a/usr.bin/vi/include/mpool.h b/usr.bin/vi/include/mpool.h new file mode 100644 index 000000000000..910e0782aa9d --- /dev/null +++ b/usr.bin/vi/include/mpool.h @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mpool.h 8.1 (Berkeley) 6/2/93 + */ + +/* + * The memory pool scheme is a simple one. Each in memory page is referenced + * by a bucket which is threaded in three ways. All active pages are threaded + * on a hash chain (hashed by the page number) and an lru chain. Inactive + * pages are threaded on a free chain. Each reference to a memory pool is + * handed an MPOOL which is the opaque cookie passed to all of the memory + * routines. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the lists. */ +typedef struct BKT { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ + void *page; /* page */ + pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ + unsigned long flags; /* flags */ +} BKT; + +/* The BKTHDR structures are the heads of the lists. */ +typedef struct BKTHDR { + struct BKT *hnext; /* next hash bucket */ + struct BKT *hprev; /* previous hash bucket */ + struct BKT *cnext; /* next free/lru bucket */ + struct BKT *cprev; /* previous free/lru bucket */ +} BKTHDR; + +typedef struct MPOOL { + BKTHDR free; /* The free list. */ + BKTHDR lru; /* The LRU list. */ + BKTHDR hashtable[HASHSIZE]; /* Hashed list by page number. */ + pgno_t curcache; /* Current number of cached pages. */ + pgno_t maxcache; /* Max number of cached pages. */ + pgno_t npages; /* Number of pages in the file. */ + u_long pagesize; /* File page size. */ + int fd; /* File descriptor. */ + /* Page in conversion routine. */ + void (*pgin) __P((void *, pgno_t, void *)); + /* Page out conversion routine. */ + void (*pgout) __P((void *, pgno_t, void *)); + void *pgcookie; /* Cookie for page in/out routines. */ +#ifdef STATISTICS + unsigned long cachehit; + unsigned long cachemiss; + unsigned long pagealloc; + unsigned long pageflush; + unsigned long pageget; + unsigned long pagenew; + unsigned long pageput; + unsigned long pageread; + unsigned long pagewrite; +#endif +} MPOOL; + +#ifdef __MPOOLINTERFACE_PRIVATE +/* Macros to insert/delete into/from hash chain. */ +#define rmhash(bp) { \ + (bp)->hprev->hnext = (bp)->hnext; \ + (bp)->hnext->hprev = (bp)->hprev; \ +} +#define inshash(bp, pg) { \ + hp = &mp->hashtable[HASHKEY(pg)]; \ + (bp)->hnext = hp->hnext; \ + (bp)->hprev = (struct BKT *)hp; \ + hp->hnext->hprev = (bp); \ + hp->hnext = (bp); \ +} + +/* Macros to insert/delete into/from lru and free chains. */ +#define rmchain(bp) { \ + (bp)->cprev->cnext = (bp)->cnext; \ + (bp)->cnext->cprev = (bp)->cprev; \ +} +#define inschain(bp, dp) { \ + (bp)->cnext = (dp)->cnext; \ + (bp)->cprev = (struct BKT *)(dp); \ + (dp)->cnext->cprev = (bp); \ + (dp)->cnext = (bp); \ +} +#endif + +__BEGIN_DECLS +MPOOL *mpool_open __P((DBT *, int, pgno_t, pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *), + void (*)(void *, pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, pgno_t *)); +void *mpool_get __P((MPOOL *, pgno_t, u_int)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS diff --git a/usr.bin/vi/include/ndbm.h b/usr.bin/vi/include/ndbm.h new file mode 100644 index 000000000000..a545bca1326e --- /dev/null +++ b/usr.bin/vi/include/ndbm.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NDBM_H_ +#define _NDBM_H_ + +#include <db.h> + +/* Map dbm interface onto db(3). */ +#define DBM_RDONLY O_RDONLY + +/* Flags to dbm_store(). */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * The db(3) support for ndbm(3) always appends this suffix to the + * file name to avoid overwriting the user's original database. + */ +#define DBM_SUFFIX ".db" + +typedef struct { + char *dptr; + int dsize; +} datum; + +typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE + +__BEGIN_DECLS +void dbm_close __P((DBM *)); +int dbm_delete __P((DBM *, datum)); +datum dbm_fetch __P((DBM *, datum)); +datum dbm_firstkey __P((DBM *)); +long dbm_forder __P((DBM *, datum)); +datum dbm_nextkey __P((DBM *)); +DBM *dbm_open __P((const char *, int, int)); +int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); +__END_DECLS + +#endif /* !_NDBM_H_ */ diff --git a/usr.bin/vi/include/queue.h b/usr.bin/vi/include/queue.h new file mode 100644 index 000000000000..40d32ccb6e29 --- /dev/null +++ b/usr.bin/vi/include/queue.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +/* + * This file defines three types of data structures: lists, tail queues, + * and circular queues. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element or at the head of the list. A list may only be + * traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) { \ + (head)->lh_first = NULL; \ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} + +#define LIST_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} + +#define LIST_REMOVE(elm, field) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} + +#define TAILQ_REMOVE(head, elm, field) { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} + +#define CIRCLEQ_REMOVE(head, elm, field) { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} +#endif /* !_QUEUE_H_ */ diff --git a/usr.bin/vi/interrupt.h b/usr.bin/vi/interrupt.h new file mode 100644 index 000000000000..baa75fef2ca9 --- /dev/null +++ b/usr.bin/vi/interrupt.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)interrupt.h 8.1 (Berkeley) 1/9/94 + */ + +/* + * Macros to declare the variables and then turn on and off interrupts. + */ +#define DECLARE_INTERRUPTS \ + struct sigaction __act, __oact; \ + struct termios __nterm, __term; \ + u_int __istate; \ + int __isig, __termreset + +/* + * Search, global, and substitute interruptions. + * + * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the + * search, so we install a handler. VQUIT is ignored by main() because + * nvi never wants to catch it. A handler for VSUSP should have been + * installed by the screen code. + */ +#define SET_UP_INTERRUPTS(handler) { \ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { \ + __act.sa_handler = handler; \ + sigemptyset(&__act.sa_mask); \ + __act.sa_flags = 0; \ + if (__isig = !sigaction(SIGINT, &__act, &__oact)) { \ + __termreset = 0; \ + __istate = F_ISSET(sp, S_INTERRUPTIBLE); \ + F_CLR(sp, S_INTERRUPTED); \ + F_SET(sp, S_INTERRUPTIBLE); \ + if (tcgetattr(STDIN_FILENO, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcgetattr"); \ + goto interrupt_err; \ + } \ + __nterm = __term; \ + __nterm.c_lflag |= ISIG; \ + if (tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__nterm)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + goto interrupt_err; \ + } \ + __termreset = 1; \ + } \ + } \ +} + +#define TEAR_DOWN_INTERRUPTS { \ + if (F_ISSET(sp->gp, G_ISFROMTTY) && __isig) { \ + if (sigaction(SIGINT, &__oact, NULL)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "signal"); \ + } \ + if (__termreset && tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &__term)) { \ + rval = 1; \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + } \ + F_CLR(sp, S_INTERRUPTED); \ + if (!__istate) \ + F_CLR(sp, S_INTERRUPTIBLE); \ + } \ +} diff --git a/usr.bin/vi/line.c b/usr.bin/vi/line.c new file mode 100644 index 000000000000..a7473491cf17 --- /dev/null +++ b/usr.bin/vi/line.c @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)line.c 8.20 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int)); + +/* + * file_gline -- + * Look in the text buffers for a line; if it's not there + * call file_rline to retrieve it from the database. + */ +char * +file_gline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + TEXT *tp; + recno_t l1, l2; + + /* + * The underlying recno stuff handles zero by returning NULL, but + * have to have an oob condition for the look-aside into the input + * buffer anyway. + */ + if (lno == 0) + return (NULL); + + /* + * Look-aside into the TEXT buffers and see if the line we want + * is there. + */ + if (F_ISSET(sp, S_INPUT)) { + l1 = ((TEXT *)sp->tiq.cqh_first)->lno; + l2 = ((TEXT *)sp->tiq.cqh_last)->lno; + if (l1 <= lno && l2 >= lno) { + for (tp = sp->tiq.cqh_first; + tp->lno != lno; tp = tp->q.cqe_next); + if (lenp) + *lenp = tp->len; + return (tp->lb); + } + /* + * Adjust the line number for the number of lines used + * by the text input buffers. + */ + if (lno > l2) + lno -= l2 - l1; + } + return (file_rline(sp, ep, lno, lenp)); +} + +/* + * file_rline -- + * Look in the cache for a line; if it's not there retrieve + * it from the file. + */ +char * +file_rline(sp, ep, lno, lenp) + SCR *sp; + EXF *ep; + recno_t lno; /* Line number. */ + size_t *lenp; /* Length store. */ +{ + DBT data, key; + + /* Check the cache. */ + if (lno == ep->c_lno) { + if (lenp) + *lenp = ep->c_len; + return (ep->c_lp); + } + ep->c_lno = OOBLNO; + + /* Get the line from the underlying database. */ + key.data = &lno; + key.size = sizeof(lno); + switch (ep->db->get(ep->db, &key, &data, 0)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + /* FALLTHROUGH */ + case 1: + return (NULL); + /* NOTREACHED */ + } + if (lenp) + *lenp = data.size; + + /* Fill the cache. */ + ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + return (data.data); +} + +/* + * file_dline -- + * Delete a line from the file. + */ +int +file_dline(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + DBT key; + +#if defined(DEBUG) && 0 + TRACE(sp, "delete line %lu\n", lno); +#endif + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_DELETE, lno); + global_insdel(sp, ep, LINE_DELETE, lno); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_DELETE); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + if (ep->db->del(ep->db, &key, 0) == 1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to delete line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno <= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + --ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_DELETE, 1)); +} + +/* + * file_aline -- + * Append a line into the file. + */ +int +file_aline(sp, ep, update, lno, p, len) + SCR *sp; + EXF *ep; + int update; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* + * Very nasty special case. The historic vi code displays a single + * space (or a '$' if the list option is set) for the first line in + * an "empty" file. If we "insert" a line, that line gets scrolled + * down, not repainted, so it's incorrect when we refresh the the + * screen. This is really hard to find and fix in the vi code -- the + * text input functions detect it explicitly and don't insert a new + * line. The hack here is to repaint the screen if we're appending + * to an empty file. + */ + if (lno == 0) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to append to line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno < ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno + 1, LOG_LINE_APPEND); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno + 1); + global_insdel(sp, ep, LINE_INSERT, lno + 1); + + /* + * Update screen. + * + * XXX + * Nasty hack. If multiple lines are input by the user, they aren't + * committed until an <ESC> is entered. The problem is the screen was + * updated/scrolled as each line was entered. So, when this routine + * is called to copy the new lines from the cut buffer into the file, + * it has to know not to update the screen again. + */ + return (scr_update(sp, ep, lno, LINE_APPEND, update)); +} + +/* + * file_iline -- + * Insert a line into the file. + */ +int +file_iline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + recno_t lline; + +#if defined(DEBUG) && 0 + TRACE(sp, + "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + + /* Very nasty special case. See comment in file_aline(). */ + if (lno == 1) { + if (file_lline(sp, ep, &lline)) + return (1); + if (lline == 0) + F_SET(sp, S_REDRAW); + } + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to insert at line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, update line count, before screen update. */ + if (lno >= ep->c_lno) + ep->c_lno = OOBLNO; + if (ep->c_nlines != OOBLNO) + ++ep->c_nlines; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log change. */ + log_line(sp, ep, lno, LOG_LINE_INSERT); + + /* + * XXX + * + * Marks and global commands have to know when lines are + * inserted or deleted. + */ + mark_insdel(sp, ep, LINE_INSERT, lno); + global_insdel(sp, ep, LINE_INSERT, lno); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_INSERT, 1)); +} + +/* + * file_sline -- + * Store a line in the file. + */ +int +file_sline(sp, ep, lno, p, len) + SCR *sp; + EXF *ep; + recno_t lno; + char *p; + size_t len; +{ + DBT data, key; + +#if defined(DEBUG) && 0 + TRACE(sp, + "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p); +#endif + /* Log before change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_B); + + /* Update file. */ + key.data = &lno; + key.size = sizeof(lno); + data.data = p; + data.size = len; + if (ep->db->put(ep->db, &key, &data, 0) == -1) { + msgq(sp, M_ERR, + "Error: %s/%d: unable to store line %u: %s.", + tail(__FILE__), __LINE__, lno, strerror(errno)); + return (1); + } + + /* Flush the cache, before logging or screen update. */ + if (lno == ep->c_lno) + ep->c_lno = OOBLNO; + + /* File now dirty. */ + if (F_ISSET(ep, F_FIRSTMODIFY)) + (void)rcv_init(sp, ep); + F_SET(ep, F_MODIFIED); + + /* Log after change. */ + log_line(sp, ep, lno, LOG_LINE_RESET_F); + + /* Update screen. */ + return (scr_update(sp, ep, lno, LINE_RESET, 1)); +} + +/* + * file_lline -- + * Return the number of lines in the file. + */ +int +file_lline(sp, ep, lnop) + SCR *sp; + EXF *ep; + recno_t *lnop; +{ + DBT data, key; + recno_t lno; + + /* Check the cache. */ + if (ep->c_nlines != OOBLNO) { + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ? + ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines); + return (0); + } + + key.data = &lno; + key.size = sizeof(lno); + + switch (ep->db->seq(ep->db, &key, &data, R_LAST)) { + case -1: + msgq(sp, M_ERR, + "Error: %s/%d: unable to get last line: %s.", + tail(__FILE__), __LINE__, strerror(errno)); + *lnop = 0; + return (1); + case 1: + lno = 0; + break; + default: + memmove(&lno, key.data, sizeof(lno)); + break; + } + + /* Fill the cache. */ + ep->c_nlines = ep->c_lno = lno; + ep->c_len = data.size; + ep->c_lp = data.data; + + *lnop = (F_ISSET(sp, S_INPUT) && + ((TEXT *)sp->tiq.cqh_last)->lno > lno ? + ((TEXT *)sp->tiq.cqh_last)->lno : lno); + return (0); +} + +/* + * scr_update -- + * Update all of the screens that are backed by the file that + * just changed. + */ +static inline int +scr_update(sp, ep, lno, op, current) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; + int current; +{ + SCR *tsp; + + if (ep->refcnt != 1) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (sp != tsp && tsp->ep == ep) + (void)sp->s_change(tsp, ep, lno, op); + return (current && sp->s_change(sp, ep, lno, op)); +} diff --git a/usr.bin/vi/log.c b/usr.bin/vi/log.c new file mode 100644 index 000000000000..fdf959f4d8a9 --- /dev/null +++ b/usr.bin/vi/log.c @@ -0,0 +1,663 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)log.c 8.9 (Berkeley) 12/28/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +/* + * The log consists of records, each containing a type byte and a variable + * length byte string, as follows: + * + * LOG_CURSOR_INIT MARK + * LOG_CURSOR_END MARK + * LOG_LINE_APPEND recno_t char * + * LOG_LINE_DELETE recno_t char * + * LOG_LINE_INSERT recno_t char * + * LOG_LINE_RESET_F recno_t char * + * LOG_LINE_RESET_B recno_t char * + * LOG_MARK MARK + * + * We do before image physical logging. This means that the editor layer + * MAY NOT modify records in place, even if simply deleting or overwriting + * characters. Since the smallest unit of logging is a line, we're using + * up lots of space. This may eventually have to be reduced, probably by + * doing logical logging, which is a much cooler database phrase. + * + * The implementation of the historic vi 'u' command, using roll-forward and + * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record, + * followed by a number of other records, followed by a LOG_CURSOR_END record. + * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B + * record, and is the line before the change. The second is LOG_LINE_RESET_F, + * and is the line after the change. Roll-back is done by backing up to the + * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a + * similar fashion. + * + * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END + * record for a line different from the current one. It should be noted that + * this means that a subsequent 'u' command will make a change based on the + * new position of the log's cursor. This is okay, and, in fact, historic vi + * behaved that way. + */ + +static int log_cursor1 __P((SCR *, EXF *, int)); +#if defined(DEBUG) && 0 +static void log_trace __P((SCR *, char *, recno_t, u_char *)); +#endif + +/* Try and restart the log on failure, i.e. if we run out of memory. */ +#define LOG_ERR { \ + msgq(sp, M_ERR, "Error: %s/%d: put log error: %s.", \ + tail(__FILE__), __LINE__, strerror(errno)); \ + (void)ep->log->close(ep->log); \ + if (!log_init(sp, ep)) \ + msgq(sp, M_ERR, "Log restarted."); \ + return (1); \ +} + +/* + * log_init -- + * Initialize the logging subsystem. + */ +int +log_init(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Initialize the buffer. The logging subsystem has its own + * buffers because the global ones are almost by definition + * going to be in use when the log runs. + */ + ep->l_lp = NULL; + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + + ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR, + S_IRUSR | S_IWUSR, DB_RECNO, NULL); + if (ep->log == NULL) { + msgq(sp, M_ERR, "log db: %s", strerror(errno)); + F_SET(ep, F_NOLOG); + return (1); + } + + return (0); +} + +/* + * log_end -- + * Close the logging subsystem. + */ +int +log_end(sp, ep) + SCR *sp; + EXF *ep; +{ + if (ep->log != NULL) { + (void)(ep->log->close)(ep->log); + ep->log = NULL; + } + if (ep->l_lp != NULL) { + free(ep->l_lp); + ep->l_lp = NULL; + } + ep->l_len = 0; + ep->l_cursor.lno = 1; /* XXX Any valid recno. */ + ep->l_cursor.cno = 0; + ep->l_high = ep->l_cur = 1; + return (0); +} + +/* + * log_cursor -- + * Log the current cursor position, starting an event. + */ +int +log_cursor(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * If any changes were made since the last cursor init, + * put out the ending cursor record. + */ + if (ep->l_cursor.lno == OOBLNO) { + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (log_cursor1(sp, ep, LOG_CURSOR_END)); + } + ep->l_cursor.lno = sp->lno; + ep->l_cursor.cno = sp->cno; + return (0); +} + +/* + * log_cursor1 -- + * Actually push a cursor record out. + */ +static int +log_cursor1(sp, ep, type) + SCR *sp; + EXF *ep; + int type; +{ + DBT data, key; + + BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = type; + memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur, + type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end", + sp->lno, sp->cno); +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_line -- + * Log a line change. + */ +int +log_line(sp, ep, lno, action) + SCR *sp; + EXF *ep; + recno_t lno; + u_int action; +{ + DBT data, key; + size_t len; + char *lp; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* + * XXX + * + * Kluge for vi. Clear the EXF undo flag so that the + * next 'u' command does a roll-back, regardless. + */ + F_CLR(ep, F_UNDO); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + /* + * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a + * special case, avoid the caches. Also, if it fails and it's + * line 1, it just means that the user started with an empty file, + * so fake an empty length line. + */ + if (action == LOG_LINE_RESET_B) { + if ((lp = file_rline(sp, ep, lno, &len)) == NULL) { + if (lno != 1) { + GETLINE_ERR(sp, lno); + return (1); + } + len = 0; + lp = ""; + } + } else + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + BINC_RET(sp, + ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t)); + ep->l_lp[0] = action; + memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t)); + memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = len + sizeof(u_char) + sizeof(recno_t); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + +#if defined(DEBUG) && 0 + switch (action) { + case LOG_LINE_APPEND: + TRACE(sp, "%u: log_line: append: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_DELETE: + TRACE(sp, "%lu: log_line: delete: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_INSERT: + TRACE(sp, "%lu: log_line: insert: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_F: + TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n", + ep->l_cur, lno, len); + break; + case LOG_LINE_RESET_B: + TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n", + ep->l_cur, lno, len); + break; + } +#endif + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + + return (0); +} + +/* + * log_mark -- + * Log a mark position. For the log to work, we assume that there + * aren't any operations that just put out a log record -- this + * would mean that undo operations would only reset marks, and not + * cause any other change. + */ +int +log_mark(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + DBT data, key; + + if (F_ISSET(ep, F_NOLOG)) + return (0); + + /* Put out one initial cursor record per set of changes. */ + if (ep->l_cursor.lno != OOBLNO) { + if (log_cursor1(sp, ep, LOG_CURSOR_INIT)) + return (1); + ep->l_cursor.lno = OOBLNO; + } + + BINC_RET(sp, ep->l_lp, + ep->l_len, sizeof(u_char) + sizeof(MARK)); + ep->l_lp[0] = LOG_MARK; + memmove(ep->l_lp + sizeof(u_char), mp, sizeof(MARK)); + + key.data = &ep->l_cur; + key.size = sizeof(recno_t); + data.data = ep->l_lp; + data.size = sizeof(u_char) + sizeof(MARK); + if (ep->log->put(ep->log, &key, &data, 0) == -1) + LOG_ERR; + + /* Reset high water mark. */ + ep->l_high = ++ep->l_cur; + return (0); +} + +/* + * Log_backward -- + * Roll the log backward one operation. + */ +int +log_backward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) { + msgq(sp, M_BERR, "No changes to undo."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_backward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + if (didop) { + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_setline -- + * Reset the line to its original appearance. + * + * XXX + * There's a bug in this code due to our not logging cursor movements + * unless a change was made. If you do a change, move off the line, + * then move back on and do a 'U', the line will be restored to the way + * it was before the original change. + */ +int +log_setline(sp, ep) + SCR *sp; + EXF *ep; +{ + DBT key, data; + MARK m; + recno_t lno; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, undo not possible."); + return (1); + } + + if (ep->l_cur == 1) + return (1); + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + + for (;;) { + --ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_setline", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno || ep->l_cur == 1) { + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (m.lno != sp->lno) { + ++ep->l_cur; + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + case LOG_LINE_DELETE: + case LOG_LINE_RESET_F: + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (lno == sp->lno && + file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +/* + * Log_forward -- + * Roll the log forward one operation. + */ +int +log_forward(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + DBT key, data; + MARK m; + recno_t lno; + int didop; + u_char *p; + + if (F_ISSET(ep, F_NOLOG)) { + msgq(sp, M_ERR, + "Logging not being performed, roll-forward not possible."); + return (1); + } + + if (ep->l_cur == ep->l_high) { + msgq(sp, M_BERR, "No changes to re-do."); + return (1); + } + + F_SET(ep, F_NOLOG); /* Turn off logging. */ + + key.data = &ep->l_cur; /* Initialize db request. */ + key.size = sizeof(recno_t); + for (didop = 0;;) { + ++ep->l_cur; + if (ep->log->get(ep->log, &key, &data, 0)) + LOG_ERR; +#if defined(DEBUG) && 0 + log_trace(sp, "log_forward", ep->l_cur, data.data); +#endif + switch (*(p = (u_char *)data.data)) { + case LOG_CURSOR_END: + if (didop) { + ++ep->l_cur; + memmove(rp, p + sizeof(u_char), sizeof(MARK)); + F_CLR(ep, F_NOLOG); + return (0); + } + break; + case LOG_CURSOR_INIT: + break; + case LOG_LINE_APPEND: + case LOG_LINE_INSERT: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_iline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_ADDED]; + break; + case LOG_LINE_DELETE: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_dline(sp, ep, lno)) + goto err; + ++sp->rptlines[L_DELETED]; + break; + case LOG_LINE_RESET_B: + break; + case LOG_LINE_RESET_F: + didop = 1; + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + if (file_sline(sp, ep, lno, p + sizeof(u_char) + + sizeof(recno_t), data.size - sizeof(u_char) - + sizeof(recno_t))) + goto err; + ++sp->rptlines[L_CHANGED]; + break; + case LOG_MARK: + didop = 1; + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + if (mark_set(sp, ep, m.name, &m, 0)) + goto err; + break; + default: + abort(); + } + } + +err: F_CLR(ep, F_NOLOG); + return (1); +} + +#if defined(DEBUG) && 0 +static void +log_trace(sp, msg, rno, p) + SCR *sp; + char *msg; + recno_t rno; + u_char *p; +{ + MARK m; + recno_t lno; + + switch (*p) { + case LOG_CURSOR_INIT: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_CURSOR_END: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno); + break; + case LOG_LINE_APPEND: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno); + break; + case LOG_LINE_INSERT: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno); + break; + case LOG_LINE_DELETE: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_F: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno); + break; + case LOG_LINE_RESET_B: + memmove(&lno, p + sizeof(u_char), sizeof(recno_t)); + TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno); + break; + case LOG_MARK: + memmove(&m, p + sizeof(u_char), sizeof(MARK)); + TRACE(sp, "%lu: %s: MARK: %u/%u\n", rno, msg, m.lno, m.cno); + break; + default: + abort(); + } +} +#endif diff --git a/usr.bin/vi/log.h b/usr.bin/vi/log.h new file mode 100644 index 000000000000..840cf8532c0c --- /dev/null +++ b/usr.bin/vi/log.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)log.h 8.3 (Berkeley) 12/28/93 + */ + +#define LOG_NOTYPE 0 +#define LOG_CURSOR_INIT 1 +#define LOG_CURSOR_END 2 +#define LOG_LINE_APPEND 3 +#define LOG_LINE_DELETE 4 +#define LOG_LINE_INSERT 5 +#define LOG_LINE_RESET_F 6 +#define LOG_LINE_RESET_B 7 +#define LOG_MARK 8 + +int log_backward __P((SCR *, EXF *, MARK *)); +int log_cursor __P((SCR *, EXF *)); +int log_end __P((SCR *, EXF *)); +int log_forward __P((SCR *, EXF *, MARK *)); +int log_init __P((SCR *, EXF *)); +int log_line __P((SCR *, EXF *, recno_t, u_int)); +int log_mark __P((SCR *, EXF *, MARK *)); +int log_setline __P((SCR *, EXF *)); diff --git a/usr.bin/vi/main.c b/usr.bin/vi/main.c new file mode 100644 index 000000000000..3413a475ddf8 --- /dev/null +++ b/usr.bin/vi/main.c @@ -0,0 +1,743 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.65 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" +#include "tag.h" + +static int exrc_isok __P((SCR *, char *, int)); +static void gs_end __P((GS *)); +static GS *gs_init __P((void)); +static void h_hup __P((int)); +static void h_term __P((int)); +static void h_winch __P((int)); +static void obsolete __P((char *[])); +static void usage __P((int)); + +GS *__global_list; /* GLOBAL: List of screens. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + static int reenter; /* STATIC: Re-entrancy check. */ + struct sigaction act; + GS *gp; + FREF *frp; + SCR *sp; + u_int flags, saved_vi_mode; + int ch, eval, flagchk, readonly, silent, snapshot; + char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg; + char path[MAXPATHLEN]; + + /* Stop if indirecting through a NULL pointer. */ + if (reenter++) + abort(); + + /* Set screen type and mode based on the program name. */ + readonly = 0; + if ((myname = strrchr(*argv, '/')) == NULL) + myname = *argv; + else + ++myname; + if (!strcmp(myname, "ex") || !strcmp(myname, "nex")) + LF_INIT(S_EX); + else { + /* View is readonly. */ + if (!strcmp(myname, "view")) + readonly = 1; + LF_INIT(S_VI_CURSES); + } + saved_vi_mode = S_VI_CURSES; + + /* Convert old-style arguments into new-style ones. */ + obsolete(argv); + + /* Parse the arguments. */ + flagchk = '\0'; + excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL; + silent = 0; + snapshot = 1; + while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF) + switch (ch) { + case 'c': /* Run the command. */ + excmdarg = optarg; + break; + case 'e': /* Ex mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_EX); + break; + case 'F': /* No snapshot. */ + snapshot = 0; + break; + case 'l': + if (flagchk != '\0' && flagchk != 'l') + errx(1, + "only one of -%c and -l may be specified.", + flagchk); + flagchk = 'l'; + break; + case 'R': /* Readonly. */ + readonly = 1; + break; + case 'r': /* Recover. */ + if (flagchk == 'r') + errx(1, + "only one recovery file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -r may be specified.", + flagchk); + flagchk = 'r'; + rec_f = optarg; + break; + case 's': + if (!LF_ISSET(S_EX)) + errx(1, "-s only applicable to ex."); + silent = 1; + break; + case 'T': /* Trace. */ + trace_f = optarg; + break; + case 't': /* Tag. */ + if (flagchk == 't') + errx(1, + "only one tag file may be specified."); + if (flagchk != '\0') + errx(1, + "only one of -%c and -t may be specified.", + flagchk); + flagchk = 't'; + tag_f = optarg; + break; + case 'v': /* Vi mode. */ + LF_CLR(S_SCREENS); + LF_SET(S_VI_CURSES); + break; + case 'w': + wsizearg = optarg; + break; + case 'x': + if (!strcmp(optarg, "aw")) { + LF_CLR(S_SCREENS); + LF_SET(S_VI_XAW); + saved_vi_mode = S_VI_XAW; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(LF_ISSET(S_EX)); + } + argc -= optind; + argv += optind; + + /* Build and initialize the GS structure. */ + __global_list = gp = gs_init(); + + if (snapshot) + F_SET(gp, G_SNAPSHOT); + + /* + * Build and initialize the first/current screen. This is a bit + * tricky. If an error is returned, we may or may not have a + * screen structure. If we have a screen structure, put it on a + * display queue so that the error messages get displayed. + */ + if (screen_init(NULL, &sp, flags)) { + if (sp != NULL) + CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); + goto err; + } + sp->saved_vi_mode = saved_vi_mode; + CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); + + if (trace_f != NULL) { +#ifdef DEBUG + if ((gp->tracefp = fopen(optarg, "w")) == NULL) + err(1, "%s", optarg); + (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg); +#else + msgq(sp, M_ERR, "-T support not compiled into this version."); +#endif + } + + if (set_window_size(sp, 0, 0)) /* Set the window size. */ + goto err; + if (opts_init(sp)) /* Options initialization. */ + goto err; + if (readonly) /* Global read-only bit. */ + O_SET(sp, O_READONLY); + if (silent) { /* Ex batch mode. */ + O_CLR(sp, O_AUTOPRINT); + O_CLR(sp, O_PROMPT); + O_CLR(sp, O_VERBOSE); + O_CLR(sp, O_WARN); + F_SET(sp, S_EXSILENT); + } + if (wsizearg != NULL) { + ARGS *av[2], a, b; + if (strtol(optarg, &p, 10) < 0 || *p) + errx(1, "illegal window size -- %s", optarg); + (void)snprintf(path, sizeof(path), "window=%s", optarg); + a.bp = (CHAR_T *)path; + a.len = strlen(path); + b.bp = NULL; + b.len = 0; + av[0] = &a; + av[1] = &b; + if (opts_set(sp, av)) + msgq(sp, M_ERR, + "Unable to set command line window option"); + } + + /* Keymaps, special keys, must follow option initializations. */ + if (term_init(sp)) + goto err; + +#ifdef DIGRAPHS + if (digraph_init(sp)) /* Digraph initialization. */ + goto err; +#endif + + /* + * Source the system, environment, ~user and local .exrc values. + * Vi historically didn't check ~user/.exrc if the environment + * variable EXINIT was set. This is all done before the file is + * read in because things in the .exrc information can set, for + * example, the recovery directory. + * + * !!! + * While nvi can handle any of the options settings of historic vi, + * the converse is not true. Since users are going to have to have + * files and environmental variables that work with both, we use nvi + * versions if they exist, otherwise the historic ones. + */ + if (!silent) { + if (exrc_isok(sp, _PATH_SYSEXRC, 1)) + (void)ex_cfile(sp, NULL, _PATH_SYSEXRC); + + /* Source the {N,}EXINIT environment variable. */ + if ((p = getenv("NEXINIT")) != NULL || + (p = getenv("EXINIT")) != NULL) + if ((p = strdup(p)) == NULL) { + msgq(sp, M_SYSERR, NULL); + goto err; + } else { + (void)ex_icmd(sp, NULL, p, strlen(p)); + free(p); + } + else if ((p = getenv("HOME")) != NULL && *p) { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_NEXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + else { + (void)snprintf(path, + sizeof(path), "%s/%s", p, _PATH_EXRC); + if (exrc_isok(sp, path, 0)) + (void)ex_cfile(sp, NULL, path); + } + } + /* + * !!! + * According to O'Reilly ("Learning the VI Editor", Fifth Ed., + * May 1992, page 106), System V release 3.2 and later, has an + * option "[no]exrc", causing vi to not "read .exrc files in + * the current directory unless you first set the exrc option + * in your home directory's .exrc file". Yeah, right. Did + * someone actually believe that users would change their home + * .exrc file based on whether or not they wanted to source the + * current local .exrc? Or that users would want ALL the local + * .exrc files on some systems, and none of them on others? + * I think not. + * + * Apply the same tests to local .exrc files that are applied + * to any other .exrc file. + */ + if (exrc_isok(sp, _PATH_EXRC, 0)) + (void)ex_cfile(sp, NULL, _PATH_EXRC); + } + + /* List recovery files if -l specified. */ + if (flagchk == 'l') + exit(rcv_list(sp)); + + /* Use a tag file or recovery file if specified. */ + if (tag_f != NULL && ex_tagfirst(sp, tag_f)) + goto err; + else if (rec_f != NULL && rcv_read(sp, rec_f)) + goto err; + + /* Append any remaining arguments as file names. */ + if (*argv != NULL) + for (; *argv != NULL; ++argv) + if (file_add(sp, NULL, *argv, 0) == NULL) + goto err; + + /* + * If no recovery or tag file, get an EXF structure. + * If no argv file, use a temporary file. + */ + if (tag_f == NULL && rec_f == NULL) { + if ((frp = file_first(sp)) == NULL && + (frp = file_add(sp, NULL, NULL, 1)) == NULL) + goto err; + if (file_init(sp, frp, NULL, 0)) + goto err; + } + + /* Set up the argument pointer. */ + sp->a_frp = sp->frp; + + /* + * Initialize the signals. Use sigaction(2), not signal(3), because + * we don't want to always restart system calls on 4BSD systems. It + * would be nice in some cases to restart system calls, but SA_RESTART + * is a 4BSD extension so we can't use it. + * + * SIGWINCH, SIGHUP, SIGTERM: + * Catch and set a global bit. + */ + act.sa_handler = h_hup; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGHUP, &act, NULL); + act.sa_handler = h_term; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGTERM, &act, NULL); + act.sa_handler = h_winch; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGWINCH, &act, NULL); + + /* + * SIGQUIT: + * Always ignore. + */ + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGQUIT, &act, NULL); + + /* + * If there's an initial command, push it on the command stack. + * Historically, it was always an ex command, not vi in vi mode + * or ex in ex mode. So, make it look like an ex command to vi. + */ + if (excmdarg != NULL) + if (IN_EX_MODE(sp)) { + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err; + } else if (IN_VI_MODE(sp)) { + if (term_push(sp, "\n", 1, 0, 0)) + goto err; + if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) + goto err; + if (term_push(sp, ":", 1, 0, 0)) + goto err; + } + + /* Vi reads from the terminal. */ + if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) { + msgq(sp, M_ERR, "Vi's standard input must be a terminal."); + goto err; + } + + for (;;) { + if (sp->s_edit(sp, sp->ep)) + goto err; + + /* + * Edit the next screen on the display queue, or, move + * a screen from the hidden queue to the display queue. + */ + if ((sp = __global_list->dq.cqh_first) == + (void *)&__global_list->dq) + if ((sp = __global_list->hq.cqh_first) != + (void *)&__global_list->hq) { + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + } else + break; + + /* + * The screen type may have changed -- reinitialize the + * functions in case it has. + */ + switch (F_ISSET(sp, S_SCREENS)) { + case S_EX: + if (sex_screen_init(sp)) + goto err; + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + goto err; + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + goto err; + break; + default: + abort(); + } + } + + eval = 0; + if (0) +err: eval = 1; + + /* + * NOTE: sp may be GONE when the screen returns, so only + * the gp can be trusted. + */ + gs_end(gp); + + /* + * XXX + * Make absolutely sure that the modes are restored correctly. + * + * This should no longer be needed, and it's here to handle what I + * believe are SunOS/Solaris curses problems. The problem is that + * for some unknown reason, when endwin() is called in the svi + * routines, it isn't resetting the terminal correctly. I have not + * been able to figure it out, so this resets the terminal to the + * right modes regardless. The problem is that, in most tty driver + * implementations, you can only reset the terminal modes once + * (changing from !ICANON to ICANON) without losing the re-parsing + * effect on the pending input. This means that this "fix" will make + * other systems mess up characters typed after the quit command to + * vi but before vi actually exits. + */ + if (F_ISSET(gp, G_ISFROMTTY)) + (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios); + exit(eval); +} + +/* + * gs_init -- + * Build and initialize the GS structure. + */ +static GS * +gs_init() +{ + GS *gp; + int fd; + + CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS)); + if (gp == NULL) + err(1, NULL); + + CIRCLEQ_INIT(&gp->dq); + CIRCLEQ_INIT(&gp->hq); + LIST_INIT(&gp->msgq); + + /* Structures shared by screens so stored in the GS structure. */ + CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF)); + if (gp->tty == NULL) + err(1, NULL); + + LIST_INIT(&gp->cutq); + LIST_INIT(&gp->seqq); + + /* Set a flag if we're reading from the tty. */ + if (isatty(STDIN_FILENO)) + F_SET(gp, G_ISFROMTTY); + + /* + * XXX + * Set a flag and don't do terminal sets/resets if the input isn't + * from a tty. Under all circumstances put reasonable things into + * the original_termios field, as some routines (seq.c:seq_save() + * and term.c:term_init()) want values for special characters. + */ + if (F_ISSET(gp, G_ISFROMTTY)) { + if (tcgetattr(STDIN_FILENO, &gp->original_termios)) + err(1, "tcgetattr"); + } else { + if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1) + err(1, "%s", _PATH_TTY); + if (tcgetattr(fd, &gp->original_termios)) + err(1, "tcgetattr"); + (void)close(fd); + } + + return (gp); +} + + +/* + * gs_end -- + * End the GS structure. + */ +static void +gs_end(gp) + GS *gp; +{ + MSG *mp; + SCR *sp; + char *tty; + + /* Reset anything that needs resetting. */ + if (gp->flags & G_SETMODE) /* O_MESG */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) + warn("ttyname"); + else if (chmod(tty, gp->origmode) < 0) + warn("%s", tty); + + /* Ring the bell if scheduled. */ + if (F_ISSET(gp, G_BELLSCHED)) + (void)fprintf(stderr, "\07"); /* \a */ + + /* If there are any remaining screens, flush their messages. */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + /* Flush messages on the global queue. */ + for (mp = gp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) + (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); + + if (gp->special_key != NULL) + FREE(gp->special_key, MAX_FAST_KEY); + + /* + * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN + * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED. + */ +} + +/* + * h_hup -- + * Handle SIGHUP. + */ +static void +h_hup(signo) + int signo; +{ + F_SET(__global_list, G_SIGHUP); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_hup(); +} + +/* + * h_term -- + * Handle SIGTERM. + */ +static void +h_term(signo) + int signo; +{ + F_SET(__global_list, G_SIGTERM); + + /* + * If we're asleep, just die. + * + * XXX + * This isn't right if the windows are independent. + */ + if (F_ISSET(__global_list, G_SLEEPING)) + rcv_term(); +} + +/* + * h_winch -- + * Handle SIGWINCH. + */ +static void +h_winch(signo) + int signo; +{ + F_SET(__global_list, G_SIGWINCH); +} + +/* + * exrc_isok -- + * Check a .exrc for source-ability. + */ +static int +exrc_isok(sp, path, rootok) + SCR *sp; + char *path; + int rootok; +{ + struct stat sb; + uid_t uid; + char *emsg, buf[MAXPATHLEN]; + + /* Check for the file's existence. */ + if (stat(path, &sb)) + return (0); + + /* + * !!! + * Historically, vi did not read the .exrc files if they were owned + * by someone other than the user, unless the undocumented option + * sourceany was set. We don't support the sourceany option. We + * check that the user (or root, for system files) owns the file and + * require that it not be writeable by anyone other than the owner. + */ + + /* Owned by the user or root. */ + uid = getuid(); + if (rootok) { + if (sb.st_uid != uid && sb.st_uid != 0) { + emsg = "not owned by you or root"; + goto err; + } + } else + if (sb.st_uid != uid) { + emsg = "not owned by you"; + goto err; + } + + /* Not writeable by anyone but the owner. */ + if (sb.st_mode & (S_IWGRP | S_IWOTH)) { + emsg = "writeable by a user other than the owner"; +err: if (strchr(path, '/') == NULL && + getcwd(buf, sizeof(buf)) != NULL) + msgq(sp, M_ERR, + "%s/%s: not sourced: %s.", buf, path, emsg); + else + msgq(sp, M_ERR, + "%s: not sourced: %s.", path, emsg); + return (0); + } + return (1); +} + +static void +obsolete(argv) + char *argv[]; +{ + size_t len; + char *p, *myname; + + /* + * Translate old style arguments into something getopt will like. + * Make sure it's not text space memory, because ex changes the + * strings. + * Change "+" into "-c$". + * Change "+<anything else>" into "-c<anything else>". + * Change "-" into "-s" + * Change "-r" into "-l" + */ + for (myname = argv[0]; *++argv;) + if (argv[0][0] == '+') { + if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 4); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-c$"); + } else { + p = argv[0]; + len = strlen(argv[0]); + MALLOC_NOMSG(NULL, argv[0], char *, len + 2); + if (argv[0] == NULL) + err(1, NULL); + argv[0][0] = '-'; + argv[0][1] = 'c'; + (void)strcpy(argv[0] + 2, p + 1); + } + } else if (argv[0][0] == '-') { + if (argv[0][1] == 'r') { + if (argv[0][2] == '\0' && argv[1] == NULL) + argv[0][1] = 'l'; + } else if (argv[0][1] == '\0') { + MALLOC_NOMSG(NULL, argv[0], char *, 3); + if (argv[0] == NULL) + err(1, NULL); + (void)strcpy(argv[0], "-s"); + } + } +} + +static void +usage(is_ex) + int is_ex; +{ +#define EX_USAGE \ + "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" +#define VI_USAGE \ + "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" + + (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE); + exit(1); +} diff --git a/usr.bin/vi/mark.c b/usr.bin/vi/mark.c new file mode 100644 index 000000000000..c510a22002fc --- /dev/null +++ b/usr.bin/vi/mark.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mark.c 8.12 (Berkeley) 12/27/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" + +static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T)); + +/* + * Marks are maintained in a key sorted doubly linked list. We can't + * use arrays because we have no idea how big an index key could be. + * The underlying assumption is that users don't have more than, say, + * 10 marks at any one time, so this will be is fast enough. + * + * Marks are fixed, and modifications to the line don't update the mark's + * position in the line. This can be hard. If you add text to the line, + * place a mark in that text, undo the addition and use ` to move to the + * mark, the location will have disappeared. It's tempting to try to adjust + * the mark with the changes in the line, but this is hard to do, especially + * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi + * would move to the first non-blank on the line when the mark location was + * past the end of the line. This can be complicated by deleting to a mark + * that has disappeared using the ` command. Historic vi vi treated this as + * a line-mode motion and deleted the line. This implementation complains to + * the user. + * + * In historic vi, marks returned if the operation was undone, unless the + * mark had been subsequently reset. Tricky. This is hard to start with, + * but in the presence of repeated undo it gets nasty. When a line is + * deleted, we delete (and log) any marks on that line. An undo will create + * the mark. Any mark creations are noted as to whether the user created + * it or if it was created by an undo. The former cannot be reset by another + * undo, but the latter may. + * + * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of + * the absolute mark locations sets both, so that "m'" and "m`" work like + * they, ah, for lack of a better word, "should". + */ + +/* + * mark_init -- + * Set up the marks. + */ +int +mark_init(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + /* + * Make sure the marks have been set up. If they + * haven't, do so, and create the absolute mark. + */ + MALLOC_RET(sp, mp, MARK *, sizeof(MARK)); + mp->lno = 1; + mp->cno = 0; + mp->name = ABSMARK1; + mp->flags = 0; + LIST_INSERT_HEAD(&ep->marks, mp, q); + return (0); +} + +/* + * mark_end -- + * Free up the marks. + */ +int +mark_end(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK *mp; + + while ((mp = ep->marks.lh_first) != NULL) { + LIST_REMOVE(mp, q); + FREE(mp, sizeof(MARK)); + } + return (0); +} + +/* + * mark_get -- + * Get the location referenced by a mark. + */ +MARK * +mark_get(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp; + size_t len; + char *p; + + if (key == ABSMARK2) + key = ABSMARK1; + + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key)); + return (NULL); + } + if (F_ISSET(mp, MARK_DELETED)) { + msgq(sp, M_BERR, + "Mark %s: the line was deleted.", charname(sp, key)); + return (NULL); + } + if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL || + mp->cno > len || mp->cno == len && len != 0) { + msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.", + charname(sp, key)); + return (NULL); + } + return (mp); +} + +/* + * mark_set -- + * Set the location referenced by a mark. + */ +int +mark_set(sp, ep, key, value, userset) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; + MARK *value; + int userset; +{ + MARK *mp, *mt; + + if (key == ABSMARK2) + key = ABSMARK1; + + /* + * The rules are simple. If the user is setting a mark (if it's a + * new mark this is always true), it always happens. If not, it's + * an undo, and we set it if it's not already set or if it was set + * by a previous undo. + */ + mp = mark_find(sp, ep, key); + if (mp == NULL || mp->name != key) { + MALLOC_RET(sp, mt, MARK *, sizeof(MARK)); + if (mp == NULL) { + LIST_INSERT_HEAD(&ep->marks, mt, q); + } else + LIST_INSERT_AFTER(mp, mt, q); + mp = mt; + } else if (!userset && + !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET)) + return (0); + + mp->lno = value->lno; + mp->cno = value->cno; + mp->name = key; + mp->flags = userset ? MARK_USERSET : 0; + return (0); +} + +/* + * mark_find -- + * Find the requested mark, or, the slot immediately before + * where it would go. + */ +static MARK * +mark_find(sp, ep, key) + SCR *sp; + EXF *ep; + ARG_CHAR_T key; +{ + MARK *mp, *lastmp; + + /* + * Return the requested mark or the slot immediately before + * where it should go. + */ + for (lastmp = NULL, mp = ep->marks.lh_first; + mp != NULL; lastmp = mp, mp = mp->q.le_next) + if (mp->name >= key) + return (mp->name == key ? mp : lastmp); + return (lastmp); +} + +/* + * mark_insdel -- + * Update the marks based on an insertion or deletion. + */ +void +mark_insdel(sp, ep, op, lno) + SCR *sp; + EXF *ep; + enum operation op; + recno_t lno; +{ + MARK *mp; + + switch (op) { + case LINE_APPEND: + return; + case LINE_DELETE: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + if (mp->lno == lno) { + F_SET(mp, MARK_DELETED); + (void)log_mark(sp, ep, mp); + } else + --mp->lno; + return; + case LINE_INSERT: + for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) + if (mp->lno >= lno) + ++mp->lno; + return; + case LINE_RESET: + return; + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/mark.h b/usr.bin/vi/mark.h new file mode 100644 index 000000000000..9c28151314b9 --- /dev/null +++ b/usr.bin/vi/mark.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mark.h 8.5 (Berkeley) 12/27/93 + */ + +/* + * The MARK structure defines a position in the file. Because of the different + * interfaces used by the db(3) package, curses, and users, the line number is + * 1 based, while the column number is 0 based. Additionally, it is known that + * the out-of-band line number is less than any legal line number. The line + * number is of type recno_t, as that's the underlying type of the database. + * The column number is of type size_t, guaranteeing that we can malloc a line. + */ +struct _mark { + LIST_ENTRY(_mark) q; /* Linked list of marks. */ +#define OOBLNO 0 /* Out-of-band line number. */ + recno_t lno; /* Line number. */ + size_t cno; /* Column number. */ + CHAR_T name; /* Mark name. */ + +#define MARK_DELETED 0x01 /* Mark was deleted. */ +#define MARK_USERSET 0x02 /* User set this mark. */ + u_char flags; +}; + +#define ABSMARK1 '\'' /* Absolute mark name. */ +#define ABSMARK2 '`' /* Absolute mark name. */ + +/* Mark routines. */ +int mark_end __P((SCR *, EXF *)); +MARK *mark_get __P((SCR *, EXF *, ARG_CHAR_T)); +int mark_init __P((SCR *, EXF *)); +void mark_insdel __P((SCR *, EXF *, enum operation, recno_t)); +int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int)); diff --git a/usr.bin/vi/mem.h b/usr.bin/vi/mem.h new file mode 100644 index 000000000000..0f8fac4b8f39 --- /dev/null +++ b/usr.bin/vi/mem.h @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mem.h 8.2 (Berkeley) 12/19/93 + */ + +/* Increase the size of a malloc'd buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define BINC_GOTO(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + goto binc_err; \ +} +#define BINC_RET(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + return (1); \ +} + +/* + * Get some temporary space, preferably from the global temporary buffer, + * from a malloc'd buffer otherwise. Two versions, one that returns, one + * that jumps to an error label. + */ +#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_GOTO(sp, bp, blen, nlen); \ + } else { \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} +#define GET_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (F_ISSET(__gp, G_TMP_INUSE)) { \ + bp = NULL; \ + blen = 0; \ + BINC_RET(sp, bp, blen, nlen); \ + } else { \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } \ +} + +/* + * Add space to a GET_SPACE returned buffer. Two versions, one that + * returns, one that jumps to an error label. + */ +#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_GOTO(sp, bp, blen, nlen); \ +} +#define ADD_SPACE_RET(sp, bp, blen, nlen) { \ + GS *__gp = (sp)->gp; \ + if (bp == __gp->tmp_bp) { \ + F_CLR(__gp, G_TMP_INUSE); \ + BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \ + bp = __gp->tmp_bp; \ + blen = __gp->tmp_blen; \ + F_SET(__gp, G_TMP_INUSE); \ + } else \ + BINC_RET(sp, bp, blen, nlen); \ +} + +/* Free memory, optionally making pointers unusable. */ +#ifdef DEBUG +#define FREE(p, sz) { \ + memset(p, 0xff, sz); \ + free(p); \ +} +#else +#define FREE(p, sz) free(p); +#endif + +/* Free a GET_SPACE returned buffer. */ +#define FREE_SPACE(sp, bp, blen) { \ + if (bp == sp->gp->tmp_bp) \ + F_CLR(sp->gp, G_TMP_INUSE); \ + else \ + FREE(bp, blen); \ +} + +/* + * Malloc a buffer, casting the return pointer. Various versions. + * + * !!! + * The cast should be unnecessary, malloc(3) and friends return void *'s, + * which is all we need. However, some systems that nvi needs to run on + * don't do it right yet, resulting in the compiler printing out roughly + * a million warnings. After awhile, it seemed easier to put the casts + * in instead of explaining it all the time. + */ +#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \ + p = (cast)calloc(nmemb, size); \ +} +#define CALLOC(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define CALLOC_RET(sp, p, cast, nmemb, size) { \ + if ((p = (cast)calloc(nmemb, size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define MALLOC_NOMSG(sp, p, cast, size) { \ + p = (cast)malloc(size); \ +} +#define MALLOC(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} +#define MALLOC_RET(sp, p, cast, size) { \ + if ((p = (cast)malloc(size)) == NULL) { \ + msgq(sp, M_SYSERR, NULL); \ + return (1); \ + } \ +} +#define REALLOC(sp, p, cast, size) { \ + if ((p = (cast)realloc(p, size)) == NULL) \ + msgq(sp, M_SYSERR, NULL); \ +} + +int binc __P((SCR *, void *, size_t *, size_t)); diff --git a/usr.bin/vi/msg.h b/usr.bin/vi/msg.h new file mode 100644 index 000000000000..6b20bb4bb63b --- /dev/null +++ b/usr.bin/vi/msg.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)msg.h 8.8 (Berkeley) 11/18/93 + */ + +/* + * M_BERR -- Error: ring a bell if O_VERBOSE not set, else + * display in inverse video. + * M_ERR -- Error: display in inverse video. + * M_INFO -- Info: display in normal video. + * M_SYSERR -- M_ERR, but use standard error message. + * M_VINFO -- Info: display only if O_VERBOSE set. + * + * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the + * error messages shorter. In this version, O_TERSE has no effect + * and O_VERBOSE results in informational displays about common + * errors. + */ +enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO }; + +typedef struct _msgh MSGH; /* MESG list head structure. */ +LIST_HEAD(_msgh, _msg); + +struct _msg { + LIST_ENTRY(_msg) q; /* Linked list of messages. */ + char *mbuf; /* Message buffer. */ + size_t blen; /* Message buffer length. */ + size_t len; /* Message length. */ + +#define M_EMPTY 0x01 /* No message. */ +#define M_INV_VIDEO 0x02 /* Inverse video. */ + u_int flags; /* Flags. */ +}; + +/* Messages. */ +void msg_app __P((GS *, SCR *, int, char *, size_t)); +int msg_rpt __P((SCR *, int)); +void msgq __P((SCR *, enum msgtype, const char *, ...)); diff --git a/usr.bin/vi/nex/ex.c b/usr.bin/vi/nex/ex.c new file mode 100644 index 000000000000..3f5beac4a734 --- /dev/null +++ b/usr.bin/vi/nex/ex.c @@ -0,0 +1,1523 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex.c 8.90 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +static inline EXCMDLIST const * + ex_comm_search __P((char *, size_t)); +static int ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *)); +static int ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *)); + +#define DEFCOM ".+1" + +/* + * ex -- + * Read an ex command and execute it. + */ +int +ex(sp, ep) + SCR *sp; + EXF *ep; +{ + TEXT *tp; + u_int saved_mode; + int eval; + char defcom[sizeof(DEFCOM)]; + + if (ex_init(sp, ep)) + return (1); + + if (sp->s_refresh(sp, ep)) + return (ex_end(sp)); + + /* If reading from a file, messages should have line info. */ + if (!F_ISSET(sp->gp, G_ISFROMTTY)) { + sp->if_lno = 1; + sp->if_name = strdup("input"); + } + for (eval = 0;; ++sp->if_lno) { + /* Get the next command. */ + switch (sp->s_get(sp, ep, &sp->tiq, ':', TXT_CR | TXT_PROMPT)) { + case INP_OK: + break; + case INP_EOF: + F_SET(sp, S_EXIT_FORCE); + goto ret; + case INP_ERR: + continue; + } + + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + tp = sp->tiq.cqh_first; + if (tp->len == 0) { + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + (void)fputc('\r', stdout); + (void)fflush(stdout); + } + memmove(defcom, DEFCOM, sizeof(DEFCOM)); + (void)ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1); + } else { + if (F_ISSET(sp->gp, G_ISFROMTTY)) + (void)fputc('\n', stdout); + (void)ex_icmd(sp, ep, tp->lb, tp->len); + } + (void)msg_rpt(sp, 0); + + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) + break; + + if (sp->s_refresh(sp, ep)) { + eval = 1; + break; + } + } +ret: if (sp->if_name != NULL) { + FREE(sp->if_name, strlen(sp->if_name) + 1); + sp->if_name = NULL; + } + return (ex_end(sp) || eval); +} + +/* + * ex_cfile -- + * Execute ex commands from a file. + */ +int +ex_cfile(sp, ep, filename) + SCR *sp; + EXF *ep; + char *filename; +{ + struct stat sb; + int fd, len, rval; + char *bp; + + bp = NULL; + if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb)) + goto err; + + /* + * XXX + * We'd like to test if the file is too big to malloc. Since we don't + * know what size or type off_t's or size_t's are, what the largest + * unsigned integral type is, or what random insanity the local C + * compiler will perpetrate, doing the comparison in a portable way + * is flatly impossible. Hope that malloc fails if the file is too + * large. + */ + MALLOC(sp, bp, char *, (size_t)sb.st_size + 1); + if (bp == NULL) + goto err; + + len = read(fd, bp, (int)sb.st_size); + if (len == -1 || len != sb.st_size) { + if (len != sb.st_size) + errno = EIO; +err: rval = 1; + msgq(sp, M_SYSERR, filename); + } else { + bp[sb.st_size] = '\0'; /* XXX */ + + /* Run the command. Messages include file/line information. */ + sp->if_lno = 1; + sp->if_name = strdup(filename); + rval = ex_icmd(sp, ep, bp, len); + FREE(sp->if_name, strlen(sp->if_name) + 1); + sp->if_name = NULL; + } + + /* + * !!! + * THE UNDERLYING EXF MAY HAVE CHANGED. + */ + if (bp != NULL) + FREE(bp, sb.st_size); + if (fd >= 0) + (void)close(fd); + return (rval); +} + +/* + * ex_icmd -- + * Call ex_cmd() after turning off interruptible bits. + */ +int +ex_icmd(sp, ep, cmd, len) + SCR *sp; + EXF *ep; + char *cmd; + size_t len; +{ + /* + * Ex goes through here for each vi :colon command and for each ex + * command, however, globally executed commands don't go through + * here, instead, they call ex_cmd directly. So, reset all of the + * interruptible flags now. + */ + F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE); + + return (ex_cmd(sp, ep, cmd, len)); +} + +/* Special command structure for :s as a repeat substitution command. */ +static EXCMDLIST const cmd_subagain = + {"s", ex_subagain, E_ADDR2|E_NORC, + "s", + "[line [,line]] s [cgr] [count] [#lp]", + "repeat the last subsitution"}; + +/* + * ex_cmd -- + * Parse and execute a string containing ex commands. + */ +int +ex_cmd(sp, ep, cmd, cmdlen) + SCR *sp; + EXF *ep; + char *cmd; + size_t cmdlen; +{ + CHAR_T vlit; + EX_PRIVATE *exp; + EXCMDARG exc; + EXCMDLIST const *cp; + MARK cur; + recno_t lno, num; + size_t arg1_len, len, save_cmdlen; + long flagoff; + u_int saved_mode; + int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp; + char *arg1, *save_cmd, *p, *t; + + /* Init. */ + nl = 0; +loop: if (nl) { + nl = 0; + ++sp->if_lno; + } + arg1 = NULL; + save_cmdlen = 0; + + /* + * It's possible that we've been interrupted during a + * command. + */ + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + + /* Skip whitespace, separators, newlines. */ + for (; cmdlen > 0; ++cmd, --cmdlen) + if ((ch = *cmd) == '\n') + ++sp->if_lno; + else if (!isblank(ch)) + break; + if (cmdlen == 0) + return (0); + + /* Command lines that start with a double-quote are comments. */ + if (ch == '"') { + while (--cmdlen > 0 && *++cmd != '\n'); + if (*cmd == '\n') { + ++cmd; + --cmdlen; + ++sp->if_lno; + } + goto loop; + } + + /* + * !!! + * Permit extra colons at the start of the line. Historically, + * ex/vi allowed a single extra one. It's simpler not to count. + * The stripping is done here because, historically, any command + * could have preceding colons, e.g. ":g/pattern/:p" worked. + */ + if (ch == ':') + while (--cmdlen > 0 && *++cmd == ':'); + + /* Skip whitespace. */ + for (; cmdlen > 0; ++cmd, --cmdlen) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* The last point at which an empty line means do nothing. */ + if (cmdlen == 0) + return (0); + + /* Initialize the structure passed to underlying functions. */ + memset(&exc, 0, sizeof(EXCMDARG)); + exp = EXP(sp); + if (argv_init(sp, ep, &exc)) + goto err; + + /* Parse command addresses. */ + if (ep_range(sp, ep, &exc, &cmd, &cmdlen)) + goto err; + + /* Skip whitespace. */ + for (; cmdlen > 0; ++cmd, --cmdlen) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* + * If no command, ex does the last specified of p, l, or #, and vi + * moves to the line. Otherwise, determine the length of the command + * name by looking for the first non-alphabetic character. (There + * are a few non-alphabetic characters in command names, but they're + * all single character commands.) This isn't a great test, because + * it means that, for the command ":e +cut.c file", we'll report that + * the command "cut" wasn't known. However, it makes ":e+35 file" work + * correctly. + */ +#define SINGLE_CHAR_COMMANDS "!#&<=>@~" + if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') { + if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) { + p = cmd; + ++cmd; + --cmdlen; + namelen = 1; + } else { + for (p = cmd; cmdlen > 0; --cmdlen, ++cmd) + if (!isalpha(*cmd)) + break; + if ((namelen = cmd - p) == 0) { + msgq(sp, M_ERR, "Unknown command name."); + goto err; + } + } + + /* + * Search the table for the command. + * + * !!! + * Historic vi permitted the mark to immediately follow the + * 'k' in the 'k' command. Make it work. + * + * !!! + * Historic vi permitted pretty much anything to follow the + * substitute command, e.g. "s/e/E/|s|sgc3p" was fine. Make + * it work. + * + * Use of msgq below is safe, command names are all alphabetics. + */ + if ((cp = ex_comm_search(p, namelen)) == NULL) + if (p[0] == 'k' && p[1] && !p[2]) { + cmd -= namelen - 1; + cmdlen += namelen - 1; + cp = &cmds[C_K]; + } else if (p[0] == 's') { + cmd -= namelen - 1; + cmdlen += namelen - 1; + cp = &cmd_subagain; + } else { + msgq(sp, M_ERR, + "The %.*s command is unknown.", namelen, p); + goto err; + } + + /* Some commands are either not implemented or turned off. */ + if (F_ISSET(cp, E_NOPERM)) { + msgq(sp, M_ERR, + "The %s command is not currently supported.", + cp->name); + goto err; + } + + /* Some commands aren't okay in globals. */ + if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) { + msgq(sp, M_ERR, + "The %s command can't be used as part of a global command.", + cp->name); + goto err; + } + + /* + * Multiple < and > characters; another "feature". Note, + * The string passed to the underlying function may not be + * nul terminated in this case. + */ + if ((cp == &cmds[C_SHIFTL] && *p == '<') || + (cp == &cmds[C_SHIFTR] && *p == '>')) { + for (ch = *p; cmdlen > 0; --cmdlen, ++cmd) + if (*cmd != ch) + break; + if (argv_exp0(sp, ep, &exc, p, cmd - p)) + goto err; + } + + /* + * The visual command has a different syntax when called + * from ex than when called from a vi colon command. FMH. + */ + if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp)) + cp = &cmds[C_VISUAL_VI]; + + uselastcmd = 0; + } else { + cp = exp->lastcmd; + uselastcmd = 1; + } + + /* Initialize local flags to the command flags. */ + LF_INIT(cp->flags); + + /* + * File state must be checked throughout this code, because it is + * called when reading the .exrc file and similar things. There's + * this little chicken and egg problem -- if we read the file first, + * we won't know how to display it. If we read/set the exrc stuff + * first, we can't allow any command that requires file state. + * Historic vi generally took the easy way out and dropped core. + */ + if (LF_ISSET(E_NORC) && ep == NULL) { + msgq(sp, M_ERR, + "The %s command requires that a file already have been read in.", + cp->name); + goto err; + } + + /* + * There are three normal termination cases for an ex command. They + * are the end of the string (cmdlen), or unescaped (by literal next + * characters) newline or '|' characters. As we're past any addresses, + * we can now determine how long the command is, so we don't have to + * look for all the possible terminations. There are three exciting + * special cases: + * + * 1: The bang, global, vglobal and the filter versions of the read and + * write commands are delimited by newlines (they can contain shell + * pipes). + * 2: The ex, edit and visual in vi mode commands take ex commands as + * their first arguments. + * 3: The substitute command takes an RE as its first argument, and + * wants it to be specially delimited. + * + * Historically, '|' characters in the first argument of the ex, edit, + * and substitute commands did not delimit the command. And, in the + * filter cases for read and write, and the bang, global and vglobal + * commands, they did not delimit the command at all. + * + * For example, the following commands were legal: + * + * :edit +25|s/abc/ABC/ file.c + * :substitute s/|/PIPE/ + * :read !spell % | columnate + * :global/pattern/p|l + * + * It's not quite as simple as it sounds, however. The command: + * + * :substitute s/a/b/|s/c/d|set + * + * was also legal, i.e. the historic ex parser (using the word loosely, + * since "parser" implies some regularity) delimited the RE's based on + * its delimiter and not anything so irretrievably vulgar as a command + * syntax. + * + * One thing that makes this easier is that we can ignore most of the + * command termination conditions for the commands that want to take + * the command up to the next newline. None of them are legal in .exrc + * files, so if we're here, we only dealing with a single line, and we + * can just eat it. + * + * Anyhow, the following code makes this all work. First, for the + * special cases we move past their special argument. Then, we do + * normal command processing on whatever is left. Barf-O-Rama. + */ + arg1_len = 0; + save_cmd = cmd; + (void)term_key_ch(sp, K_VLNEXT, &vlit); + if (cp == &cmds[C_EDIT] || + cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) { + /* + * Move to the next non-whitespace character. As '+' must + * be the character after the command name, if there isn't + * one, we're done. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + /* + * QUOTING NOTE: + * + * The historic implementation ignored all escape characters + * so there was no way to put a space or newline into the +cmd + * field. We do a simplistic job of fixing it by moving to the + * first whitespace character that isn't escaped by a literal + * next character. The literal next characters are stripped + * as they're no longer useful. + */ + if (cmdlen > 0 && ch == '+') { + ++cmd; + --cmdlen; + for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + ch = *++cmd; + } else if (isblank(ch)) + break; + *p++ = ch; + } + arg1_len = cmd - arg1; + + /* Reset, so the first argument isn't reparsed. */ + save_cmd = cmd; + } + } else if (cp == &cmds[C_BANG] || + cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) { + cmd += cmdlen; + cmdlen = 0; + } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) { + /* + * Move to the next character. If it's a '!', it's a filter + * command and we want to eat it all, otherwise, we're done. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen > 0 && ch == '!') { + cmd += cmdlen; + cmdlen = 0; + } + } else if (cp == &cmds[C_SUBSTITUTE]) { + /* + * Move to the next non-whitespace character, we'll use it as + * the delimiter. If the character isn't an alphanumeric or + * a '|', it's the delimiter, so parse it. Otherwise, we're + * into something like ":s g", so use the special substitute + * command. + */ + for (; cmdlen > 0; --cmdlen, ++cmd) + if (!isblank(cmd[0])) + break; + + if (isalnum(cmd[0]) || cmd[0] == '|') + cp = &cmd_subagain; + else if (cmdlen > 0) { + /* + * QUOTING NOTE: + * + * Backslashes quote delimiter characters for RE's. + * The backslashes are NOT removed since they'll be + * used by the RE code. Move to the third delimiter + * that's not escaped (or the end of the command). + */ + delim = *cmd; + ++cmd; + --cmdlen; + for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd) + if (cmd[0] == '\\' && cmdlen > 1) { + ++cmd; + --cmdlen; + } else if (cmd[0] == delim) + --cnt; + } + } + /* + * Use normal quoting and termination rules to find the end + * of this command. + * + * QUOTING NOTE: + * + * Historically, vi permitted ^V's to escape <newline>'s in the .exrc + * file. It was almost certainly a bug, but that's what bug-for-bug + * compatibility means, Grasshopper. Also, ^V's escape the command + * delimiters. Literal next quote characters in front of the newlines, + * '|' characters or literal next characters are stripped as as they're + * no longer useful. + */ + for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = cmd[0]) == vlit && cmdlen > 1) { + ch = cmd[1]; + if (ch == '\n' || ch == '|') { + if (ch == '\n') + ++sp->if_lno; + --cmdlen; + ++cmd; + ++cnt; + } else + ch = vlit; + } else if (ch == '\n' || ch == '|') { + if (ch == '\n') + nl = 1; + --cmdlen; + break; + } + *p++ = ch; + } + + /* + * Save off the next command information, go back to the + * original start of the command. + */ + p = cmd + 1; + cmd = save_cmd; + save_cmd = p; + save_cmdlen = cmdlen; + cmdlen = ((save_cmd - cmd) - 1) - cnt; + + /* + * !!! + * The "set tags" command historically used a backslash, not the + * user's literal next character, to escape whitespace. Handle + * it here instead of complicating the argv_exp3() code. Note, + * this isn't a particularly complex trap, and if backslashes were + * legal in set commands, this would have to be much more complicated. + */ + if (cp == &cmds[C_SET]) + for (p = cmd, len = cmdlen; len > 0; --len, ++p) + if (*p == '\\') + *p = vlit; + + /* + * Set the default addresses. It's an error to specify an address for + * a command that doesn't take them. If two addresses are specified + * for a command that only takes one, lose the first one. Two special + * cases here, some commands take 0 or 2 addresses. For most of them + * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one + * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines. + * + * Also, if the file is empty, some commands want to use an address of + * 0, i.e. the entire file is 0 to 0, and the default first address is + * 0. Otherwise, an entire file is 1 to N and the default line is 1. + * Note, we also add the E_ZERO flag to the command flags, for the case + * where the 0 address is only valid if it's a default address. + * + * Also, set a flag if we set the default addresses. Some commands + * (ex: z) care if the user specified an address of if we just used + * the current cursor. + */ + switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) { + case E_ADDR1: /* One address: */ + switch (exc.addrcnt) { + case 0: /* Default cursor/empty file. */ + exc.addrcnt = 1; + F_SET(&exc, E_ADDRDEF); + if (LF_ISSET(E_ZERODEF)) { + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + exc.addr1.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = sp->lno; + } else + exc.addr1.lno = sp->lno; + exc.addr1.cno = sp->cno; + break; + case 1: + break; + case 2: /* Lose the first address. */ + exc.addrcnt = 1; + exc.addr1 = exc.addr2; + } + break; + case E_ADDR2_NONE: /* Zero/two addresses: */ + if (exc.addrcnt == 0) /* Default to nothing. */ + break; + goto two; + case E_ADDR2_ALL: /* Zero/two addresses: */ + if (exc.addrcnt == 0) { /* Default entire/empty file. */ + exc.addrcnt = 2; + F_SET(&exc, E_ADDRDEF); + if (file_lline(sp, ep, &exc.addr2.lno)) + goto err; + if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) { + exc.addr1.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = 1; + exc.addr1.cno = exc.addr2.cno = 0; + F_SET(&exc, E_ADDR2_ALL); + break; + } + /* FALLTHROUGH */ + case E_ADDR2: /* Two addresses: */ +two: switch (exc.addrcnt) { + case 0: /* Default cursor/empty file. */ + exc.addrcnt = 2; + F_SET(&exc, E_ADDRDEF); + if (LF_ISSET(E_ZERODEF) && sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + exc.addr1.lno = exc.addr2.lno = 0; + LF_SET(E_ZERO); + } else + exc.addr1.lno = exc.addr2.lno = sp->lno; + } else + exc.addr1.lno = exc.addr2.lno = sp->lno; + exc.addr1.cno = exc.addr2.cno = sp->cno; + break; + case 1: /* Default to first address. */ + exc.addrcnt = 2; + exc.addr2 = exc.addr1; + break; + case 2: + break; + } + break; + default: + if (exc.addrcnt) /* Error. */ + goto usage; + } + + flagoff = 0; + for (p = cp->syntax; *p != '\0'; ++p) { + /* + * The write command is sensitive to leading whitespace, e.g. + * "write !" is different from "write!". If not the write + * command, skip leading whitespace. + */ + if (cp != &cmds[C_WRITE]) + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + + /* + * Quit when reach the end of the command, unless it's a + * command that does its own parsing, in which case we want + * to build a reasonable argv for it. This code guarantees + * that there will be an argv when the function gets called, + * so the correct test is for a length of 0, not for the + * argc > 0. + */ + if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's') + break; + + switch (*p) { + case '!': /* ! */ + if (*cmd == '!') { + ++cmd; + --cmdlen; + F_SET(&exc, E_FORCE); + } + break; + case '1': /* +, -, #, l, p */ + /* + * !!! + * Historically, some flags were ignored depending + * on where they occurred in the command line. For + * example, in the command, ":3+++p--#", historic vi + * acted on the '#' flag, but ignored the '-' flags. + * It's unambiguous what the flags mean, so we just + * handle them regardless of the stupidity of their + * location. + */ + for (; cmdlen; --cmdlen, ++cmd) + switch (*cmd) { + case '+': + ++flagoff; + break; + case '-': + --flagoff; + break; + case '#': + F_SET(&exc, E_F_HASH); + break; + case 'l': + F_SET(&exc, E_F_LIST); + break; + case 'p': + F_SET(&exc, E_F_PRINT); + break; + default: + goto end1; + } +end1: break; + case '2': /* -, ., +, ^ */ + case '3': /* -, ., +, ^, = */ + for (; cmdlen; --cmdlen, ++cmd) + switch (*cmd) { + case '-': + F_SET(&exc, E_F_DASH); + break; + case '.': + F_SET(&exc, E_F_DOT); + break; + case '+': + F_SET(&exc, E_F_PLUS); + break; + case '^': + F_SET(&exc, E_F_CARAT); + break; + case '=': + if (*p == '3') { + F_SET(&exc, E_F_EQUAL); + break; + } + /* FALLTHROUGH */ + default: + goto end2; + } +end2: break; + case 'b': /* buffer */ + /* + * Digits can't be buffer names in ex commands, or the + * command "d2" would be a delete into buffer '2', and + * not a two-line deletion. + */ + if (!isdigit(cmd[0])) { + exc.buffer = *cmd; + ++cmd; + --cmdlen; + F_SET(&exc, E_BUFFER); + } + break; + case 'c': /* count [01+a] */ + ++p; + if (!isdigit(*cmd) && + (*p != '+' || (*cmd != '+' && *cmd != '-'))) + break; +/* 8-bit XXX */ if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') { + msgq(sp, M_ERR, "Count may not be zero."); + goto err; + } + cmdlen -= (t - cmd); + cmd = t; + /* + * Count as address offsets occur in commands taking + * two addresses. Historic vi practice was to use + * the count as an offset from the *second* address. + * + * Set a count flag; some underlying commands (see + * join) do different things with counts than with + * line addresses. + */ + if (*p == 'a') { + exc.addr1 = exc.addr2; + exc.addr2.lno = exc.addr1.lno + lno - 1; + } else + exc.count = lno; + F_SET(&exc, E_COUNT); + break; + case 'f': /* file */ + if (argv_exp2(sp, ep, + &exc, cmd, cmdlen, cp == &cmds[C_BANG])) + goto err; + goto countchk; + case 'l': /* line */ + if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp)) + goto err; + /* Line specifications are always required. */ + if (!tmp) { + msgq(sp, M_ERR, + "%s: bad line specification", cmd); + goto err; + } + exc.lineno = cur.lno; + break; + case 'S': /* string, file exp. */ + if (argv_exp1(sp, ep, + &exc, cmd, cmdlen, cp == &cmds[C_BANG])) + goto err; + goto addr2; + case 's': /* string */ + if (argv_exp0(sp, ep, &exc, cmd, cmdlen)) + goto err; + goto addr2; + case 'W': /* word string */ + /* + * QUOTING NOTE: + * + * Literal next characters escape the following + * character. Quoting characters are stripped + * here since they are no longer useful. + * + * First there was the word. + */ + for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) { + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + *p++ = *++cmd; + } else if (isblank(ch)) { + ++cmd; + --cmdlen; + break; + } else + *p++ = ch; + } + if (argv_exp0(sp, ep, &exc, t, p - t)) + goto err; + + /* Delete intervening whitespace. */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen == 0) + goto usage; + + /* Followed by the string. */ + for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p) + if ((ch = *cmd) == vlit && cmdlen > 1) { + --cmdlen; + *p = *++cmd; + } else + *p = ch; + if (argv_exp0(sp, ep, &exc, t, p - t)) + goto err; + goto addr2; + case 'w': /* word */ + if (argv_exp3(sp, ep, &exc, cmd, cmdlen)) + goto err; +countchk: if (*++p != 'N') { /* N */ + /* + * If a number is specified, must either be + * 0 or that number, if optional, and that + * number, if required. + */ + num = *p - '0'; + if ((*++p != 'o' || exp->argsoff != 0) && + exp->argsoff != num) + goto usage; + } + goto addr2; + default: + msgq(sp, M_ERR, + "Internal syntax table error (%s: %c).", + cp->name, *p); + } + } + + /* Skip trailing whitespace. */ + for (; cmdlen; --cmdlen) { + ch = *cmd++; + if (!isblank(ch)) + break; + } + + /* + * There shouldn't be anything left, and no more required + * fields, i.e neither 'l' or 'r' in the syntax string. + */ + if (cmdlen || strpbrk(p, "lr")) { +usage: msgq(sp, M_ERR, "Usage: %s.", cp->usage); + goto err; + } + + /* Verify that the addresses are legal. */ +addr2: switch (exc.addrcnt) { + case 2: + if (file_lline(sp, ep, &lno)) + goto err; + /* + * Historic ex/vi permitted commands with counts to go past + * EOF. So, for example, if the file only had 5 lines, the + * ex command "1,6>" would fail, but the command ">300" + * would succeed. Since we don't want to have to make all + * of the underlying commands handle random line numbers, + * fix it here. + */ + if (exc.addr2.lno > lno) + if (F_ISSET(&exc, E_COUNT)) + exc.addr2.lno = lno; + else { + if (lno == 0) + msgq(sp, M_ERR, "The file is empty."); + else + msgq(sp, M_ERR, + "Only %lu line%s in the file", + lno, lno > 1 ? "s" : ""); + goto err; + } + /* FALLTHROUGH */ + case 1: + num = exc.addr1.lno; + /* + * If it's a "default vi command", zero is okay. Historic + * vi allowed this, note, it's also the hack that allows + * "vi + nonexistent_file" to work. + */ + if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) && + !LF_ISSET(E_ZERO)) { + msgq(sp, M_ERR, + "The %s command doesn't permit an address of 0.", + cp->name); + goto err; + } + if (file_lline(sp, ep, &lno)) + goto err; + if (num > lno) { + if (lno == 0) + msgq(sp, M_ERR, "The file is empty."); + else + msgq(sp, M_ERR, "Only %lu line%s in the file", + lno, lno > 1 ? "s" : ""); + goto err; + } + break; + } + + /* If doing a default command, vi just moves to the line. */ + if (IN_VI_MODE(sp) && uselastcmd) { + switch (exc.addrcnt) { + case 2: + sp->lno = exc.addr2.lno ? exc.addr2.lno : 1; + sp->cno = exc.addr2.cno; + break; + case 1: + sp->lno = exc.addr1.lno ? exc.addr1.lno : 1; + sp->cno = exc.addr1.cno; + break; + } + cmd = save_cmd; + cmdlen = save_cmdlen; + goto loop; + } + + /* Reset "last" command. */ + if (LF_ISSET(E_SETLAST)) + exp->lastcmd = cp; + + /* Final setup for the command. */ + exc.cmd = cp; + +#if defined(DEBUG) && 0 + TRACE(sp, "ex_cmd: %s", exc.cmd->name); + if (exc.addrcnt > 0) { + TRACE(sp, "\taddr1 %d", exc.addr1.lno); + if (exc.addrcnt > 1) + TRACE(sp, " addr2: %d", exc.addr2.lno); + TRACE(sp, "\n"); + } + if (exc.lineno) + TRACE(sp, "\tlineno %d", exc.lineno); + if (exc.flags) + TRACE(sp, "\tflags %0x", exc.flags); + if (F_ISSET(&exc, E_BUFFER)) + TRACE(sp, "\tbuffer %c", exc.buffer); + TRACE(sp, "\n"); + if (exc.argc) { + for (cnt = 0; cnt < exc.argc; ++cnt) + TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]); + TRACE(sp, "\n"); + } +#endif + /* Clear autoprint flag. */ + F_CLR(exp, EX_AUTOPRINT); + + /* Increment the command count if not called from vi. */ + if (!IN_VI_MODE(sp)) + ++sp->ccnt; + + /* + * If file state and not doing a global command, log the start of + * an action. + */ + if (ep != NULL && !F_ISSET(sp, S_GLOBAL)) + (void)log_cursor(sp, ep); + + /* Save the current mode. */ + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + + /* Do the command. */ + if ((cp->fn)(sp, ep, &exc)) + goto err; + +#ifdef DEBUG + /* Make sure no function left the temporary space locked. */ + if (F_ISSET(sp->gp, G_TMP_INUSE)) { + F_CLR(sp->gp, G_TMP_INUSE); + msgq(sp, M_ERR, "Error: ex: temporary buffer not released."); + goto err; + } +#endif + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) { + /* + * Only here if the mode of the underlying file changed, e.g. + * the user switched files or is exiting. There are two things + * that we might have to save. First, any "+cmd" field set up + * for an ex/edit command will have to be saved for later, also, + * any not yet executed part of the current ex command. + * + * :edit +25 file.c|s/abc/ABC/|1 + * + * for example. + * + * The historic vi just hung, of course; we handle by + * pushing the keys onto the tty queue. If we're + * switching modes to vi, since the commands are intended + * as ex commands, add the extra characters to make it + * work. + * + * For the fun of it, if you want to see if a vi clone got + * the ex argument parsing right, try: + * + * echo 'foo|bar' > file1; echo 'foo/bar' > file2; + * vi + * :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq + */ + if (arg1_len == NULL && save_cmdlen == 0) + return (0); + if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0)) + goto err; + if (save_cmdlen != 0) + if (term_push(sp, save_cmd, save_cmdlen, 0, 0)) + goto err; + if (arg1 != NULL) { + if (IN_VI_MODE(sp) && save_cmdlen != 0 && + term_push(sp, "|", 1, 0, 0)) + goto err; + if (term_push(sp, arg1, arg1_len, 0, 0)) + goto err; + } + if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0)) + goto err; + return (0); + } + + if (IN_EX_MODE(sp) && ep != NULL) { + /* + * The print commands have already handled the `print' flags. + * If so, clear them. Don't return, autoprint may still have + * stuff to print out. + */ + if (LF_ISSET(E_F_PRCLEAR)) + F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT); + + /* + * If the command was successful, and there was an explicit + * flag to display the new cursor line, or we're in ex mode, + * autoprint is set, and a change was made, display the line. + */ + if (flagoff) { + if (flagoff < 0) { + if (sp->lno < -flagoff) { + msgq(sp, M_ERR, + "Flag offset before line 1."); + goto err; + } + } else { + if (file_lline(sp, ep, &lno)) + goto err; + if (sp->lno + flagoff > lno) { + msgq(sp, M_ERR, + "Flag offset past end-of-file."); + goto err; + } + } + sp->lno += flagoff; + } + + if (O_ISSET(sp, O_AUTOPRINT) && + (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT))) + LF_INIT(E_F_PRINT); + else + LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT)); + + memset(&exc, 0, sizeof(EXCMDARG)); + exc.addrcnt = 2; + exc.addr1.lno = exc.addr2.lno = sp->lno; + exc.addr1.cno = exc.addr2.cno = sp->cno; + switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) { + case E_F_HASH: + exc.cmd = &cmds[C_HASH]; + ex_number(sp, ep, &exc); + break; + case E_F_LIST: + exc.cmd = &cmds[C_LIST]; + ex_list(sp, ep, &exc); + break; + case E_F_PRINT: + exc.cmd = &cmds[C_PRINT]; + ex_pr(sp, ep, &exc); + break; + } + } + + cmd = save_cmd; + cmdlen = save_cmdlen; + goto loop; + /* NOTREACHED */ + + /* + * On error, we discard any keys we have left, as well as any keys + * that were mapped. The test of save_cmdlen isn't necessarily + * correct. If we fail early enough we don't know if the entire + * string was a single command or not. Try and guess, it's useful + * to know if part of the command was discarded. + */ +err: if (save_cmdlen == 0) + for (; cmdlen; --cmdlen) + if ((ch = *cmd++) == vlit && cmdlen > 1) { + --cmdlen; + ++cmd; + } else if (ch == '\n' || ch == '|') { + if (cmdlen > 1) + save_cmdlen = 1; + break; + } + if (save_cmdlen != 0) + msgq(sp, M_ERR, + "Ex command failed: remaining command input discarded."); + term_map_flush(sp, "Ex command failed"); + return (1); +} + +/* + * ep_range -- + * Get a line range for ex commands. + */ +static int +ep_range(sp, ep, excp, cmdp, cmdlenp) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char **cmdp; + size_t *cmdlenp; +{ + MARK cur, savecursor; + size_t cmdlen; + int savecursor_set, tmp; + char *cmd; + + /* Percent character is all lines in the file. */ + cmd = *cmdp; + cmdlen = *cmdlenp; + if (*cmd == '%') { + excp->addr1.lno = 1; + if (file_lline(sp, ep, &excp->addr2.lno)) + return (1); + + /* If an empty file, then the first line is 0, not 1. */ + if (excp->addr2.lno == 0) + excp->addr1.lno = 0; + excp->addr1.cno = excp->addr2.cno = 0; + excp->addrcnt = 2; + + ++*cmdp; + --*cmdlenp; + return (0); + } + + /* Parse comma or semi-colon delimited line specs. */ + for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;) + switch (*cmd) { + case ';': /* Semi-colon delimiter. */ + /* + * Comma delimiters delimit; semi-colon delimiters + * change the current address for the 2nd address + * to be the first address. Trailing or multiple + * delimiters are discarded. + */ + if (excp->addrcnt == 0) + goto done; + if (!savecursor_set) { + savecursor.lno = sp->lno; + savecursor.cno = sp->cno; + sp->lno = excp->addr1.lno; + sp->cno = excp->addr1.cno; + savecursor_set = 1; + } + ++cmd; + --cmdlen; + break; + case ',': /* Comma delimiter. */ + /* If no addresses yet, defaults to ".". */ + if (excp->addrcnt == 0) { + excp->addr1.lno = sp->lno; + excp->addr1.cno = sp->cno; + excp->addrcnt = 1; + } + /* FALLTHROUGH */ + case ' ': /* Whitespace. */ + case '\t': /* Whitespace. */ + ++cmd; + --cmdlen; + break; + default: + if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp)) + return (1); + if (!tmp) + goto done; + + /* + * Extra addresses are discarded, starting with + * the first. + */ + switch (excp->addrcnt) { + case 0: + excp->addr1 = cur; + excp->addrcnt = 1; + break; + case 1: + excp->addr2 = cur; + excp->addrcnt = 2; + break; + case 2: + excp->addr1 = excp->addr2; + excp->addr2 = cur; + break; + } + break; + } + + /* + * XXX + * This is probably not right behavior for savecursor -- need + * to figure out what the historical ex did for ";,;,;5p" or + * similar stupidity. + */ +done: if (savecursor_set) { + sp->lno = savecursor.lno; + sp->cno = savecursor.cno; + } + if (excp->addrcnt == 2 && + (excp->addr2.lno < excp->addr1.lno || + excp->addr2.lno == excp->addr1.lno && + excp->addr2.cno < excp->addr1.cno)) { + msgq(sp, M_ERR, + "The second address is smaller than the first."); + return (1); + } + *cmdp = cmd; + *cmdlenp = cmdlen; + return (0); +} + +/* + * Get a single line address specifier. + */ +static int +ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found) + SCR *sp; + EXF *ep; + MARK *cur; + char **cmdp; + size_t *cmdlenp; + int *addr_found; +{ + MARK m, *mp; + long total; + u_int flags; + size_t cmdlen; + char *cmd, *endp; + + *addr_found = 0; + + cmd = *cmdp; + cmdlen = *cmdlenp; + switch (*cmd) { + case '$': /* Last line in the file. */ + *addr_found = 1; + cur->cno = 0; + if (file_lline(sp, ep, &cur->lno)) + return (1); + ++cmd; + --cmdlen; + break; /* Absolute line number. */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *addr_found = 1; + /* + * The way the vi "previous context" mark worked was that + * "non-relative" motions set it. While vi wasn't totally + * consistent about this, ANY numeric address was considered + * non-relative, and set the value. Which is why we're + * hacking marks down here. + */ + if (IN_VI_MODE(sp)) { + m.lno = sp->lno; + m.cno = sp->cno; + if (mark_set(sp, ep, ABSMARK1, &m, 1)) + return (1); + } + cur->cno = 0; +/* 8-bit XXX */ cur->lno = strtol(cmd, &endp, 10); + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '\'': /* Use a mark. */ + *addr_found = 1; + if (cmdlen == 1) { + msgq(sp, M_ERR, "No mark name supplied."); + return (1); + } + if ((mp = mark_get(sp, ep, cmd[1])) == NULL) + return (1); + *cur = *mp; + cmd += 2; + cmdlen -= 2; + break; + case '\\': /* Search: forward/backward. */ + /* + * !!! + * I can't find any difference between // and \/ or + * between ?? and \?. Mark Horton doesn't remember + * there being any difference. C'est la vie. + */ + if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') { + msgq(sp, M_ERR, "\\ not followed by / or ?."); + return (1); + } + ++cmd; + --cmdlen; + if (cmd[0] == '/') + goto forward; + if (cmd[0] == '?') + goto backward; + /* NOTREACHED */ + case '/': /* Search forward. */ +forward: *addr_found = 1; + m.lno = sp->lno; + m.cno = sp->cno; + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET; + if (f_search(sp, ep, &m, &m, cmd, &endp, &flags)) + return (1); + cur->lno = m.lno; + cur->cno = m.cno; + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '?': /* Search backward. */ +backward: *addr_found = 1; + m.lno = sp->lno; + m.cno = sp->cno; + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET; + if (b_search(sp, ep, &m, &m, cmd, &endp, &flags)) + return (1); + cur->lno = m.lno; + cur->cno = m.cno; + cmdlen -= (endp - cmd); + cmd = endp; + break; + case '.': /* Current position. */ + *addr_found = 1; + cur->cno = sp->cno; + + /* If an empty file, then '.' is 0, not 1. */ + if (sp->lno == 1) { + if (file_lline(sp, ep, &cur->lno)) + return (1); + if (cur->lno != 0) + cur->lno = 1; + } else + cur->lno = sp->lno; + ++cmd; + --cmdlen; + break; + } + + /* + * Evaluate any offset. Offsets are +/- any number, or any number + * of +/- signs, or any combination thereof. If no address found + * yet, offset is relative to ".". + */ + for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) { + if (!*addr_found) { + cur->lno = sp->lno; + cur->cno = sp->cno; + *addr_found = 1; + } + + if (cmdlen > 1 && isdigit(cmd[1])) { +/* 8-bit XXX */ total += strtol(cmd, &endp, 10); + cmdlen -= (endp - cmd); + cmd = endp; + } else { + total += cmd[0] == '-' ? -1 : 1; + --cmdlen; + ++cmd; + } + } + if (total < 0 && -total > cur->lno) { + msgq(sp, M_ERR, "Reference to a line number less than 0."); + return (1); + } + cur->lno += total; + + *cmdp = cmd; + *cmdlenp = cmdlen; + return (0); +} + +/* + * ex_is_abbrev - + * The vi text input routine needs to know if ex thinks this is + * an [un]abbreviate command, so it can turn off abbreviations. + * Usual ranting in the vi/v_ntext:txt_abbrev() routine. + */ +int +ex_is_abbrev(name, len) + char *name; + size_t len; +{ + EXCMDLIST const *cp; + + return ((cp = ex_comm_search(name, len)) != NULL && + (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE])); +} + +static inline EXCMDLIST const * +ex_comm_search(name, len) + char *name; + size_t len; +{ + EXCMDLIST const *cp; + + for (cp = cmds; cp->name != NULL; ++cp) { + if (cp->name[0] > name[0]) + return (NULL); + if (cp->name[0] != name[0]) + continue; + if (!memcmp(name, cp->name, len)) + return (cp); + } + return (NULL); +} diff --git a/usr.bin/vi/nex/ex_abbrev.c b/usr.bin/vi/nex/ex_abbrev.c new file mode 100644 index 000000000000..f28cbe45e490 --- /dev/null +++ b/usr.bin/vi/nex/ex_abbrev.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_abbrev.c 8.6 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * ex_abbr -- :abbreviate [key replacement] + * Create an abbreviation or display abbreviations. + */ +int +ex_abbr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch (cmdp->argc) { + case 0: + if (seq_dump(sp, SEQ_ABBREV, 0) == 0) + msgq(sp, M_INFO, "No abbreviations to display."); + return (0); + case 2: + break; + default: + abort(); + } + + if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len, + cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, 1)) + return (1); + F_SET(sp->gp, G_ABBREV); + return (0); +} + +/* + * ex_unabbr -- :unabbreviate key + * Delete an abbreviation. + */ +int +ex_unabbr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + + ap = cmdp->argv[0]; + if (!F_ISSET(sp->gp, G_ABBREV) || + seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) { + msgq(sp, M_ERR, "\"%s\" is not an abbreviation.", ap->bp); + return (1); + } + return (0); +} + +/* + * abbr_save -- + * Save the abbreviation sequences to a file. + */ +int +abbr_save(sp, fp) + SCR *sp; + FILE *fp; +{ + return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV)); +} diff --git a/usr.bin/vi/nex/ex_append.c b/usr.bin/vi/nex/ex_append.c new file mode 100644 index 000000000000..c6edfd57db3b --- /dev/null +++ b/usr.bin/vi/nex/ex_append.c @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_append.c 8.6 (Berkeley) 11/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +enum which {APPEND, CHANGE, INSERT}; + +static int aci __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_append -- :[line] a[ppend][!] + * Append one or more lines of new text after the specified line, + * or the current line if no address is specified. + */ +int +ex_append(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, APPEND)); +} + +/* + * ex_change -- :[line[,line]] c[hange][!] [count] + * Change one or more lines to the input text. + */ +int +ex_change(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, CHANGE)); +} + +/* + * ex_insert -- :[line] i[nsert][!] + * Insert one or more lines of new text before the specified line, + * or the current line if no address is specified. + */ +int +ex_insert(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (aci(sp, ep, cmdp, INSERT)); +} + +static int +aci(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + MARK m; + TEXT *tp; + recno_t cnt; + int rval, aiset; + + /* + * The ! flag turns off autoindent for append, change and + * insert. + */ + if (F_ISSET(cmdp, E_FORCE)) { + aiset = O_ISSET(sp, O_AUTOINDENT); + O_CLR(sp, O_AUTOINDENT); + } else + aiset = 0; + + /* + * If doing a change, replace lines as long as possible. + * Then, append more lines or delete remaining lines. + * Inserts are the same as appends to the previous line. + */ + rval = 0; + m = cmdp->addr1; + if (cmd == INSERT) { + --m.lno; + cmd = APPEND; + } + if (cmd == CHANGE) + for (;; ++m.lno) { + if (m.lno > cmdp->addr2.lno) { + cmd = APPEND; + --m.lno; + break; + } + switch (sp->s_get(sp, ep, &sp->tiq, 0, + TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) { + case INP_OK: + break; + case INP_EOF: + case INP_ERR: + rval = 1; + goto done; + } + tp = sp->tiq.cqh_first; + if (tp->len == 1 && tp->lb[0] == '.') { + for (cnt = + (cmdp->addr2.lno - m.lno) + 1; cnt--;) + if (file_dline(sp, ep, m.lno)) { + rval = 1; + goto done; + } + goto done; + } + if (file_sline(sp, ep, m.lno, tp->lb, tp->len)) { + rval = 1; + goto done; + } + } + + if (cmd == APPEND) + for (;; ++m.lno) { + switch (sp->s_get(sp, ep, &sp->tiq, 0, + TXT_BEAUTIFY | TXT_CR | TXT_NLECHO)) { + case INP_OK: + break; + case INP_EOF: + case INP_ERR: + rval = 1; + goto done; + } + tp = sp->tiq.cqh_first; + if (tp->len == 1 && tp->lb[0] == '.') + break; + if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) { + rval = 1; + goto done; + } + } + +done: if (rval == 0) + sp->lno = m.lno; + + if (aiset) + O_SET(sp, O_AUTOINDENT); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_args.c b/usr.bin/vi/nex/ex_args.c new file mode 100644 index 000000000000..2f96df727dd8 --- /dev/null +++ b/usr.bin/vi/nex/ex_args.c @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_args.c 8.13 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_next -- :next [files] + * Edit the next file, optionally setting the list of files. + * + * !!! + * The :next command behaved differently from the :rewind command in + * historic vi. See nvi/docs/autowrite for details, but the basic + * idea was that it ignored the force flag if the autowrite flag was + * set. This implementation handles them all identically. + */ +int +ex_next(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS **argv; + FREF *frp; + char *name; + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if (cmdp->argc) { + /* Mark all the current files as ignored. */ + for (frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) + F_SET(frp, FR_IGNORE); + + /* Add the new files into the file list. */ + for (argv = cmdp->argv; argv[0]->len != 0; ++argv) + if (file_add(sp, NULL, argv[0]->bp, 0) == NULL) + return (1); + + if ((frp = file_first(sp)) == NULL) + return (1); + } else if ((frp = file_next(sp, sp->a_frp)) == NULL) { + msgq(sp, M_ERR, "No more files to edit."); + return (1); + } + + /* + * There's a tricky sequence, where the user edits two files, e.g. + * "x" and "y". While in "x", they do ":e y|:f foo", which changes + * the name of that FRP entry. Then, the :n command finds the file + * "y" with a name change. If the file name has been changed, get + * a new FREF for the original file name, and make it be the one that + * is displayed in the argument list, not the one with the name change. + */ + if (frp->cname != NULL) { + F_SET(frp, FR_IGNORE); + name = frp->name == NULL ? frp->tname : frp->name; + if ((frp = file_add(sp, sp->a_frp, name, 0)) == NULL) + return (1); + } + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_prev -- :prev + * Edit the previous file. + */ +int +ex_prev(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + char *name; + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if ((frp = file_prev(sp, sp->a_frp)) == NULL) { + msgq(sp, M_ERR, "No previous files to edit."); + return (1); + } + + /* See comment in ex_next(). */ + if (frp->cname != NULL) { + F_SET(frp, FR_IGNORE); + name = frp->name == NULL ? frp->tname : frp->name; + if ((frp = file_add(sp, frp, name, 0)) == NULL) + return (1); + } + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_rew -- :rew + * Re-edit the list of files. + */ +int +ex_rew(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp, *tfrp; + + /* + * !!! + * Historic practice -- you can rewind to the current file. + */ + if ((frp = file_first(sp)) == NULL) { + msgq(sp, M_ERR, "No previous files to rewind."); + return (1); + } + + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + /* + * !!! + * Historic practice, turn off the edited bit. The :next and :prev + * code will discard any name changes, so ignore them here. Start + * at the beginning of the file, too. + */ + for (tfrp = sp->frefq.cqh_first; + tfrp != (FREF *)&sp->frefq; tfrp = tfrp->q.cqe_next) + F_CLR(tfrp, FR_CHANGEWRITE | FR_CURSORSET | FR_EDITED); + + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + sp->a_frp = frp; + F_SET(sp, S_FSWITCH); + return (0); +} + +/* + * ex_args -- :args + * Display the list of files. + */ +int +ex_args(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + int cnt, col, iscur, len, nlen, sep; + char *name; + + /* + * !!! + * Ignore files that aren't in the "argument" list unless they are the + * one we're currently editing. I'm not sure this is right, but the + * historic vi behavior of not showing the current file if it was the + * result of a ":e" command, or if the file name was changed was wrong. + * This is actually pretty tricky, don't modify it without thinking it + * through. There have been a lot of problems in here. + * + * Also, historic practice was to display the original name of the file + * even if the user had used a file command to change the file name. + * Confusing, at best. We show both names: the original as that's what + * the user will get in a next, prev or rewind, and the new one since + * that's what the user is actually editing now. + * + * When we find the "argument" FREF, i.e. the current location in the + * user's argument list, if it's not the same as the current FREF, we + * display the current FREF as following the argument in the list. + * This means that if the user edits three files, "x", "y" and "z", and + * then does a :e command in the file "x" to edit "z", "z" will appear + * in the list twice. + */ + col = len = sep = 0; + for (cnt = 1, frp = sp->frefq.cqh_first; + frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) { + iscur = 0; + /* + * If the last argument FREF structure, and we're editing + * it, set the current bit. Otherwise, we'll display it, + * then the file we're editing, and the latter will have + * the current bit set. + */ + if (frp == sp->a_frp) { + if (frp == sp->frp && frp->cname == NULL) + iscur = 1; + } else if (F_ISSET(frp, FR_IGNORE)) + continue; + name = frp->name == NULL ? frp->tname : frp->name; + /* + * Mistake. The user edited a temporary file (vi /tmp), then + * switched to another file (:e file). The argument FREF is + * pointing to the temporary file, but it doesn't have a name. + * Gracefully recover through the creative use of goto's. + */ + if (name == NULL) + goto testcur; +extra: nlen = strlen(name); + col += len = nlen + sep + (iscur ? 2 : 0); + if (col >= sp->cols - 1) { + col = len; + sep = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else if (cnt != 1) { + sep = 1; + (void)ex_printf(EXCOOKIE, " "); + } + ++cnt; + + if (iscur) + (void)ex_printf(EXCOOKIE, "[%s]", name); + else { + (void)ex_printf(EXCOOKIE, "%s", name); +testcur: if (frp == sp->a_frp) { + if (frp != sp->frp) + name = FILENAME(sp->frp); + else + name = frp->cname; + iscur = 1; + goto extra; + } + } + } + /* This should never happen; left in because it's been known to. */ + if (cnt == 1) + (void)ex_printf(EXCOOKIE, "No files.\n"); + else + (void)ex_printf(EXCOOKIE, "\n"); + return (0); +} diff --git a/usr.bin/vi/nex/ex_argv.c b/usr.bin/vi/nex/ex_argv.c new file mode 100644 index 000000000000..6ad76c61dfbc --- /dev/null +++ b/usr.bin/vi/nex/ex_argv.c @@ -0,0 +1,575 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_argv.c 8.26 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +static int argv_alloc __P((SCR *, size_t)); +static int argv_fexp __P((SCR *, EXCMDARG *, + char *, size_t, char *, size_t *, char **, size_t *, int)); +static int argv_sexp __P((SCR *, char **, size_t *, size_t *)); + +/* + * argv_init -- + * Build a prototype arguments list. + */ +int +argv_init(sp, ep, excp) + SCR *sp; + EXF *ep; + EXCMDARG *excp; +{ + EX_PRIVATE *exp; + + exp = EXP(sp); + exp->argsoff = 0; + argv_alloc(sp, 1); + + excp->argv = exp->args; + excp->argc = exp->argsoff; + return (0); +} + +/* + * argv_exp0 -- + * Put a string into an argv. + */ +int +argv_exp0(sp, ep, excp, cmd, cmdlen) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; +{ + EX_PRIVATE *exp; + + exp = EXP(sp); + argv_alloc(sp, cmdlen); + memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen); + exp->args[exp->argsoff]->bp[cmdlen] = '\0'; + exp->args[exp->argsoff]->len = cmdlen; + ++exp->argsoff; + excp->argv = exp->args; + excp->argc = exp->argsoff; + return (0); +} + +/* + * argv_exp1 -- + * Do file name expansion on a string, and leave it in a string. + */ +int +argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; + int is_bang; +{ + EX_PRIVATE *exp; + size_t blen, len; + char *bp; + + GET_SPACE_RET(sp, bp, blen, 512); + + len = 0; + exp = EXP(sp); + if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { + FREE_SPACE(sp, bp, blen); + return (1); + } + + argv_alloc(sp, len); + memmove(exp->args[exp->argsoff]->bp, bp, len); + exp->args[exp->argsoff]->bp[len] = '\0'; + exp->args[exp->argsoff]->len = len; + ++exp->argsoff; + excp->argv = exp->args; + excp->argc = exp->argsoff; + + FREE_SPACE(sp, bp, blen); + return (0); +} + +/* + * argv_exp2 -- + * Do file name and shell expansion on a string, and break + * it up into an argv. + */ +int +argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; + int is_bang; +{ + size_t blen, len, n; + int rval; + char *bp, *p; + + GET_SPACE_RET(sp, bp, blen, 512); + +#define SHELLECHO "echo " +#define SHELLOFFSET (sizeof(SHELLECHO) - 1) + memmove(bp, SHELLECHO, SHELLOFFSET); + p = bp + SHELLOFFSET; + len = SHELLOFFSET; + +#if defined(DEBUG) && 0 + TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); +#endif + + if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) { + rval = 1; + goto err; + } + +#if defined(DEBUG) && 0 + TRACE(sp, "before shell: %d: {%s}\n", len, bp); +#endif + + /* + * Do shell word expansion -- it's very, very hard to figure out + * what magic characters the user's shell expects. If it's not + * pure vanilla, don't even try. + */ + for (p = bp, n = len; n > 0; --n, ++p) + if (!isalnum(*p) && !isblank(*p) && *p != '/' && *p != '.') + break; + if (n > 0) { + if (argv_sexp(sp, &bp, &blen, &len)) { + rval = 1; + goto err; + } + p = bp; + } else { + p = bp + SHELLOFFSET; + len -= SHELLOFFSET; + } + +#if defined(DEBUG) && 0 + TRACE(sp, "after shell: %d: {%s}\n", len, bp); +#endif + + rval = argv_exp3(sp, ep, excp, p, len); + +err: FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * argv_exp3 -- + * Take a string and break it up into an argv. + */ +int +argv_exp3(sp, ep, excp, cmd, cmdlen) + SCR *sp; + EXF *ep; + EXCMDARG *excp; + char *cmd; + size_t cmdlen; +{ + CHAR_T vlit; + EX_PRIVATE *exp; + size_t len; + int ch, off; + char *ap, *p; + + (void)term_key_ch(sp, K_VLNEXT, &vlit); + for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { + /* Skip any leading whitespace. */ + for (; cmdlen > 0; --cmdlen, ++cmd) { + ch = *cmd; + if (!isblank(ch)) + break; + } + if (cmdlen == 0) + break; + + /* + * Determine the length of this whitespace delimited + * argument. + * + * QUOTING NOTE: + * + * Skip any character preceded by the user's quoting + * character. + */ + for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) + if ((ch = *cmd) == vlit && cmdlen > 1) { + ++cmd; + --cmdlen; + } else if (isblank(ch)) + break; + + /* + * Copy the argument into place. + * + * QUOTING NOTE: + * + * Lose quote chars. + */ + argv_alloc(sp, len); + off = exp->argsoff; + exp->args[off]->len = len; + for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) + if (*ap == vlit) { + ++ap; + --exp->args[off]->len; + } + *p = '\0'; + } + excp->argv = exp->args; + excp->argc = exp->argsoff; + +#if defined(DEBUG) && 0 + for (cnt = 0; cnt < exp->argsoff; ++cnt) + TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); +#endif + return (0); +} + +/* + * argv_fexp -- + * Do file name and bang command expansion. + */ +static int +argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang) + SCR *sp; + EXCMDARG *excp; + char *cmd, *p, **bpp; + size_t cmdlen, *lenp, *blenp; + int is_bang; +{ + EX_PRIVATE *exp; + char *bp, *t; + size_t blen, len, tlen; + + /* Replace file name characters. */ + for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) + switch (*cmd) { + case '!': + if (!is_bang) + goto ins_ch; + exp = EXP(sp); + if (exp->lastbcomm == NULL) { + msgq(sp, M_ERR, + "No previous command to replace \"!\"."); + return (1); + } + len += tlen = strlen(exp->lastbcomm); + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, exp->lastbcomm, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '%': + if (sp->frp->cname == NULL && sp->frp->name == NULL) { + msgq(sp, M_ERR, + "No filename to substitute for %%."); + return (1); + } + tlen = strlen(t = FILENAME(sp->frp)); + len += tlen; + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, t, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '#': + /* + * Try the alternate file name first, then the + * previously edited file. + */ + if (sp->alt_name == NULL && (sp->p_frp == NULL || + sp->frp->cname == NULL && sp->frp->name == NULL)) { + msgq(sp, M_ERR, + "No filename to substitute for #."); + return (1); + } + if (sp->alt_name != NULL) + t = sp->alt_name; + else + t = FILENAME(sp->frp); + len += tlen = strlen(t); + ADD_SPACE_RET(sp, bp, blen, len); + memmove(p, t, tlen); + p += tlen; + F_SET(excp, E_MODIFY); + break; + case '\\': + /* + * QUOTING NOTE: + * + * Strip any backslashes that protected the file + * expansion characters. + */ + if (cmdlen > 1 && cmd[1] == '%' || cmd[1] == '#') + ++cmd; + /* FALLTHROUGH */ + default: +ins_ch: ++len; + ADD_SPACE_RET(sp, bp, blen, len); + *p++ = *cmd; + } + + /* Nul termination. */ + ++len; + ADD_SPACE_RET(sp, bp, blen, len); + *p = '\0'; + + /* Return the new string length, buffer, buffer length. */ + *lenp = len - 1; + *bpp = bp; + *blenp = blen; + return (0); +} + +/* + * argv_alloc -- + * Make more space for arguments. + */ +static int +argv_alloc(sp, len) + SCR *sp; + size_t len; +{ + ARGS *ap; + EX_PRIVATE *exp; + int cnt, off; + + /* + * Allocate room for another argument, always leaving + * enough room for an ARGS structure with a length of 0. + */ +#define INCREMENT 20 + exp = EXP(sp); + off = exp->argsoff; + if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { + cnt = exp->argscnt + INCREMENT; + REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); + if (exp->args == NULL) { + (void)argv_free(sp); + goto mem; + } + memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *)); + exp->argscnt = cnt; + } + + /* First argument. */ + if (exp->args[off] == NULL) { + CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); + if (exp->args[off] == NULL) + goto mem; + } + + /* First argument buffer. */ + ap = exp->args[off]; + ap->len = 0; + if (ap->blen < len + 1) { + ap->blen = len + 1; + REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); + if (ap->bp == NULL) { + ap->bp = NULL; + ap->blen = 0; + F_CLR(ap, A_ALLOCATED); +mem: msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(ap, A_ALLOCATED); + } + + /* Second argument. */ + if (exp->args[++off] == NULL) { + CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); + if (exp->args[off] == NULL) + goto mem; + } + /* 0 length serves as end-of-argument marker. */ + exp->args[off]->len = 0; + return (0); +} + +/* + * argv_free -- + * Free up argument structures. + */ +int +argv_free(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + int off; + + exp = EXP(sp); + if (exp->args != NULL) { + for (off = 0; off < exp->argscnt; ++off) { + if (exp->args[off] == NULL) + continue; + if (F_ISSET(exp->args[off], A_ALLOCATED)) + free(exp->args[off]->bp); + FREE(exp->args[off], sizeof(ARGS)); + } + FREE(exp->args, exp->argscnt * sizeof(ARGS *)); + } + exp->args = NULL; + exp->argscnt = 0; + exp->argsoff = 0; + return (0); +} + +/* + * argv_sexp -- + * Fork a shell, pipe a command through it, and read the output into + * a buffer. + */ +static int +argv_sexp(sp, bpp, blenp, lenp) + SCR *sp; + char **bpp; + size_t *blenp, *lenp; +{ + FILE *ifp; + pid_t pid; + size_t blen, len; + int ch, rval, output[2]; + char *bp, *p, *sh, *sh_path; + + bp = *bpp; + blen = *blenp; + + sh_path = O_STR(sp, O_SHELL); + if ((sh = strrchr(sh_path, '/')) == NULL) + sh = sh_path; + else + ++sh; + + /* + * There are two different processes running through this code. + * They are named the utility and the parent. The utility reads + * from standard input and writes to the parent. The parent reads + * from the utility and writes into the buffer. The parent reads + * from output[0], and the utility writes to output[1]. + */ + if (pipe(output) < 0) { + msgq(sp, M_SYSERR, "pipe"); + return (1); + } + if ((ifp = fdopen(output[0], "r")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + + /* + * Do the minimal amount of work possible, the shell is going + * to run briefly and then exit. Hopefully. + */ + switch (pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); +err: (void)close(output[0]); + (void)close(output[1]); + return (1); + case 0: /* Utility. */ + /* Redirect stdout/stderr to the write end of the pipe. */ + (void)dup2(output[1], STDOUT_FILENO); + (void)dup2(output[1], STDERR_FILENO); + + /* Close the utility's file descriptors. */ + (void)close(output[0]); + (void)close(output[1]); + + /* Assumes that all shells have -c. */ + execl(sh_path, sh, "-c", bp, NULL); + msgq(sp, M_ERR, + "Error: execl: %s: %s", sh_path, strerror(errno)); + _exit(127); + default: + /* Close the pipe end the parent won't use. */ + (void)close(output[1]); + break; + } + + rval = 0; + + /* + * Copy process output into a buffer. + * + * !!! + * Historic vi apparently discarded leading \n and \r's from + * the shell output stream. We don't on the grounds that any + * shell that does that is broken. + */ + for (p = bp, len = 0, ch = EOF; + (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len) + if (blen < 5) { + ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2); + p = bp + len; + blen = *blenp - len; + } + + /* Delete the final newline, nul terminate the string. */ + if (p > bp && p[-1] == '\n' || p[-1] == '\r') { + --len; + *--p = '\0'; + } else + *p = '\0'; + *lenp = len; + + if (ferror(ifp)) { + msgq(sp, M_ERR, "I/O error: %s", sh); +binc_err: rval = 1; + } + (void)fclose(ifp); + + *bpp = bp; /* *blenp is already updated. */ + + /* Wait for the process. */ + return (proc_wait(sp, (long)pid, sh, 0) | rval); +} diff --git a/usr.bin/vi/nex/ex_at.c b/usr.bin/vi/nex/ex_at.c new file mode 100644 index 000000000000..51d001f304a7 --- /dev/null +++ b/usr.bin/vi/nex/ex_at.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_at.c 8.16 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_at -- :@[@ | buffer] + * :*[* | buffer] + * + * Execute the contents of the buffer. + */ +int +ex_at(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + CB *cbp; + EX_PRIVATE *exp; + TEXT *tp; + int name, lmode; + + exp = EXP(sp); + + /* Historically, @@ and ** execute the last buffer. */ + name = cmdp->buffer; + if (name == cmdp->cmd->name[0]) { + if (!exp->at_lbuf_set) { + msgq(sp, M_ERR, "No previous buffer to execute."); + return (1); + } + name = exp->at_lbuf; + } + + CBNAME(sp, cbp, name); + if (cbp == NULL) { + msgq(sp, M_ERR, "Buffer %s is empty.", charname(sp, name)); + return (1); + } + + /* Save for reuse. */ + exp->at_lbuf = name; + exp->at_lbuf_set = 1; + + /* + * If the buffer was cut in line mode or had portions of more + * than one line, <newlines> are appended to each line as it + * is pushed onto the stack. + */ + tp = cbp->textq.cqh_last; + lmode = F_ISSET(cbp, CB_LMODE) || tp->q.cqe_prev != (void *)&cbp->textq; + for (; tp != (void *)&cbp->textq; tp = tp->q.cqe_prev) + if ((lmode || tp->q.cqe_prev != (void *)&cbp->textq) && + term_push(sp, "\n", 1, 0, 0) || + term_push(sp, tp->lb, tp->len, 0, CH_QUOTED)) + return (1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_bang.c b/usr.bin/vi/nex/ex_bang.c new file mode 100644 index 000000000000..4f7222b4e78c --- /dev/null +++ b/usr.bin/vi/nex/ex_bang.c @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_bang.c 8.22 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex/sex_screen.h" + +/* + * ex_bang -- :[line [,line]] ! command + * + * Pass the rest of the line after the ! character to the program named by + * the O_SHELL option. + * + * Historical vi did NOT do shell expansion on the arguments before passing + * them, only file name expansion. This means that the O_SHELL program got + * "$t" as an argument if that is what the user entered. Also, there's a + * special expansion done for the bang command. Any exclamation points in + * the user's argument are replaced by the last, expanded ! command. + * + * There's some fairly amazing slop in this routine to make the different + * ways of getting here display the right things. It took a long time to + * get get right (wrong?), so be careful. + */ +int +ex_bang(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum filtertype ftype; + ARGS *ap; + EX_PRIVATE *exp; + MARK rm; + recno_t lno; + size_t blen; + int rval; + char *bp, *msg; + + + ap = cmdp->argv[0]; + if (ap->len == 0) { + msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); + return (1); + } + + /* Swap commands. */ + exp = EXP(sp); + if (exp->lastbcomm != NULL) + FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1); + if ((exp->lastbcomm = strdup(ap->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + /* + * If the command was modified by the expansion, we redisplay it. + * Redisplaying it in vi mode is tricky, and handled separately + * in each case below. If we're in ex mode, it's easy, so we just + * do it here. + */ + bp = NULL; + if (F_ISSET(cmdp, E_MODIFY)) { + if (IN_EX_MODE(sp)) { + (void)ex_printf(EXCOOKIE, "!%s\n", ap->bp); + (void)ex_fflush(EXCOOKIE); + } + /* + * Vi: Display the command if modified. Historic vi displayed + * the command if it was modified due to file name and/or bang + * expansion. If piping lines, it was immediately overwritten + * by any error or line change reporting. We don't the user to + * have to page through the responses, so we only post it until + * it's erased by something else. Otherwise, pass it on to the + * ex_exec_proc routine to display after the screen has been + * cleaned up. + */ + if (IN_VI_MODE(sp)) { + GET_SPACE_RET(sp, bp, blen, ap->len + 2); + bp[0] = '!'; + memmove(bp + 1, ap->bp, ap->len + 1); + } + } + + /* + * If addresses were specified, pipe lines from the file through + * the command. + */ + if (cmdp->addrcnt != 0) { + if (bp != NULL) { + (void)sp->s_busy(sp, bp); + FREE_SPACE(sp, bp, blen); + } + /* + * !!! + * Historical vi permitted "!!" in an empty file. When it + * happens, we get called with two addresses of 1,1 and a + * bad attitude. + */ + ftype = FILTER; + if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) { + cmdp->addr1.lno = cmdp->addr2.lno = 0; + ftype = FILTER_READ; + } + } + if (filtercmd(sp, ep, + &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype)) + return (1); + sp->lno = rm.lno; + F_SET(exp, EX_AUTOPRINT); + return (0); + } + + /* + * If no addresses were specified, run the command. If the file + * has been modified and autowrite is set, write the file back. + * If the file has been modified, autowrite is not set and the + * warn option is set, tell the user about the file. + */ + msg = "\n"; + if (F_ISSET(ep, F_MODIFIED)) + if (O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) { + rval = 1; + goto ret; + } + } else if (O_ISSET(sp, O_WARN)) + if (IN_VI_MODE(sp) && F_ISSET(cmdp, E_MODIFY)) + msg = "\nFile modified since last write.\n"; + else + msg = "File modified since last write.\n"; + + /* Run the command. */ + rval = ex_exec_proc(sp, ap->bp, bp, msg); + + /* Ex terminates with a bang. */ + if (IN_EX_MODE(sp)) + (void)write(STDOUT_FILENO, "!\n", 2); + + /* Vi requires user permission to continue. */ + if (IN_VI_MODE(sp)) + F_SET(sp, S_CONTINUE); + + /* Free the extra space. */ +ret: if (bp != NULL) + FREE_SPACE(sp, bp, blen); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_cd.c b/usr.bin/vi/nex/ex_cd.c new file mode 100644 index 000000000000..6b51bc51c15e --- /dev/null +++ b/usr.bin/vi/nex/ex_cd.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_cd.c 8.3 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_cd -- :cd[!] [directory] + * Change directories. + */ +int +ex_cd(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + char *dir, buf[MAXPATHLEN]; + + switch (cmdp->argc) { + case 0: + if ((dir = getenv("HOME")) == NULL) { + msgq(sp, M_ERR, + "Environment variable HOME not set."); + return (1); + } + break; + case 1: + dir = cmdp->argv[0]->bp; + break; + default: + abort(); + } + + if (chdir(dir) < 0) { + msgq(sp, M_SYSERR, dir); + return (1); + } + if (getcwd(buf, sizeof(buf)) != NULL) + msgq(sp, M_INFO, "New directory: %s", buf); + return (0); +} diff --git a/usr.bin/vi/nex/ex_delete.c b/usr.bin/vi/nex/ex_delete.c new file mode 100644 index 000000000000..5b737e8f547d --- /dev/null +++ b/usr.bin/vi/nex/ex_delete.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_delete.c 8.6 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags] + * + * Delete lines from the file. + */ +int +ex_delete(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + /* Yank the lines. */ + if (cut(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &cmdp->addr2, CUT_DELETE | CUT_LINEMODE)) + return (1); + + /* Delete the lines. */ + if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1)) + return (1); + + /* Set the cursor to the line after the last line deleted. */ + sp->lno = cmdp->addr1.lno; + + /* Or the last line in the file if deleted to the end of the file. */ + if (file_lline(sp, ep, &lno)) + return (1); + if (sp->lno > lno) + sp->lno = lno; + return (0); +} diff --git a/usr.bin/vi/nex/ex_digraph.c b/usr.bin/vi/nex/ex_digraph.c new file mode 100644 index 000000000000..20f1bf327ded --- /dev/null +++ b/usr.bin/vi/nex/ex_digraph.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_digraph.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#ifndef NO_DIGRAPH +#include <sys/types.h> + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +static void do_digraph __P((SCR *, EXF *, int, u_char *)); + +/* This stuff is used to build the default digraphs table. */ +static u_char digtable[][4] = { +# ifdef CS_IBMPC + "C,\200", "u\"\1", "e'\2", "a^\3", + "a\"\4", "a`\5", "a@\6", "c,\7", + "e^\10", "e\"\211", "e`\12", "i\"\13", + "i^\14", "i`\15", "A\"\16", "A@\17", + "E'\20", "ae\21", "AE\22", "o^\23", + "o\"\24", "o`\25", "u^\26", "u`\27", + "y\"\30", "O\"\31", "U\"\32", "a'\240", + "i'!", "o'\"", "u'#", "n~$", + "N~%", "a-&", "o-'", "~?(", + "~!-", "\"<.", "\">/", +# ifdef CS_SPECIAL + "2/+", "4/,", "^+;", "^q<", + "^c=", "^r>", "^t?", "pp]", + "^^^", "oo_", "*a`", "*ba", + "*pc", "*Sd", "*se", "*uf", + "*tg", "*Ph", "*Ti", "*Oj", + "*dk", "*Hl", "*hm", "*En", + "*No", "eqp", "pmq", "ger", + "les", "*It", "*iu", "*/v", + "*=w", "sq{", "^n|", "^2}", + "^3~", "^_\377", +# endif /* CS_SPECIAL */ +# endif /* CS_IBMPC */ +# ifdef CS_LATIN1 + "~!!", "a-*", "\">+", "o-:", + "\"<>", "~??", + + "A`@", "A'A", "A^B", "A~C", + "A\"D", "A@E", "AEF", "C,G", + "E`H", "E'I", "E^J", "E\"K", + "I`L", "I'M", "I^N", "I\"O", + "-DP", "N~Q", "O`R", "O'S", + "O^T", "O~U", "O\"V", "O/X", + "U`Y", "U'Z", "U^[", "U\"\\", + "Y'_", + + "a``", "a'a", "a^b", "a~c", + "a\"d", "a@e", "aef", "c,g", + "e`h", "e'i", "e^j", "e\"k", + "i`l", "i'm", "i^n", "i\"o", + "-dp", "n~q", "o`r", "o's", + "o^t", "o~u", "o\"v", "o/x", + "u`y", "u'z", "u^{", "u\"|", + "y'~", +# endif /* CS_LATIN1 */ + "" +}; + +int +digraph_init(sp) + SCR *sp; +{ + int i; + + for (i = 0; *digtable[i]; i++) + do_digraph(sp, NULL, 0, digtable[i]); + do_digraph(sp, NULL, 0, NULL); + return (0); +} + +int +ex_digraph(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp); + return (0); +} + +static struct _DIG +{ + struct _DIG *next; + char key1; + char key2; + char dig; + char save; +} *digs; + +int +digraph(sp, key1, key2) + SCR *sp; + char key1; /* the underlying character */ + char key2; /* the second character */ +{ + int new_key; + register struct _DIG *dp; + + /* if digraphs are disabled, then just return the new char */ + if (O_ISSET(sp, O_DIGRAPH)) + { + return key2; + } + + /* remember the new key, so we can return it if this isn't a digraph */ + new_key = key2; + + /* sort key1 and key2, so that their original order won't matter */ + if (key1 > key2) + { + key2 = key1; + key1 = new_key; + } + + /* scan through the digraph chart */ + for (dp = digs; + dp && (dp->key1 != key1 || dp->key2 != key2); + dp = dp->next) + { + } + + /* if this combination isn't in there, just use the new key */ + if (!dp) + { + return new_key; + } + + /* else use the digraph key */ + return dp->dig; +} + +/* this function lists or defines digraphs */ +static void +do_digraph(sp, ep, bang, extra) + SCR *sp; + EXF *ep; + int bang; + u_char *extra; +{ + int dig; + register struct _DIG *dp; + struct _DIG *prev; + static int user_defined = 0; /* boolean: are all later digraphs user-defined? */ + char listbuf[8]; + + /* if "extra" is NULL, then we've reached the end of the built-ins */ + if (!extra) + { + user_defined = 1; + return; + } + + /* if no args, then display the existing digraphs */ + if (*extra < ' ') + { + listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' '; + listbuf[7] = '\0'; + for (dig = 0, dp = digs; dp; dp = dp->next) + { + if (dp->save || bang) + { + dig += 7; + if (dig >= sp->cno) + { + addch('\n'); + refresh(); + dig = 7; + } + listbuf[3] = dp->key1; + listbuf[4] = dp->key2; + listbuf[6] = dp->dig; + addstr(listbuf); + } + } + addch('\n'); + refresh(); + return; + } + + /* make sure we have at least two characters */ + if (!extra[1]) + { + msgq(sp, M_ERR, + "Digraphs must be composed of two characters"); + return; + } + + /* sort key1 and key2, so that their original order won't matter */ + if (extra[0] > extra[1]) + { + dig = extra[0]; + extra[0] = extra[1]; + extra[1] = dig; + } + + /* locate the new digraph character */ + for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++) + { + } + dig = extra[dig]; + if (!bang && dig) + { + dig |= 0x80; + } + + /* search for the digraph */ + for (prev = (struct _DIG *)0, dp = digs; + dp && (dp->key1 != extra[0] || dp->key2 != extra[1]); + prev = dp, dp = dp->next) + { + } + + /* deleting the digraph? */ + if (!dig) + { + if (!dp) + { +#ifndef CRUNCH + msgq(sp, M_ERR, + "%c%c not a digraph", extra[0], extra[1]); +#endif + return; + } + if (prev) + prev->next = dp->next; + else + digs = dp->next; + free(dp); + return; + } + + /* if necessary, create a new digraph struct for the new digraph */ + if (dig && !dp) + { + MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG)); + if (dp == NULL) + return; + if (prev) + prev->next = dp; + else + digs = dp; + dp->next = (struct _DIG *)0; + } + + /* assign it the new digraph value */ + dp->key1 = extra[0]; + dp->key2 = extra[1]; + dp->dig = dig; + dp->save = user_defined; +} + +void +digraph_save(sp, fd) + SCR *sp; + int fd; +{ + static char buf[] = "digraph! XX Y\n"; + register struct _DIG *dp; + + for (dp = digs; dp; dp = dp->next) + { + if (dp->save) + { + buf[9] = dp->key1; + buf[10] = dp->key2; + buf[12] = dp->dig; + write(fd, buf, (unsigned)14); + } + } +} +#endif diff --git a/usr.bin/vi/nex/ex_display.c b/usr.bin/vi/nex/ex_display.c new file mode 100644 index 000000000000..0cfef42f675b --- /dev/null +++ b/usr.bin/vi/nex/ex_display.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_display.c 8.14 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "tag.h" +#include "excmd.h" + +static int bdisplay __P((SCR *, EXF *)); +static void db __P((SCR *, CB *, char *)); + +/* + * ex_display -- :display b[uffers] | s[creens] | t[ags] + * + * Display buffers, tags or screens. + */ +int +ex_display(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch (cmdp->argv[0]->bp[0]) { + case 'b': + return (bdisplay(sp, ep)); + case 's': + return (ex_sdisplay(sp, ep)); + case 't': + return (ex_tagdisplay(sp, ep)); + } + msgq(sp, M_ERR, + "Unknown display argument %s, use b[uffers], s[creens], or t[ags].", + cmdp->argv[0]); + return (1); +} + +/* + * bdisplay -- + * + * Display buffers. + */ +static int +bdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + CB *cbp; + + if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) { + (void)ex_printf(EXCOOKIE, "No cut buffers to display."); + return (0); + } + + /* Buffers can be infinitely long, make it interruptible. */ + F_SET(sp, S_INTERRUPTIBLE); + + /* Display regular cut buffers. */ + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) { + if (isdigit(cbp->name)) + continue; + if (cbp->textq.cqh_first != (void *)&cbp->textq) + db(sp, cbp, NULL); + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + } + /* Display numbered buffers. */ + for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) { + if (!isdigit(cbp->name)) + continue; + if (cbp->textq.cqh_first != (void *)&cbp->textq) + db(sp, cbp, NULL); + if (F_ISSET(sp, S_INTERRUPTED)) + return (0); + } + /* Display default buffer. */ + if ((cbp = sp->gp->dcbp) != NULL) + db(sp, cbp, "default buffer"); + return (0); +} + +/* + * db -- + * Display a buffer. + */ +static void +db(sp, cbp, name) + SCR *sp; + CB *cbp; + char *name; +{ + TEXT *tp; + size_t len; + char *p; + + (void)ex_printf(EXCOOKIE, "********** %s%s\n", + name == NULL ? charname(sp, cbp->name) : name, + F_ISSET(cbp, CB_LMODE) ? " (line mode)" : ""); + for (tp = cbp->textq.cqh_first; + tp != (void *)&cbp->textq; tp = tp->q.cqe_next) { + for (len = tp->len, p = tp->lb; len--;) { + (void)ex_printf(EXCOOKIE, "%s", charname(sp, *p++)); + if (F_ISSET(sp, S_INTERRUPTED)) + return; + } + (void)ex_printf(EXCOOKIE, "\n"); + } +} diff --git a/usr.bin/vi/nex/ex_edit.c b/usr.bin/vi/nex/ex_edit.c new file mode 100644 index 000000000000..eeafedaed778 --- /dev/null +++ b/usr.bin/vi/nex/ex_edit.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_edit.c 8.14 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_edit -- :e[dit][!] [+cmd] [file] + * :vi[sual][!] [+cmd] [file] + * + * Edit a file; if none specified, re-edit the current file. The second + * form of the command can only be executed while in vi mode. See the + * hack in ex.c:ex_cmd(). + * + * !!! + * Historic vi didn't permit the '+' command form without specifying + * a file name as well. + */ +int +ex_edit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + FREF *frp; + + frp = sp->frp; + switch (cmdp->argc) { + case 0: + /* + * If the name has been changed, we edit that file, not the + * original name. If the user was editing a temporary file, + * create another one. The reason for this is that we do + * special exit processing of temporary files, and reusing + * them is tricky. + */ + if (frp->cname != NULL) { + if ((frp = file_add(sp, frp, frp->cname, 1)) == NULL) + return (1); + set_alt_name(sp, sp->frp->cname); + } else if (frp->name == NULL) + if ((frp = file_add(sp, frp, NULL, 1)) == NULL) + return (1); + break; + case 1: + ap = cmdp->argv[0]; + if ((frp = file_add(sp, sp->frp, ap->bp, 1)) == NULL) + return (1); + set_alt_name(sp, ap->bp); + break; + default: + abort(); + } + + /* + * Check for modifications. + * + * !!! + * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit. + */ + if (F_ISSET(ep, F_MODIFIED) && + ep->refcnt <= 1 && !F_ISSET(cmdp, E_FORCE)) { + msgq(sp, M_ERR, + "Modified since last write; write or use ! to override."); + return (1); + } + + /* Switch files. */ + if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) + return (1); + F_SET(sp, S_FSWITCH); + return (0); +} diff --git a/usr.bin/vi/nex/ex_equal.c b/usr.bin/vi/nex/ex_equal.c new file mode 100644 index 000000000000..f0a18bef41f4 --- /dev/null +++ b/usr.bin/vi/nex/ex_equal.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_equal.c 8.4 (Berkeley) 11/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_equal -- :address = + * Print out the line number matching the specified address, or the + * last line number in the file if no address specified. + */ +int +ex_equal(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + if (F_ISSET(cmdp, E_ADDRDEF)) { + if (file_lline(sp, ep, &lno)) + return (1); + (void)ex_printf(EXCOOKIE, "%ld\n", lno); + } else + (void)ex_printf(EXCOOKIE, "%ld\n", cmdp->addr1.lno); + return (0); +} diff --git a/usr.bin/vi/nex/ex_exit.c b/usr.bin/vi/nex/ex_exit.c new file mode 100644 index 000000000000..b0f929a5bbe3 --- /dev/null +++ b/usr.bin/vi/nex/ex_exit.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_exit.c 8.7 (Berkeley) 12/10/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_quit -- :quit[!] + * Quit. + */ +int +ex_quit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + force = F_ISSET(cmdp, E_FORCE); + + /* Check for modifications. */ + if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) { + msgq(sp, M_ERR, + "Modified since last write; write or use ! to override."); + return (1); + } + + /* + * !!! + * Historic practice: quit! or two quit's done in succession + * (where ZZ counts as a quit) didn't check for other files. + * + * Also check for related screens; if they exist, quit, the + * user will get the message on the last screen. + */ + if (!force && sp->ccnt != sp->q_ccnt + 1 && + ep->refcnt <= 1 && file_unedited(sp) != NULL) { + sp->q_ccnt = sp->ccnt; + msgq(sp, M_ERR, + "More files; use \":n\" to go to the next file, \":q!\" to quit."); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} diff --git a/usr.bin/vi/nex/ex_file.c b/usr.bin/vi/nex/ex_file.c new file mode 100644 index 000000000000..d556d5214e0e --- /dev/null +++ b/usr.bin/vi/nex/ex_file.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_file.c 8.7 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_file -- :f[ile] [name] + * Status line and change the file's name. + */ +int +ex_file(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + FREF *frp; + char *p, *t; + + switch (cmdp->argc) { + case 0: + break; + case 1: + frp = sp->frp; + + /* Make sure can allocate enough space. */ + if ((p = strdup(cmdp->argv[0]->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + /* If already have a file name, it becomes the alternate. */ + if ((t = FILENAME(frp)) != NULL) + set_alt_name(sp, t); + + /* Free any previously changed name. */ + if (frp->cname != NULL) + free(frp->cname); + frp->cname = p; + + /* The read-only bit follows the file name; clear it. */ + F_CLR(frp, FR_RDONLY); + + /* Have to force a write if the file exists, next time. */ + F_CLR(frp, FR_CHANGEWRITE); + break; + default: + abort(); + } + status(sp, ep, sp->lno, 1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_global.c b/usr.bin/vi/nex/ex_global.c new file mode 100644 index 000000000000..4f942a39f680 --- /dev/null +++ b/usr.bin/vi/nex/ex_global.c @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_global.c 8.29 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "interrupt.h" + +enum which {GLOBAL, VGLOBAL}; + +static int global __P((SCR *, EXF *, EXCMDARG *, enum which)); +static void global_intr __P((int)); + +/* + * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] + * Exec on lines matching a pattern. + */ +int +ex_global(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (global(sp, ep, + cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL)); +} + +/* + * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands] + * Exec on lines not matching a pattern. + */ +int +ex_vglobal(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (global(sp, ep, cmdp, VGLOBAL)); +} + +static int +global(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + DECLARE_INTERRUPTS; + RANGE *rp; + EX_PRIVATE *exp; + recno_t elno, lno; + regmatch_t match[1]; + regex_t *re, lre; + size_t clen, len; + int delim, eval, reflags, replaced, rval; + char *cb, *ptrn, *p, *t; + + /* + * Skip leading white space. Historic vi allowed any non- + * alphanumeric to serve as the global command delimiter. + */ + for (p = cmdp->argv[0]->bp; isblank(*p); ++p); + if (*p == '\0' || isalnum(*p)) { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + delim = *p++; + + /* + * Get the pattern string, toss escaped characters. + * + * QUOTING NOTE: + * Only toss an escaped character if it escapes a delimiter. + */ + for (ptrn = t = p;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + /* + * !!! + * Nul terminate the pattern string -- it's passed + * to regcomp which doesn't understand anything else. + */ + *t = '\0'; + break; + } + if (p[0] == '\\' && p[1] == delim) + ++p; + *t++ = *p++; + } + + /* If the pattern string is empty, use the last one. */ + if (*ptrn == '\0') { + if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + re = &sp->sre; + } else { + /* Set RE flags. */ + reflags = 0; + if (O_ISSET(sp, O_EXTENDED)) + reflags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + reflags |= REG_ICASE; + + /* Convert vi-style RE's to POSIX 1003.2 RE's. */ + if (re_conv(sp, &ptrn, &replaced)) + return (1); + + /* Compile the RE. */ + re = &lre; + eval = regcomp(re, ptrn, reflags); + + /* Free up any allocated memory. */ + if (replaced) + free(ptrn); + + if (eval) { + re_error(sp, eval, re); + return (1); + } + + /* + * Set saved RE. Historic practice is that + * globals set direction as well as the RE. + */ + sp->sre = lre; + sp->searchdir = FORWARD; + F_SET(sp, S_SRE_SET); + } + + /* Get a copy of the command string. */ + if ((clen = strlen(p)) == 0) { + msgq(sp, M_ERR, "No command string specified."); + return (1); + } + MALLOC_RET(sp, cb, char *, clen); + memmove(cb, p, clen); + + /* + * The global commands sets the substitute RE as well as + * the everything-else RE. + */ + sp->subre = sp->sre; + F_SET(sp, S_SUBRE_SET); + + /* Set the global flag, and set up interrupts. */ + F_SET(sp, S_GLOBAL); + SET_UP_INTERRUPTS(global_intr); + + /* + * For each line... The semantics of global matching are that we first + * have to decide which lines are going to get passed to the command, + * and then pass them to the command, ignoring other changes. There's + * really no way to do this in a single pass, since arbitrary line + * creation, deletion and movement can be done in the ex command. For + * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". + * What we do is create linked list of lines that are tracked through + * each ex command. There's a callback routine which the DB interface + * routines call when a line is created or deleted. This doesn't help + * the layering much. + */ + exp = EXP(sp); + for (rval = 0, lno = cmdp->addr1.lno, + elno = cmdp->addr2.lno; lno <= elno; ++lno) { + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) + goto interrupted; + + /* Get the line and search for a match. */ + if ((t = file_gline(sp, ep, lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + goto err; + } + match[0].rm_so = 0; + match[0].rm_eo = len; + switch(eval = regexec(re, t, 1, match, REG_STARTEND)) { + case 0: + if (cmd == VGLOBAL) + continue; + break; + case REG_NOMATCH: + if (cmd == GLOBAL) + continue; + break; + default: + re_error(sp, eval, re); + goto err; + } + + /* If follows the last entry, extend the last entry's range. */ + if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq && + rp->stop == lno - 1) { + ++rp->stop; + continue; + } + + /* Allocate a new range, and append it to the list. */ + CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE)); + if (rp == NULL) + goto err; + rp->start = rp->stop = lno; + CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q); + } + + exp = EXP(sp); + exp->range_lno = OOBLNO; + for (;;) { + /* + * Start at the beginning of the range each time, it may have + * been changed (or exhausted) if lines were inserted/deleted. + */ + if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq) + break; + if (rp->start > rp->stop) { + CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q); + free(rp); + continue; + } + + /* + * Execute the command, setting the cursor to the line so that + * relative addressing works. This means that the cursor moves + * to the last line sent to the command, by default, even if + * the command fails. + */ + exp->range_lno = sp->lno = rp->start++; + if (ex_cmd(sp, ep, cb, clen)) + goto err; + + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) { +interrupted: msgq(sp, M_INFO, "Interrupted."); + break; + } + } + + /* Set the cursor to the new value, making sure it exists. */ + if (exp->range_lno != OOBLNO) { + if (file_lline(sp, ep, &lno)) + return (1); + sp->lno = + lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno; + } + if (0) { +err: rval = 1; + } + +interrupt_err: + F_CLR(sp, S_GLOBAL); + TEAR_DOWN_INTERRUPTS; + + /* Free any remaining ranges and the command buffer. */ + while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) { + CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q); + free(rp); + } + free(cb); + return (rval); +} + +/* + * global_insdel -- + * Update the ranges based on an insertion or deletion. + */ +void +global_insdel(sp, ep, op, lno) + SCR *sp; + EXF *ep; + enum operation op; + recno_t lno; +{ + EX_PRIVATE *exp; + RANGE *nrp, *rp; + + exp = EXP(sp); + + switch (op) { + case LINE_APPEND: + return; + case LINE_DELETE: + for (rp = exp->rangeq.cqh_first; + rp != (void *)&exp->rangeq; rp = nrp) { + nrp = rp->q.cqe_next; + /* If range less than the line, ignore it. */ + if (rp->stop < lno) + continue; + /* If range greater than the line, decrement range. */ + if (rp->start > lno) { + --rp->start; + --rp->stop; + continue; + } + /* Lno is inside the range, decrement the end point. */ + if (rp->start > --rp->stop) { + CIRCLEQ_REMOVE(&exp->rangeq, rp, q); + free(rp); + } + } + break; + case LINE_INSERT: + for (rp = exp->rangeq.cqh_first; + rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) { + /* If range less than the line, ignore it. */ + if (rp->stop < lno) + continue; + /* If range greater than the line, increment range. */ + if (rp->start >= lno) { + ++rp->start; + ++rp->stop; + continue; + } + /* + * Lno is inside the range, so the range must be split. + * Since we're inserting a new element, neither range + * can be exhausted. + */ + CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE)); + if (nrp == NULL) { + F_SET(sp, S_INTERRUPTED); + return; + } + nrp->start = lno + 1; + nrp->stop = rp->stop + 1; + rp->stop = lno - 1; + CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q); + rp = nrp; + } + break; + case LINE_RESET: + return; + } + /* + * If the command deleted/inserted lines, the cursor moves to + * the line after the deleted/inserted line. + */ + exp->range_lno = lno; +} + +/* + * global_intr -- + * Set the interrupt bit in any screen that is running an interruptible + * global. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one global running, it will be hard to decide which one to + * stop. + */ +static void +global_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_GLOBAL) && F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} diff --git a/usr.bin/vi/nex/ex_init.c b/usr.bin/vi/nex/ex_init.c new file mode 100644 index 000000000000..26acf2f44c92 --- /dev/null +++ b/usr.bin/vi/nex/ex_init.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_init.c 8.11 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "tag.h" + +/* + * ex_screen_copy -- + * Copy ex screen. + */ +int +ex_screen_copy(orig, sp) + SCR *orig, *sp; +{ + EX_PRIVATE *oexp, *nexp; + + /* Create the private ex structure. */ + CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE)); + sp->ex_private = nexp; + + /* Initialize queues. */ + TAILQ_INIT(&nexp->tagq); + TAILQ_INIT(&nexp->tagfq); + CIRCLEQ_INIT(&nexp->rangeq); + + if (orig == NULL) { + nexp->at_lbuf_set = 0; + } else { + oexp = EXP(orig); + + nexp->at_lbuf = oexp->at_lbuf; + nexp->at_lbuf_set = oexp->at_lbuf_set; + + if (oexp->lastbcomm != NULL && + (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return(1); + } + + if (ex_tagcopy(orig, sp)) + return (1); + } + + nexp->lastcmd = &cmds[C_PRINT]; + return (0); +} + +/* + * ex_screen_end -- + * End a vi screen. + */ +int +ex_screen_end(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + int rval; + + rval = 0; + exp = EXP(sp); + + if (argv_free(sp)) + rval = 1; + + if (exp->ibp != NULL) + FREE(exp->ibp, exp->ibp_len); + + if (exp->lastbcomm != NULL) + FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1); + + /* Free private memory. */ + FREE(exp, sizeof(EX_PRIVATE)); + sp->ex_private = NULL; + + return (rval); +} + +/* + * ex_init -- + * Initialize ex. + */ +int +ex_init(sp, ep) + SCR *sp; + EXF *ep; +{ + size_t len; + + /* + * The default address is the last line of the file. If the address + * set bit is on for this file, load the address, ensuring that it + * exists. + */ + if (F_ISSET(sp->frp, FR_CURSORSET)) { + sp->lno = sp->frp->lno; + sp->cno = sp->frp->cno; + + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } else if (sp->cno >= len) + sp->cno = 0; + } else { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } + + /* Display the status line. */ + return (status(sp, ep, sp->lno, 0)); +} + +/* + * ex_end -- + * End ex session. + */ +int +ex_end(sp) + SCR *sp; +{ + /* Save the cursor location. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + return (0); +} + +/* + * ex_optchange -- + * Handle change of options for vi. + */ +int +ex_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_TAGS: + return (ex_tagalloc(sp, O_STR(sp, O_TAGS))); + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_join.c b/usr.bin/vi/nex/ex_join.c new file mode 100644 index 000000000000..33bace97500e --- /dev/null +++ b/usr.bin/vi/nex/ex_join.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_join.c 8.8 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_join -- :[line [,line]] j[oin][!] [count] [flags] + * Join lines. + */ +int +ex_join(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t from, to; + size_t blen, clen, len, tlen; + int echar, extra, first; + char *bp, *p, *tbp; + + from = cmdp->addr1.lno; + to = cmdp->addr2.lno; + + /* Check for no lines to join. */ + if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) { + msgq(sp, M_ERR, "No following lines to join."); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, 256); + + /* + * The count for the join command was off-by-one, + * historically, to other counts for other commands. + */ + if (F_ISSET(cmdp, E_COUNT)) + ++cmdp->addr2.lno; + + /* + * If only a single address specified, or, the same address + * specified twice, the from/two addresses will be the same. + */ + if (cmdp->addr1.lno == cmdp->addr2.lno) + ++cmdp->addr2.lno; + + clen = tlen = 0; + for (first = 1, from = cmdp->addr1.lno, + to = cmdp->addr2.lno; from <= to; ++from) { + /* + * Get next line. Historic versions of vi allowed "10J" while + * less than 10 lines from the end-of-file, so we do too. + */ + if ((p = file_gline(sp, ep, from, &len)) == NULL) { + cmdp->addr2.lno = from - 1; + break; + } + + /* Empty lines just go away. */ + if (len == 0) + continue; + + /* + * Get more space if necessary. Note, tlen isn't the length + * of the new line, it's roughly the amount of space needed. + * tbp - bp is the length of the new line. + */ + tlen += len + 2; + ADD_SPACE_RET(sp, bp, blen, tlen); + tbp = bp + clen; + + /* + * Historic practice: + * + * If force specified, join without modification. + * If the current line ends with whitespace, strip leading + * whitespace from the joined line. + * If the next line starts with a ), do nothing. + * If the current line ends with ., ? or !, insert two spaces. + * Else, insert one space. + * + * Echar is the last character in the last line joined. + */ + extra = 0; + if (!first && !F_ISSET(cmdp, E_FORCE)) { + if (isblank(echar)) + for (; len && isblank(*p); --len, ++p); + else if (p[0] != ')') { + if (strchr(".?!", echar)) { + *tbp++ = ' '; + ++clen; + extra = 1; + } + *tbp++ = ' '; + ++clen; + for (; len && isblank(*p); --len, ++p); + } + } + + if (len != 0) { + memmove(tbp, p, len); + tbp += len; + clen += len; + echar = p[len - 1]; + } else + echar = ' '; + + /* + * Historic practice for vi was to put the cursor at the first + * inserted whitespace character, if there was one, or the + * first character of the joined line, if there wasn't, or the + * last character of the line if joined to an empty line. If + * a count was specified, the cursor was moved as described + * for the first line joined, ignoring subsequent lines. If + * the join was a ':' command, the cursor was placed at the + * first non-blank character of the line unless the cursor was + * "attracted" to the end of line when the command was executed + * in which case it moved to the new end of line. There are + * probably several more special cases, but frankly, my dear, + * I don't give a damn. This implementation puts the cursor + * on the first inserted whitespace character, the first + * character of the joined line, or the last character of the + * line regardless. Note, if the cursor isn't on the joined + * line (possible with : commands), it is reset to the starting + * line. + */ + if (first) { + sp->cno = (tbp - bp) - (1 + extra); + first = 0; + } else + sp->cno = (tbp - bp) - len - (1 + extra); + } + sp->lno = cmdp->addr1.lno; + + /* Delete the joined lines. */ + for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to) + if (file_dline(sp, ep, to)) + goto err; + + /* Reset the original line. */ + if (file_sline(sp, ep, from, bp, tbp - bp)) { +err: FREE_SPACE(sp, bp, blen); + return (1); + } + FREE_SPACE(sp, bp, blen); + + sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1; + return (0); +} diff --git a/usr.bin/vi/nex/ex_map.c b/usr.bin/vi/nex/ex_map.c new file mode 100644 index 000000000000..0154c34699be --- /dev/null +++ b/usr.bin/vi/nex/ex_map.c @@ -0,0 +1,158 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_map.c 8.6 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * ex_map -- :map[!] [input] [replacement] + * Map a key/string or display mapped keys. + * + * Historical note: + * Historic vi maps were fairly bizarre, and likely to differ in + * very subtle and strange ways from this implementation. Two + * things worth noting are that vi would often hang or drop core + * if the map was strange enough (ex: map X "xy$@x^V), or, simply + * not work. One trick worth remembering is that if you put a + * mark at the start of the map, e.g. map X mx"xy ...), or if you + * put the map in a .exrc file, things would often work much better. + * No clue why. + */ +int +ex_map(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum seqtype stype; + CHAR_T *input; + size_t nlen; + int key; + char *name, buf[10]; + + stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND; + + switch (cmdp->argc) { + case 0: + if (seq_dump(sp, stype, 1) == 0) + msgq(sp, M_INFO, "No %s map entries.", + stype == SEQ_INPUT ? "input" : "command"); + return (0); + case 2: + input = cmdp->argv[0]->bp; + break; + default: + abort(); + } + + /* + * If the mapped string is #[0-9] (and wasn't quoted in any + * way, then map to a function key. + */ + nlen = 0; + if (input[0] == '#' && isdigit(input[1]) && !input[2]) { + key = atoi(input + 1); + nlen = snprintf(buf, sizeof(buf), "f%d", key); +#ifdef notdef + if (FKEY[key]) { /* CCC */ + input = FKEY[key]; + name = buf; + } else { + msgq(sp, M_ERR, "This terminal has no %s key.", buf); + return (1); + } +#else + name = NULL; +#endif + } else { + name = NULL; + + /* Some single keys may not be remapped in command mode. */ + if (stype == SEQ_COMMAND && input[1] == '\0') + switch (term_key_val(sp, input[0])) { + case K_COLON: + case K_CR: + case K_ESCAPE: + case K_NL: + msgq(sp, M_ERR, + "The %s character may not be remapped.", + charname(sp, input[0])); + return (1); + } + } + return (seq_set(sp, name, nlen, input, cmdp->argv[0]->len, + cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, 1)); +} + +/* + * ex_unmap -- (:unmap[!] key) + * Unmap a key. + */ +int +ex_unmap(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len, + F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) { + msgq(sp, M_INFO, "\"%s\" isn't mapped.", cmdp->argv[0]->bp); + return (1); + } + return (0); +} + +/* + * map_save -- + * Save the mapped sequences to a file. + */ +int +map_save(sp, fp) + SCR *sp; + FILE *fp; +{ + if (seq_save(sp, fp, "map ", SEQ_COMMAND)) + return (1); + return (seq_save(sp, fp, "map! ", SEQ_INPUT)); +} diff --git a/usr.bin/vi/nex/ex_mark.c b/usr.bin/vi/nex/ex_mark.c new file mode 100644 index 000000000000..34efe21648b1 --- /dev/null +++ b/usr.bin/vi/nex/ex_mark.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_mark.c 8.4 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +int +ex_mark(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (cmdp->argv[0]->len != 1) { + msgq(sp, M_ERR, "Mark names must be a single character."); + return (1); + } + return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1)); +} diff --git a/usr.bin/vi/nex/ex_mkexrc.c b/usr.bin/vi/nex/ex_mkexrc.c new file mode 100644 index 000000000000..b26c7f814532 --- /dev/null +++ b/usr.bin/vi/nex/ex_mkexrc.c @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_mkexrc.c 8.8 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "seq.h" +#include "pathnames.h" + +/* + * ex_mkexrc -- :mkexrc[!] [file] + * + * Create (or overwrite) a .exrc file with the current info. + */ +int +ex_mkexrc(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + struct stat sb; + FILE *fp; + int fd, sverrno; + char *fname; + + switch (cmdp->argc) { + case 0: + fname = _PATH_EXRC; + break; + case 1: + fname = cmdp->argv[0]->bp; + set_alt_name(sp, fname); + break; + default: + abort(); + } + + if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) { + msgq(sp, M_ERR, + "%s exists, not written; use ! to override.", fname); + return (1); + } + + /* Create with max permissions of rw-r--r--. */ + if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + msgq(sp, M_SYSERR, fname); + return (1); + } + + if ((fp = fdopen(fd, "w")) == NULL) { + sverrno = errno; + (void)close(fd); + errno = sverrno; + goto e2; + } + + if (abbr_save(sp, fp) || ferror(fp)) + goto e1; + if (map_save(sp, fp) || ferror(fp)) + goto e1; + if (opts_save(sp, fp) || ferror(fp)) + goto e1; +#ifndef NO_DIGRAPH + digraph_save(sp, fd); +#endif + if (fclose(fp)) + goto e2; + + msgq(sp, M_INFO, "New .exrc file: %s. ", fname); + return (0); + +e1: sverrno = errno; + (void)fclose(fp); + errno = sverrno; +e2: msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno)); + return (1); +} diff --git a/usr.bin/vi/nex/ex_move.c b/usr.bin/vi/nex/ex_move.c new file mode 100644 index 000000000000..965618b0834f --- /dev/null +++ b/usr.bin/vi/nex/ex_move.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_move.c 8.6 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +enum which {COPY, MOVE}; +static int cm __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_copy -- :[line [,line]] co[py] line [flags] + * Copy selected lines. + */ +int +ex_copy(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cm(sp, ep, cmdp, COPY)); +} + +/* + * ex_move -- :[line [,line]] co[py] line + * Move selected lines. + */ +int +ex_move(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cm(sp, ep, cmdp, MOVE)); +} + +static int +cm(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + CB cb; + MARK fm1, fm2, m, tm; + recno_t diff; + int rval; + + fm1 = cmdp->addr1; + fm2 = cmdp->addr2; + tm.lno = cmdp->lineno; + tm.cno = 0; + + /* Make sure the destination is valid. */ + if (cmd == MOVE && tm.lno >= fm1.lno && tm.lno < fm2.lno) { + msgq(sp, M_ERR, "Destination line is inside move range."); + return (1); + } + + /* Save the text to a cut buffer. */ + memset(&cb, 0, sizeof(cb)); + CIRCLEQ_INIT(&cb.textq); + if (cut(sp, ep, &cb, NULL, &fm1, &fm2, CUT_LINEMODE)) + return (1); + + /* If we're not copying, delete the old text and adjust tm. */ + if (cmd == MOVE) { + if (delete(sp, ep, &fm1, &fm2, 1)) { + rval = 1; + goto err; + } + if (tm.lno >= fm1.lno) + tm.lno -= (fm2.lno - fm1.lno) + 1; + } + + /* Add the new text. */ + if (put(sp, ep, &cb, NULL, &tm, &m, 1)) { + rval = 1; + goto err; + } + + /* + * Move and copy put the cursor on the last line moved or copied. + * The returned cursor from the put routine is the first line put, + * not the last, because that's the semantics of vi. + */ + diff = (fm2.lno - fm1.lno) + 1; + sp->lno = m.lno + (diff - 1); + sp->cno = 0; + + sp->rptlines[cmd == COPY ? L_COPIED : L_MOVED] += diff; + rval = 0; + +err: (void)text_lfree(&cb.textq); + return (rval); +} diff --git a/usr.bin/vi/nex/ex_open.c b/usr.bin/vi/nex/ex_open.c new file mode 100644 index 000000000000..77b418a41d6f --- /dev/null +++ b/usr.bin/vi/nex/ex_open.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_open.c 8.2 (Berkeley) 10/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_open -- :[line] o[pen] [/pattern/] [flags] + * + * Switch to single line "open" mode. + */ +int +ex_open(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* If open option off, disallow open command. */ + if (!O_ISSET(sp, O_OPEN)) { + msgq(sp, M_ERR, + "The open command requires that the open option be set."); + return (1); + } + + msgq(sp, M_ERR, "The open command is not yet implemented."); + return (1); +} diff --git a/usr.bin/vi/nex/ex_preserve.c b/usr.bin/vi/nex/ex_preserve.c new file mode 100644 index 000000000000..a7f35485698d --- /dev/null +++ b/usr.bin/vi/nex/ex_preserve.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_preserve.c 8.4 (Berkeley) 11/8/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_preserve -- :pre[serve] + * Push the file to recovery. + */ +int +ex_preserve(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t lno; + + if (!F_ISSET(ep, F_RCV_ON)) { + msgq(sp, M_ERR, "Preservation of this file not possible."); + return (1); + } + + /* If recovery not initialized, do so. */ + if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep)) + return (1); + + /* Force the file to be read in, in case it hasn't yet. */ + if (file_lline(sp, ep, &lno)) + return (1); + + /* Sync to disk. */ + if (rcv_sync(sp, ep)) + return (1); + + /* Preserve the recovery files. */ + F_SET(ep, F_RCV_NORM); + + msgq(sp, M_INFO, "File preserved."); + return (0); +} diff --git a/usr.bin/vi/nex/ex_print.c b/usr.bin/vi/nex/ex_print.c new file mode 100644 index 000000000000..d25e9ff563b1 --- /dev/null +++ b/usr.bin/vi/nex/ex_print.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_print.c 8.6 (Berkeley) 11/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_list -- :[line [,line]] l[ist] [count] [flags] + * + * Display the addressed lines such that the output is unambiguous. + */ +int +ex_list(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, + &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_number -- :[line [,line]] nu[mber] [count] [flags] + * + * Display the addressed lines with a leading line number. + */ +int +ex_number(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, + &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_pr -- :[line [,line]] p[rint] [count] [flags] + * + * Display the addressed lines. + */ +int +ex_pr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags)) + return (1); + sp->lno = cmdp->addr2.lno; + sp->cno = cmdp->addr2.cno; + return (0); +} + +/* + * ex_print -- + * Print the selected lines. + */ +int +ex_print(sp, ep, fp, tp, flags) + SCR *sp; + EXF *ep; + MARK *fp, *tp; + register int flags; +{ + register int ch, col, rlen; + recno_t from, to; + size_t len; + int cnt; + char *p, buf[10]; + + F_SET(sp, S_INTERRUPTIBLE); + for (from = fp->lno, to = tp->lno; from <= to; ++from) { + /* Display the line number. */ + if (LF_ISSET(E_F_HASH)) + col = ex_printf(EXCOOKIE, "%7ld ", from); + else + col = 0; + + /* + * Display the line. The format for E_F_PRINT isn't very good, + * especially in handling end-of-line tabs, but they're almost + * backward compatible. + */ + if ((p = file_gline(sp, ep, from, &len)) == NULL) { + GETLINE_ERR(sp, from); + return (1); + } + +#define WCHECK(ch) { \ + if (col == sp->cols) { \ + (void)ex_printf(EXCOOKIE, "\n"); \ + col = 0; \ + } \ + (void)ex_printf(EXCOOKIE, "%c", ch); \ + ++col; \ +} + for (rlen = len; rlen--;) { + ch = *p++; + if (LF_ISSET(E_F_LIST)) + if (ch != '\t' && isprint(ch)) { + WCHECK(ch); + } else if (ch & 0x80) { + (void)snprintf(buf, + sizeof(buf), "\\%03o", ch); + len = strlen(buf); + for (cnt = 0; cnt < len; ++cnt) + WCHECK(buf[cnt]); + } else { + WCHECK('^'); + WCHECK(ch + 0x40); + } + else { + ch &= 0x7f; + if (ch == '\t') { + while (col < sp->cols && + ++col % O_VAL(sp, O_TABSTOP)) + (void)ex_printf(EXCOOKIE, " "); + if (col == sp->cols) { + col = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } + } else if (isprint(ch)) { + WCHECK(ch); + } else if (ch == '\n') { + col = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else { + WCHECK('^'); + WCHECK(ch + 0x40); + } + } + } + if (LF_ISSET(E_F_LIST)) { + WCHECK('$'); + } else if (len == 0) { + /* + * If the line is empty, output a space + * to overwrite the colon prompt. + */ + WCHECK(' '); + } + (void)ex_printf(EXCOOKIE, "\n"); + + if (F_ISSET(sp, S_INTERRUPTED)) + break; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_put.c b/usr.bin/vi/nex/ex_put.c new file mode 100644 index 000000000000..3bc687fd1496 --- /dev/null +++ b/usr.bin/vi/nex/ex_put.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_put.c 8.4 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_put -- [line] pu[t] [buffer] + * + * Append a cut buffer into the file. + */ +int +ex_put(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + MARK m; + + m.lno = sp->lno; + m.cno = sp->cno; + if (put(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &m, 1)) + return (1); + return (0); +} diff --git a/usr.bin/vi/nex/ex_read.c b/usr.bin/vi/nex/ex_read.c new file mode 100644 index 000000000000..9328b93c329a --- /dev/null +++ b/usr.bin/vi/nex/ex_read.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_read.c 8.21 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_read -- :read [file] + * :read [! cmd] + * Read from a file or utility. + */ +int +ex_read(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + struct stat sb; + FILE *fp; + MARK rm; + recno_t nlines; + size_t blen, len; + int rval; + char *bp, *name; + + /* + * If "read !", it's a pipe from a utility. + * + * !!! + * Historical vi wouldn't undo a filter read, for no apparent + * reason. + */ + if (F_ISSET(cmdp, E_FORCE)) { + /* Expand the user's argument. */ + if (argv_exp1(sp, ep, + cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0)) + return (1); + + /* If argc still 1, there wasn't anything to expand. */ + if (cmdp->argc == 1) { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + /* Redisplay the user's argument if it's changed. */ + if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) { + len = cmdp->argv[1]->len; + GET_SPACE_RET(sp, bp, blen, len + 2); + bp[0] = '!'; + memmove(bp + 1, cmdp->argv[1], cmdp->argv[1]->len + 1); + (void)sp->s_busy(sp, bp); + FREE_SPACE(sp, bp, blen); + } + + if (filtercmd(sp, ep, + &cmdp->addr1, NULL, &rm, cmdp->argv[1]->bp, FILTER_READ)) + return (1); + sp->lno = rm.lno; + return (0); + } + + /* Expand the user's argument. */ + if (argv_exp2(sp, ep, + cmdp, cmdp->argv[0]->bp, cmdp->argv[0]->len, 0)) + return (1); + + switch (cmdp->argc) { + case 1: + /* + * No arguments, read the current file. + * Doesn't set the alternate file name. + */ + name = FILENAME(sp->frp); + break; + case 2: + /* + * One argument, read it. + * Sets the alternate file name. + */ + name = cmdp->argv[1]->bp; + set_alt_name(sp, name); + break; + default: + /* If expanded to more than one argument, object. */ + msgq(sp, M_ERR, + "%s expanded into too many file names", cmdp->argv[0]->bp); + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + /* + * !!! + * Historically, vi did not permit reads from non-regular files, + * nor did it distinguish between "read !" and "read!", so there + * was no way to "force" it. + */ + if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) { + msgq(sp, M_SYSERR, name); + return (1); + } + if (!S_ISREG(sb.st_mode)) { + (void)fclose(fp); + msgq(sp, M_ERR, "Only regular files may be read."); + return (1); + } + + rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1); + + /* + * Set the cursor to the first line read in, if anything read + * in, otherwise, the address. (Historic vi set it to the + * line after the address regardless, but since that line may + * not exist we don't bother.) + */ + sp->lno = cmdp->addr1.lno; + if (nlines) + ++sp->lno; + + F_SET(EXP(sp), EX_AUTOPRINT); + return (rval); +} + +/* + * ex_readfp -- + * Read lines into the file. + */ +int +ex_readfp(sp, ep, name, fp, fm, nlinesp, success_msg) + SCR *sp; + EXF *ep; + char *name; + FILE *fp; + MARK *fm; + recno_t *nlinesp; + int success_msg; +{ + EX_PRIVATE *exp; + u_long ccnt; + size_t len; + recno_t lno, nlines; + int rval; + + /* + * Add in the lines from the output. Insertion starts at the line + * following the address. + */ + ccnt = 0; + rval = 0; + exp = EXP(sp); + for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno) { + if (file_aline(sp, ep, 1, lno, exp->ibp, len)) { + rval = 1; + break; + } + ccnt += len; + } + + if (ferror(fp)) { + msgq(sp, M_SYSERR, name); + rval = 1; + } + + if (fclose(fp)) { + msgq(sp, M_SYSERR, name); + return (1); + } + + if (rval) + return (1); + + /* Return the number of lines read in. */ + nlines = lno - fm->lno; + if (nlinesp != NULL) + *nlinesp = nlines; + + if (success_msg) + msgq(sp, M_INFO, "%s: %lu line%s, %lu characters.", + name, nlines, nlines == 1 ? "" : "s", ccnt); + + return (0); +} diff --git a/usr.bin/vi/nex/ex_screen.c b/usr.bin/vi/nex/ex_screen.c new file mode 100644 index 000000000000..0fd000d4898a --- /dev/null +++ b/usr.bin/vi/nex/ex_screen.c @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_screen.c 8.10 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_split -- :s[plit] [file ...] + * Split the screen, optionally setting the file list. + */ +int +ex_split(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_split(sp, cmdp->argc ? cmdp->argv : NULL)); +} + +/* + * ex_bg -- :bg + * Hide the screen. + */ +int +ex_bg(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_bg(sp)); +} + +/* + * ex_fg -- :fg [file] + * Show the screen. + */ +int +ex_fg(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (sp->s_fg(sp, cmdp->argc ? cmdp->argv[0]->bp : NULL)); +} + +/* + * ex_resize -- :resize [change] + * Change the screen size. + */ +int +ex_resize(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(cmdp, E_COUNT)) + cmdp->count = 1; + return (sp->s_rabs(sp, cmdp->count)); +} + +/* + * ex_sdisplay -- + * Display the list of screens. + */ +int +ex_sdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + int cnt, col, len, sep; + + if ((tsp = sp->gp->hq.cqh_first) == (void *)&sp->gp->hq) { + (void)ex_printf(EXCOOKIE, + "No backgrounded screens to display.\n"); + return (0); + } + + col = len = sep = 0; + for (cnt = 1; tsp != (void *)&sp->gp->hq; tsp = tsp->q.cqe_next) { + col += len = strlen(FILENAME(tsp->frp)) + sep; + if (col >= sp->cols - 1) { + col = len; + sep = 0; + (void)ex_printf(EXCOOKIE, "\n"); + } else if (cnt != 1) { + sep = 1; + (void)ex_printf(EXCOOKIE, " "); + } + (void)ex_printf(EXCOOKIE, "%s", FILENAME(tsp->frp)); + ++cnt; + } + (void)ex_printf(EXCOOKIE, "\n"); + return (0); +} diff --git a/usr.bin/vi/nex/ex_script.c b/usr.bin/vi/nex/ex_script.c new file mode 100644 index 000000000000..606e479d6af4 --- /dev/null +++ b/usr.bin/vi/nex/ex_script.c @@ -0,0 +1,570 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_script.c 8.10 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "script.h" + +/* + * XXX + */ +int openpty __P((int *, int *, char *, struct termios *, struct winsize *)); + +static int sscr_getprompt __P((SCR *, EXF *)); +static int sscr_init __P((SCR *, EXF *)); +static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *)); +static int sscr_setprompt __P((SCR *, char *, size_t)); + +/* + * ex_script -- : sc[ript][!] [file] + * + * Switch to script mode. + */ +int +ex_script(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* Vi only command. */ + if (!IN_VI_MODE(sp)) { + msgq(sp, M_ERR, + "The script command is only available in vi mode."); + return (1); + } + + /* Switch to the new file. */ + if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp)) + return (1); + + /* + * Create the shell, figure out the prompt. + * + * !!! + * The files just switched, use sp->ep. + */ + if (sscr_init(sp, sp->ep)) + return (1); + + return (0); +} + +/* + * sscr_init -- + * Create a pty setup for a shell. + */ +static int +sscr_init(sp, ep) + SCR *sp; + EXF *ep; +{ + SCRIPT *sc; + char *sh, *sh_path; + + MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT)); + sp->script = sc; + sc->sh_prompt = NULL; + sc->sh_prompt_len = 0; + + /* + * There are two different processes running through this code. + * They are the shell and the parent. + */ + sc->sh_master = sc->sh_slave = -1; + + if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) { + msgq(sp, M_SYSERR, "tcgetattr"); + goto err; + } + + /* + * Turn off output postprocessing and echo. + */ + sc->sh_term.c_oflag &= ~OPOST; + sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK); + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) { + msgq(sp, M_SYSERR, "tcgetattr"); + goto err; + } + + if (openpty(&sc->sh_master, + &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) { + msgq(sp, M_SYSERR, "openpty"); + goto err; + } + + /* + * Don't use vfork() here, because the signal semantics + * differ from implementation to implementation. + */ + switch (sc->sh_pid = fork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "fork"); +err: if (sc->sh_master != -1) + (void)close(sc->sh_master); + if (sc->sh_slave != -1) + (void)close(sc->sh_slave); + return (1); + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + /* + * XXX + * So that shells that do command line editing turn it off. + */ + (void)putenv("TERM=emacs"); + (void)putenv("TERMCAP=emacs:"); + (void)putenv("EMACS=t"); + + (void)setsid(); +#ifdef TIOCSCTTY + /* + * 4.4BSD allocates a controlling terminal using the TIOCSCTTY + * ioctl, not by opening a terminal device file. POSIX 1003.1 + * doesn't define a portable way to do this. If TIOCSCTTY is + * not available, hope that the open does it. + */ + (void)ioctl(sc->sh_slave, TIOCSCTTY, 0); +#endif + (void)close(sc->sh_master); + (void)dup2(sc->sh_slave, STDIN_FILENO); + (void)dup2(sc->sh_slave, STDOUT_FILENO); + (void)dup2(sc->sh_slave, STDERR_FILENO); + (void)close(sc->sh_slave); + + /* Assumes that all shells have -i. */ + sh_path = O_STR(sp, O_SHELL); + if ((sh = strrchr(sh_path, '/')) == NULL) + sh = sh_path; + else + ++sh; + execl(sh_path, sh, "-i", NULL); + msgq(sp, M_ERR, + "Error: execl: %s: %s", sh_path, strerror(errno)); + _exit(127); + default: + break; + } + + if (sscr_getprompt(sp, ep)) + return (1); + + F_SET(sp, S_REDRAW | S_SCRIPT); + return (0); + +} + +/* + * sscr_getprompt -- + * Eat lines printed by the shell until a line with no trailing + * carriage return comes; set the prompt from that line. + */ +static int +sscr_getprompt(sp, ep) + SCR *sp; + EXF *ep; +{ + struct timeval tv; + SCRIPT *sc; + fd_set fdset; + recno_t lline; + size_t llen, len; + u_int value; + int nr; + char *endp, *p, *t, buf[1024]; + + FD_ZERO(&fdset); + endp = buf; + len = sizeof(buf); + + /* Wait up to a second for characters to read. */ + tv.tv_sec = 5; + tv.tv_usec = 0; + sc = sp->script; + FD_SET(sc->sh_master, &fdset); + switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "select"); + goto prompterr; + case 0: /* Timeout */ + msgq(sp, M_ERR, "Error: timed out."); + goto prompterr; + case 1: /* Characters to read. */ + break; + } + + /* Read the characters. */ +more: len = sizeof(buf) - (endp - buf); + switch (nr = read(sc->sh_master, endp, len)) { + case 0: /* EOF. */ + msgq(sp, M_ERR, "Error: shell: EOF"); + goto prompterr; + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "shell"); + goto prompterr; + default: + endp += nr; + break; + } + + /* If any complete lines, push them into the file. */ + for (p = t = buf; p < endp; ++p) { + value = term_key_val(sp, *p); + if (value == K_CR || value == K_NL) { + if (file_lline(sp, ep, &lline) || + file_aline(sp, ep, 0, lline, t, p - t)) + goto prompterr; + t = p + 1; + } + } + if (p > buf) { + memmove(buf, t, endp - t); + endp = buf + (endp - t); + } + if (endp == buf) + goto more; + + /* Wait up 1/10 of a second to make sure that we got it all. */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) { + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "select"); + goto prompterr; + case 0: /* Timeout */ + break; + case 1: /* Characters to read. */ + goto more; + } + + /* Timed out, so theoretically we have a prompt. */ + llen = endp - buf; + endp = buf; + + /* Append the line into the file. */ + if (file_lline(sp, ep, &lline) || + file_aline(sp, ep, 0, lline, buf, llen)) { +prompterr: sscr_end(sp); + return (1); + } + + return (sscr_setprompt(sp, buf, llen)); +} + +/* + * sscr_exec -- + * Take a line and hand it off to the shell. + */ +int +sscr_exec(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SCRIPT *sc; + recno_t last_lno; + size_t blen, len, last_len, tlen; + int matchprompt, nw, rval; + char *bp, *p; + + /* If there's a prompt on the last line, append the command. */ + if (file_lline(sp, ep, &last_lno)) + return (1); + if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) { + GETLINE_ERR(sp, last_lno); + return (1); + } + if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) { + matchprompt = 1; + GET_SPACE_RET(sp, bp, blen, last_len + 128); + memmove(bp, p, last_len); + } else + matchprompt = 0; + + /* Get something to execute. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + goto err1; + if (lno == 0) + goto empty; + else + GETLINE_ERR(sp, lno); + goto err1; + } + + /* Empty lines aren't interesting. */ + if (len == 0) + goto empty; + + /* Delete any prompt. */ + if (sscr_matchprompt(sp, p, len, &tlen)) { + if (tlen == len) { +empty: msgq(sp, M_BERR, "Nothing to execute."); + goto err1; + } + p += (len - tlen); + len = tlen; + } + + /* Push the line to the shell. */ + sc = sp->script; + if ((nw = write(sc->sh_master, p, len)) != len) + goto err2; + rval = 0; + if (write(sc->sh_master, "\n", 1) != 1) { +err2: if (nw == 0) + errno = EIO; + msgq(sp, M_SYSERR, "shell"); + goto err1; + } + + if (matchprompt) { + ADD_SPACE_RET(sp, bp, blen, last_len + len); + memmove(bp + last_len, p, len); + if (file_sline(sp, ep, last_lno, bp, last_len + len)) +err1: rval = 1; + } + if (matchprompt) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * sscr_input -- + * Take a line from the shell and insert it into the file. + */ +int +sscr_input(sp) + SCR *sp; +{ + struct timeval tv; + SCRIPT *sc; + EXF *ep; + recno_t lno; + size_t blen, len, tlen; + u_int value; + int nr, rval; + char *bp, *endp, *p, *t; + + /* Find out where the end of the file is. */ + ep = sp->ep; + if (file_lline(sp, ep, &lno)) + return (1); + +#define MINREAD 1024 + GET_SPACE_RET(sp, bp, blen, MINREAD); + endp = bp; + + /* Read the characters. */ + rval = 1; + sc = sp->script; +more: switch (nr = read(sc->sh_master, endp, MINREAD)) { + case 0: /* EOF; shell just exited. */ + sscr_end(sp); + F_CLR(sp, S_SCRIPT); + rval = 0; + goto ret; + case -1: /* Error or interrupt. */ + msgq(sp, M_SYSERR, "shell"); + goto ret; + default: + endp += nr; + break; + } + + /* Append the lines into the file. */ + for (p = t = bp; p < endp; ++p) { + value = term_key_val(sp, *p); + if (value == K_CR || value == K_NL) { + len = p - t; + if (file_aline(sp, ep, 1, lno++, t, len)) + goto ret; + t = p + 1; + } + } + if (p > t) { + len = p - t; + /* + * If the last thing from the shell isn't another prompt, wait + * up to 1/10 of a second for more stuff to show up, so that + * we don't break the output into two separate lines. Don't + * want to hang indefinitely because some program is hanging, + * confused the shell, or whatever. + */ + if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + FD_SET(sc->sh_master, &sp->rdfd); + FD_CLR(STDIN_FILENO, &sp->rdfd); + if (select(sc->sh_master + 1, + &sp->rdfd, NULL, NULL, &tv) == 1) { + memmove(bp, t, len); + endp = bp + len; + goto more; + } + } + if (sscr_setprompt(sp, t, len)) + return (1); + if (file_aline(sp, ep, 1, lno++, t, len)) + goto ret; + } + + /* The cursor moves to EOF. */ + sp->lno = lno; + sp->cno = len ? len - 1 : 0; + rval = sp->s_refresh(sp, ep); + +ret: FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * sscr_setprompt -- + * + * Set the prompt to the last line we got from the shell. + * + */ +static int +sscr_setprompt(sp, buf, len) + SCR *sp; + char* buf; + size_t len; +{ + SCRIPT *sc; + + sc = sp->script; + if (sc->sh_prompt) + FREE(sc->sh_prompt, sc->sh_prompt_len); + MALLOC(sp, sc->sh_prompt, char *, len + 1); + if (sc->sh_prompt == NULL) { + sscr_end(sp); + return (1); + } + memmove(sc->sh_prompt, buf, len); + sc->sh_prompt_len = len; + sc->sh_prompt[len] = '\0'; + return (0); +} + +/* + * sscr_matchprompt -- + * Check to see if a line matches the prompt. Nul's indicate + * parts that can change, in both content and size. + */ +static int +sscr_matchprompt(sp, lp, line_len, lenp) + SCR *sp; + char *lp; + size_t line_len, *lenp; +{ + SCRIPT *sc; + size_t prompt_len; + char *pp; + + sc = sp->script; + if (line_len < (prompt_len = sc->sh_prompt_len)) + return (0); + + for (pp = sc->sh_prompt; + prompt_len && line_len; --prompt_len, --line_len) { + if (*pp == '\0') { + for (; prompt_len && *pp == '\0'; --prompt_len, ++pp); + if (!prompt_len) + return (0); + for (; line_len && *lp != *pp; --line_len, ++lp); + if (!line_len) + return (0); + } + if (*pp++ != *lp++) + break; + } + + if (prompt_len) + return (0); + if (lenp != NULL) + *lenp = line_len; + return (1); +} + +/* + * sscr_end -- + * End the pipe to a shell. + */ +int +sscr_end(sp) + SCR *sp; +{ + SCRIPT *sc; + int rval; + + if ((sc = sp->script) == NULL) + return (0); + + /* Turn off the script flag. */ + F_CLR(sp, S_SCRIPT); + + /* Close down the parent's file descriptors. */ + if (sc->sh_master != -1) + (void)close(sc->sh_master); + if (sc->sh_slave != -1) + (void)close(sc->sh_slave); + + /* This should have killed the child. */ + rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0); + + /* Free memory. */ + FREE(sc->sh_prompt, sc->sh_prompt_len); + FREE(sc, sizeof(SCRIPT)); + sp->script = NULL; + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_set.c b/usr.bin/vi/nex/ex_set.c new file mode 100644 index 000000000000..a463524e5fa9 --- /dev/null +++ b/usr.bin/vi/nex/ex_set.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_set.c 8.2 (Berkeley) 10/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +int +ex_set(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + switch(cmdp->argc) { + case 0: + opts_dump(sp, CHANGED_DISPLAY); + break; + default: + opts_set(sp, cmdp->argv); + break; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_shell.c b/usr.bin/vi/nex/ex_shell.c new file mode 100644 index 000000000000..282cc8f4fbdc --- /dev/null +++ b/usr.bin/vi/nex/ex_shell.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_shell.c 8.17 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <curses.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "../svi/svi_screen.h" + +/* + * ex_shell -- :sh[ell] + * Invoke the program named in the SHELL environment variable + * with the argument -i. + */ +int +ex_shell(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + char buf[MAXPATHLEN]; + + (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)); + return (ex_exec_proc(sp, buf, "\n", NULL)); +} + +/* + * ex_exec_proc -- + * Run a separate process. + */ +int +ex_exec_proc(sp, cmd, p1, p2) + SCR *sp; + char *cmd, *p1, *p2; +{ + struct sigaction act, oact; + struct stat osb, sb; + struct termios term; + const char *name; + pid_t pid; + int isig, rval; + + /* Clear the rest of the screen. */ + if (sp->s_clear(sp)) + return (1); + + /* Save ex/vi terminal settings, and restore the original ones. */ + EX_LEAVE(sp, isig, act, oact, sb, osb, term); + + /* Put out various messages. */ + if (p1 != NULL) + (void)write(STDOUT_FILENO, p1, strlen(p1)); + if (p2 != NULL) + (void)write(STDOUT_FILENO, p2, strlen(p2)); + + + switch (pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); + rval = 1; + goto err; + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) + name = O_STR(sp, O_SHELL); + else + ++name; + execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL); + msgq(sp, M_ERR, "Error: execl: %s: %s", + O_STR(sp, O_SHELL), strerror(errno)); + _exit(127); + /* NOTREACHED */ + } + + rval = proc_wait(sp, (long)pid, cmd, 0); + + /* Restore ex/vi terminal settings. */ +err: EX_RETURN(sp, isig, act, oact, sb, osb, term); + + /* + * XXX + * EX_LEAVE/EX_RETURN only give us 1-second resolution on the tty + * changes. A fast '!' command, e.g. ":!pwd" can beat us to the + * refresh. When there's better resolution from the stat(2) timers, + * this can go away. + */ + F_SET(sp, S_REFRESH); + + return (rval); +} diff --git a/usr.bin/vi/nex/ex_shift.c b/usr.bin/vi/nex/ex_shift.c new file mode 100644 index 000000000000..8a0b858cbb6f --- /dev/null +++ b/usr.bin/vi/nex/ex_shift.c @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_shift.c 8.12 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +enum which {LEFT, RIGHT}; +static int shift __P((SCR *, EXF *, EXCMDARG *, enum which)); + +int +ex_shiftl(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (shift(sp, ep, cmdp, LEFT)); +} + +int +ex_shiftr(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (shift(sp, ep, cmdp, RIGHT)); +} + +static int +shift(sp, ep, cmdp, rl) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which rl; +{ + recno_t from, to; + size_t blen, len, newcol, newidx, oldcol, oldidx, sw; + int curset; + char *p, *bp, *tbp; + + if (O_VAL(sp, O_SHIFTWIDTH) == 0) { + msgq(sp, M_INFO, "shiftwidth option set to 0"); + return (0); + } + + /* + * The historic version of vi permitted the user to string any number + * of '>' or '<' characters together, resulting in an indent of the + * appropriate levels. There's a special hack in ex_cmd() so that + * cmdp->argv[0] points to the string of '>' or '<' characters. + * + * Q: What's the difference between the people adding features + * to vi and the Girl Scouts? + * A: The Girl Scouts have mint cookies and adult supervision. + */ + for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p) + sw += O_VAL(sp, O_SHIFTWIDTH); + + GET_SPACE_RET(sp, bp, blen, 256); + + curset = 0; + for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) { + if ((p = file_gline(sp, ep, from, &len)) == NULL) + goto err; + if (!len) { + if (sp->lno == from) + curset = 1; + continue; + } + + /* + * Calculate the old indent amount and the number of + * characters it used. + */ + for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx) + if (p[oldidx] == ' ') + ++oldcol; + else if (p[oldidx] == '\t') + oldcol += O_VAL(sp, O_TABSTOP) - + oldcol % O_VAL(sp, O_TABSTOP); + else + break; + + /* Calculate the new indent amount. */ + if (rl == RIGHT) + newcol = oldcol + sw; + else { + newcol = oldcol < sw ? 0 : oldcol - sw; + if (newcol == oldcol) { + if (sp->lno == from) + curset = 1; + continue; + } + } + + /* Get a buffer that will hold the new line. */ + ADD_SPACE_RET(sp, bp, blen, newcol + len); + + /* + * Build a new indent string and count the number of + * characters it uses. + */ + for (tbp = bp, newidx = 0; + newcol >= O_VAL(sp, O_TABSTOP); ++newidx) { + *tbp++ = '\t'; + newcol -= O_VAL(sp, O_TABSTOP); + } + for (; newcol > 0; --newcol, ++newidx) + *tbp++ = ' '; + + /* Add the original line. */ + memmove(tbp, p + oldidx, len - oldidx); + + /* Set the replacement line. */ + if (file_sline(sp, ep, from, bp, (tbp + (len - oldidx)) - bp)) { +err: FREE_SPACE(sp, bp, blen); + return (1); + } + + /* + * !!! + * The shift command in historic vi had the usual bizarre + * collection of cursor semantics. If called from vi, the + * cursor was repositioned to the first non-blank character + * of the lowest numbered line shifted. If called from ex, + * the cursor was repositioned to the first non-blank of the + * highest numbered line shifted. Here, if the cursor isn't + * part of the set of lines that are moved, move it to the + * first non-blank of the last line shifted. (This makes + * ":3>>" in vi work reasonably.) If the cursor is part of + * the shifted lines, it doesn't get moved at all. This + * permits shifting of marked areas, i.e. ">'a." shifts the + * marked area twice, something that couldn't be done with + * historic vi. + */ + if (sp->lno == from) { + curset = 1; + if (newidx > oldidx) + sp->cno += newidx - oldidx; + else if (sp->cno >= oldidx - newidx) + sp->cno -= oldidx - newidx; + } + } + if (!curset) { + sp->lno = to; + sp->cno = 0; + (void)nonblank(sp, ep, to, &sp->cno); + } + + FREE_SPACE(sp, bp, blen); + + sp->rptlines[rl == RIGHT ? L_RSHIFT : L_LSHIFT] += + cmdp->addr2.lno - cmdp->addr1.lno + 1; + return (0); +} diff --git a/usr.bin/vi/nex/ex_source.c b/usr.bin/vi/nex/ex_source.c new file mode 100644 index 000000000000..bf2a9f1b275e --- /dev/null +++ b/usr.bin/vi/nex/ex_source.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_source.c 8.3 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_source -- :source file + * Execute ex commands from a file. + */ +int +ex_source(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (ex_cfile(sp, ep, cmdp->argv[0]->bp)); +} diff --git a/usr.bin/vi/nex/ex_stop.c b/usr.bin/vi/nex/ex_stop.c new file mode 100644 index 000000000000..26ec067c622a --- /dev/null +++ b/usr.bin/vi/nex/ex_stop.c @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_stop.c 8.4 (Berkeley) 10/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex/sex_screen.h" + +/* + * ex_stop -- :stop[!] + * :suspend[!] + * Suspend execution. + */ +int +ex_stop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + /* For some strange reason, the force flag turns off autowrite. */ + if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) && + !F_ISSET(cmdp, E_FORCE)) { + if (file_write((sp), (ep), NULL, NULL, NULL, FS_ALL)) + return (1); + if (sex_refresh(sp, ep)) + return (1); + } + return (sp->s_suspend(sp)); +} diff --git a/usr.bin/vi/nex/ex_subst.c b/usr.bin/vi/nex/ex_subst.c new file mode 100644 index 000000000000..6d132c56dd24 --- /dev/null +++ b/usr.bin/vi/nex/ex_subst.c @@ -0,0 +1,984 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_substitute.c 8.33 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "interrupt.h" + +#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */ +#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */ + +static int checkmatchsize __P((SCR *, regex_t *)); +static inline int regsub __P((SCR *, + char *, char **, size_t *, size_t *)); +static void subst_intr __P((int)); +static int substitute __P((SCR *, EXF *, + EXCMDARG *, char *, regex_t *, u_int)); + +/* + * ex_substitute -- + * [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]] + * + * Substitute on lines matching a pattern. + */ +int +ex_substitute(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + regex_t *re, lre; + size_t blen, len; + u_int flags; + int delim, eval, reflags, replaced; + char *bp, *ptrn, *rep, *p, *t; + + /* + * Skip leading white space. + * + * !!! + * Historic vi allowed any non-alphanumeric to serve as the + * substitution command delimiter. + * + * !!! + * If the arguments are empty, it's the same as &, i.e. we + * repeat the last substitution. + */ + for (p = cmdp->argv[0]->bp, + len = cmdp->argv[0]->len; len > 0; --len, ++p) { + if (!isblank(*p)) + break; + } + if (len == 0) + return (ex_subagain(sp, ep, cmdp)); + delim = *p++; + if (isalnum(delim)) + return (substitute(sp, ep, + cmdp, p, &sp->subre, SUB_MUSTSETR)); + + /* + * Get the pattern string, toss escaped characters. + * + * !!! + * Historic vi accepted any of the following forms: + * + * :s/abc/def/ change "abc" to "def" + * :s/abc/def change "abc" to "def" + * :s/abc/ delete "abc" + * :s/abc delete "abc" + * + * QUOTING NOTE: + * + * Only toss an escape character if it escapes a delimiter. + * This means that "s/A/\\\\f" replaces "A" with "\\f". It + * would be nice to be more regular, i.e. for each layer of + * escaping a single escape character is removed, but that's + * not how the historic vi worked. + */ + for (ptrn = t = p;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + /* + * !!! + * Nul terminate the pattern string -- it's passed + * to regcomp which doesn't understand anything else. + */ + *t = '\0'; + break; + } + if (p[0] == '\\' && p[1] == delim) + ++p; + *t++ = *p++; + } + + /* If the pattern string is empty, use the last one. */ + if (*ptrn == NULL) { + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, + "No previous regular expression."); + return (1); + } + re = &sp->subre; + flags = 0; + } else { + /* Set RE flags. */ + reflags = 0; + if (O_ISSET(sp, O_EXTENDED)) + reflags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + reflags |= REG_ICASE; + + /* Convert vi-style RE's to POSIX 1003.2 RE's. */ + if (re_conv(sp, &ptrn, &replaced)) + return (1); + + /* Compile the RE. */ + eval = regcomp(&lre, (char *)ptrn, reflags); + + /* Free up any allocated memory. */ + if (replaced) + FREE_SPACE(sp, ptrn, 0); + + if (eval) { + re_error(sp, eval, &lre); + return (1); + } + + /* + * Set saved RE. + * + * !!! + * Historic practice is that substitutes set the search + * direction as well as both substitute and search RE's. + */ + sp->searchdir = FORWARD; + sp->sre = lre; + F_SET(sp, S_SRE_SET); + sp->subre = lre; + F_SET(sp, S_SUBRE_SET); + + re = &lre; + flags = SUB_FIRST; + } + + /* + * Get the replacement string. + * + * The special character ~ (\~ if O_MAGIC not set) inserts the + * previous replacement string into this replacement string. + * + * The special character & (\& if O_MAGIC not set) matches the + * entire RE. No handling of & is required here, it's done by + * regsub(). + * + * QUOTING NOTE: + * + * Only toss an escape character if it escapes a delimiter or + * an escape character, or if O_MAGIC is set and it escapes a + * tilde. + */ + if (*p == '\0') { + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + sp->repl = NULL; + sp->repl_len = 0; + } else { + /* + * Count ~'s to figure out how much space we need. We could + * special case nonexistent last patterns or whether or not + * O_MAGIC is set, but it's probably not worth the effort. + */ + for (rep = p, len = 0; + p[0] != '\0' && p[0] != delim; ++p, ++len) + if (p[0] == '~') + len += sp->repl_len; + GET_SPACE_RET(sp, bp, blen, len); + for (t = bp, len = 0, p = rep;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + break; + } + if (p[0] == '\\') { + if (p[1] == '\\' || p[1] == delim) + ++p; + else if (p[1] == '~') { + ++p; + if (!O_ISSET(sp, O_MAGIC)) + goto tilde; + } + } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) { +tilde: ++p; + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + len += sp->repl_len; + continue; + } + *t++ = *p++; + ++len; + } + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + if ((sp->repl = malloc(len)) == NULL) { + msgq(sp, M_SYSERR, NULL); + FREE_SPACE(sp, bp, blen); + return (1); + } + memmove(sp->repl, bp, len); + sp->repl_len = len; + FREE_SPACE(sp, bp, blen); + } + + if (checkmatchsize(sp, &sp->subre)) + return (1); + return (substitute(sp, ep, cmdp, p, re, flags)); +} + +/* + * ex_subagain -- + * [line [,line]] & [cgr] [count] [#lp]] + * + * Substitute using the last substitute RE and replacement pattern. + */ +int +ex_subagain(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0)); +} + +/* + * ex_subtilde -- + * [line [,line]] ~ [cgr] [count] [#lp]] + * + * Substitute using the last RE and last substitute replacement pattern. + */ +int +ex_subtilde(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous regular expression."); + return (1); + } + return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0)); +} + +/* + * The nasty part of the substitution is what happens when the replacement + * string contains newlines. It's a bit tricky -- consider the information + * that has to be retained for "s/f\(o\)o/^M\1^M\1/". The solution here is + * to build a set of newline offsets which we use to break the line up later, + * when the replacement is done. Don't change it unless you're pretty damned + * confident. + */ +#define NEEDNEWLINE(sp) { \ + if (sp->newl_len == sp->newl_cnt) { \ + sp->newl_len += 25; \ + REALLOC(sp, sp->newl, size_t *, \ + sp->newl_len * sizeof(size_t)); \ + if (sp->newl == NULL) { \ + sp->newl_len = 0; \ + return (1); \ + } \ + } \ +} + +#define BUILD(sp, l, len) { \ + if (lbclen + (len) > lblen) { \ + lblen += MAX(lbclen + (len), 256); \ + REALLOC(sp, lb, char *, lblen); \ + if (lb == NULL) { \ + lbclen = 0; \ + return (1); \ + } \ + } \ + memmove(lb + lbclen, l, len); \ + lbclen += len; \ +} + +#define NEEDSP(sp, len, pnt) { \ + if (lbclen + (len) > lblen) { \ + lblen += MAX(lbclen + (len), 256); \ + REALLOC(sp, lb, char *, lblen); \ + if (lb == NULL) { \ + lbclen = 0; \ + return (1); \ + } \ + pnt = lb + lbclen; \ + } \ +} + +/* + * substitute -- + * Do the substitution. This stuff is *really* tricky. There are + * lots of special cases, and general nastiness. Don't mess with it + * unless you're pretty confident. + */ +static int +substitute(sp, ep, cmdp, s, re, flags) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + char *s; + regex_t *re; + u_int flags; +{ + DECLARE_INTERRUPTS; + MARK from, to; + recno_t elno, lno; + size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset; + int didsub, do_eol_match, eflags, empty_ok, eval; + int linechanged, matched, quit, rval; + int cflag, gflag, lflag, nflag, pflag, rflag; + char *bp, *lb; + + /* + * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but + * it only displayed the last change. I'd disallow them, but they are + * useful in combination with the [v]global commands. In the current + * model the problem is combining them with the 'c' flag -- the screen + * would have to flip back and forth between the confirm screen and the + * ex print screen, which would be pretty awful. We do display all + * changes, though, for what that's worth. + * + * !!! + * Historic vi was fairly strict about the order of "options", the + * count, and "flags". I'm somewhat fuzzy on the difference between + * options and flags, anyway, so this is a simpler approach, and we + * just take it them in whatever order the user gives them. (The ex + * usage statement doesn't reflect this.) + */ + cflag = gflag = lflag = nflag = pflag = rflag = 0; + for (lno = OOBLNO; *s != '\0'; ++s) + switch (*s) { + case ' ': + case '\t': + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (lno != OOBLNO) + goto usage; + errno = 0; + lno = strtoul(s, &s, 10); + if (*s == '\0') /* Loop increment correction. */ + --s; + if (errno == ERANGE) { + if (lno == LONG_MAX) + msgq(sp, M_ERR, "Count overflow."); + else if (lno == LONG_MIN) + msgq(sp, M_ERR, "Count underflow."); + else + msgq(sp, M_SYSERR, NULL); + return (1); + } + /* + * In historic vi, the count was inclusive from the + * second address. + */ + cmdp->addr1.lno = cmdp->addr2.lno; + cmdp->addr2.lno += lno - 1; + break; + case '#': + nflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'g': + gflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + if (LF_ISSET(SUB_FIRST)) { + msgq(sp, M_ERR, + "Regular expression specified; r flag meaningless."); + return (1); + } + if (!F_ISSET(sp, S_SUBRE_SET)) { + msgq(sp, M_ERR, + "No previous regular expression."); + return (1); + } + rflag = 1; + break; + default: + goto usage; + } + + if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) { +usage: msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage); + return (1); + } + + if (IN_VI_MODE(sp) && cflag && (lflag || nflag || pflag)) { + msgq(sp, M_ERR, + "The #, l and p flags may not be combined with the c flag in vi mode."); + return (1); + } + + if (!F_ISSET(sp, S_GLOBAL)) + SET_UP_INTERRUPTS(subst_intr); + + /* + * bp: if interactive, line cache + * blen: if interactive, line cache length + * lb: build buffer pointer. + * lbclen: current length of built buffer. + * lblen; length of build buffer. + */ + bp = lb = NULL; + blen = lbclen = lblen = 0; + + /* For each line... */ + for (matched = quit = 0, lno = cmdp->addr1.lno, + elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) { + + /* Someone's unhappy, time to stop. */ + if (F_ISSET(sp, S_INTERRUPTED)) { + if (!F_ISSET(sp, S_GLOBAL)) + msgq(sp, M_INFO, "Interrupted."); + break; + } + + /* Get the line. */ + if ((s = file_gline(sp, ep, lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + + /* + * Make a local copy if doing confirmation -- when calling + * the confirm routine we're likely to lose the cached copy. + */ + if (cflag) { + if (bp == NULL) { + GET_SPACE_RET(sp, bp, blen, llen); + } else + ADD_SPACE_RET(sp, bp, blen, llen); + memmove(bp, s, llen); + s = bp; + } + + /* Start searching from the beginning. */ + offset = 0; + len = llen; + + /* Reset the build buffer offset. */ + lbclen = 0; + + /* Reset empty match flag. */ + empty_ok = 1; + + /* + * We don't want to have to do a setline if the line didn't + * change -- keep track of whether or not this line changed. + * If doing confirmations, don't want to keep setting the + * line if change is refused -- keep track of substitutions. + */ + didsub = linechanged = 0; + + /* New line, do an EOL match. */ + do_eol_match = 1; + + /* It's not nul terminated, but we pretend it is. */ + eflags = REG_STARTEND; + + /* + * The search area is from s + offset to the EOL. + * + * Generally, sp->match[0].rm_so is the offset of the start + * of the match from the start of the search, and offset is + * the offset of the start of the last search. + */ +nextmatch: sp->match[0].rm_so = 0; + sp->match[0].rm_eo = len; + + /* Get the next match. */ + eval = regexec(re, + (char *)s + offset, re->re_nsub + 1, sp->match, eflags); + + /* + * There wasn't a match or if there was an error, deal with + * it. If there was a previous match in this line, resolve + * the changes into the database. Otherwise, just move on. + */ + if (eval == REG_NOMATCH) + goto endmatch; + if (eval != 0) { + re_error(sp, eval, re); + goto ret1; + } + matched = 1; + + /* Only the first search can match an anchored expression. */ + eflags |= REG_NOTBOL; + + /* + * !!! + * It's possible to match 0-length strings -- for example, the + * command s;a*;X;, when matched against the string "aabb" will + * result in "XbXbX", i.e. the matches are "aa", the space + * between the b's and the space between the b's and the end of + * the string. There is a similar space between the beginning + * of the string and the a's. The rule that we use (because vi + * historically used it) is that any 0-length match, occurring + * immediately after a match, is ignored. Otherwise, the above + * example would have resulted in "XXbXbX". Another example is + * incorrectly using " *" to replace groups of spaces with one + * space. + * + * The way we do this is that if we just had a successful match, + * the starting offset does not skip characters, and the match + * is empty, ignore the match and move forward. If there's no + * more characters in the string, we were attempting to match + * after the last character, so quit. + */ + if (!empty_ok && + sp->match[0].rm_so == 0 && sp->match[0].rm_eo == 0) { + empty_ok = 1; + if (len == 0) + goto endmatch; + BUILD(sp, s + offset, 1) + ++offset; + --len; + goto nextmatch; + } + + /* Confirm change. */ + if (cflag) { + /* + * Set the cursor position for confirmation. Note, + * if we matched on a '$', the cursor may be past + * the end of line. + * + * XXX + * We may want to "fix" this in the confirm routine, + * if the confirm routine should be able to display + * a cursor past EOL. + */ + from.lno = to.lno = lno; + from.cno = sp->match[0].rm_so + offset; + to.cno = sp->match[0].rm_eo; + if (llen == 0) + from.cno = to.cno = 0; + else { + if (to.cno >= llen) + to.cno = llen - 1; + if (from.cno >= llen) + from.cno = llen - 1; + } + switch (sp->s_confirm(sp, ep, &from, &to)) { + case CONF_YES: + break; + case CONF_NO: + didsub = 0; + BUILD(sp, s +offset, sp->match[0].rm_eo); + goto skip; + case CONF_QUIT: + /* Set the quit flag. */ + quit = 1; + + /* If interruptible, pass the info back. */ + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); + + /* + * If any changes, resolve them, otherwise + * return to the main loop. + */ + goto endmatch; + } + } + + /* Copy the bytes before the match into the build buffer. */ + BUILD(sp, s + offset, sp->match[0].rm_so); + + /* + * Cursor moves to last line changed, unless doing confirm, + * in which case don't move it. + * + * !!! + * Historic vi just put the cursor on the first non-blank + * of the last line changed. This might be better. + */ + if (!cflag) { + sp->lno = lno; + sp->cno = sp->match[0].rm_so + offset; + } + + /* Substitute the matching bytes. */ + didsub = 1; + if (regsub(sp, s + offset, &lb, &lbclen, &lblen)) + goto ret1; + + /* Set the change flag so we know this line was modified. */ + linechanged = 1; + + /* Move past the matched bytes. */ +skip: offset += sp->match[0].rm_eo; + len -= sp->match[0].rm_eo; + + /* A match cannot be followed by an empty pattern. */ + empty_ok = 0; + + /* + * If doing a global change with confirmation, we have to + * update the screen. The basic idea is to store the line + * so the screen update routines can find it, and restart. + */ + if (didsub && cflag && gflag) { + /* + * The new search offset will be the end of the + * modified line. + */ + saved_offset = lbclen; + + /* Copy the rest of the line. */ + if (len) + BUILD(sp, s + offset, len) + + /* Set the new offset. */ + offset = saved_offset; + + /* Store inserted lines, adjusting the build buffer. */ + last = 0; + if (sp->newl_cnt) { + for (cnt = 0; + cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { + if (file_iline(sp, ep, lno, + lb + last, sp->newl[cnt] - last)) + goto ret1; + last = sp->newl[cnt] + 1; + ++sp->rptlines[L_ADDED]; + } + lbclen -= last; + offset -= last; + sp->newl_cnt = 0; + } + + /* Store and retrieve the line. */ + if (file_sline(sp, ep, lno, lb + last, lbclen)) + goto ret1; + if ((s = file_gline(sp, ep, lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + goto ret1; + } + ADD_SPACE_RET(sp, bp, blen, llen) + memmove(bp, s, llen); + s = bp; + len = llen - offset; + + /* Restart the build. */ + lbclen = 0; + BUILD(sp, s, offset); + + /* + * If we haven't already done the after-the-string + * match, do one. Set REG_NOTEOL so the '$' pattern + * only matches once. + */ + if (!do_eol_match) + goto endmatch; + if (offset == len) { + do_eol_match = 0; + eflags |= REG_NOTEOL; + } + goto nextmatch; + } + + /* + * If it's a global: + * + * If at the end of the string, do a test for the after + * the string match. Set REG_NOTEOL so the '$' pattern + * only matches once. + */ + if (gflag && do_eol_match) { + if (len == 0) { + do_eol_match = 0; + eflags |= REG_NOTEOL; + } + goto nextmatch; + } + +endmatch: if (!linechanged) + continue; + + /* Copy any remaining bytes into the build buffer. */ + if (len) + BUILD(sp, s + offset, len) + + /* Store inserted lines, adjusting the build buffer. */ + last = 0; + if (sp->newl_cnt) { + for (cnt = 0; + cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) { + if (file_iline(sp, ep, + lno, lb + last, sp->newl[cnt] - last)) + goto ret1; + last = sp->newl[cnt] + 1; + ++sp->rptlines[L_ADDED]; + } + lbclen -= last; + sp->newl_cnt = 0; + } + + /* Store the changed line. */ + if (file_sline(sp, ep, lno, lb + last, lbclen)) + goto ret1; + + /* Update changed line counter. */ + ++sp->rptlines[L_CHANGED]; + + /* Display as necessary. */ + if (lflag || nflag || pflag) { + from.lno = to.lno = lno; + from.cno = to.cno = 0; + if (lflag) + ex_print(sp, ep, &from, &to, E_F_LIST); + if (nflag) + ex_print(sp, ep, &from, &to, E_F_HASH); + if (pflag) + ex_print(sp, ep, &from, &to, E_F_PRINT); + } + } + + /* + * If not in a global command, and nothing matched, say so. + * Else, if none of the lines displayed, put something up. + */ + if (!matched) { + if (!F_ISSET(sp, S_GLOBAL)) + msgq(sp, M_INFO, "No match found."); + } else if (!lflag && !nflag && !pflag) + F_SET(EXP(sp), EX_AUTOPRINT); + + rval = 0; + if (0) { +ret1: rval = 1; + } + +interrupt_err: + if (!F_ISSET(sp, S_GLOBAL)) + TEAR_DOWN_INTERRUPTS; + + if (bp != NULL) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * regsub -- + * Do the substitution for a regular expression. + */ +static inline int +regsub(sp, ip, lbp, lbclenp, lblenp) + SCR *sp; + char *ip; /* Input line. */ + char **lbp; + size_t *lbclenp, *lblenp; +{ + enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv; + size_t lbclen, lblen; /* Local copies. */ + size_t mlen; /* Match length. */ + size_t rpl; /* Remaining replacement length. */ + char *rp; /* Replacement pointer. */ + int ch; + int no; /* Match replacement offset. */ + char *p, *t; /* Buffer pointers. */ + char *lb; /* Local copies. */ + + lb = *lbp; /* Get local copies. */ + lbclen = *lbclenp; + lblen = *lblenp; + + /* + * QUOTING NOTE: + * + * There are some special sequences that vi provides in the + * replacement patterns. + * & string the RE matched (\& if nomagic set) + * \# n-th regular subexpression + * \E end \U, \L conversion + * \e end \U, \L conversion + * \l convert the next character to lower-case + * \L convert to lower-case, until \E, \e, or end of replacement + * \u convert the next character to upper-case + * \U convert to upper-case, until \E, \e, or end of replacement + * + * Otherwise, since this is the lowest level of replacement, discard + * all escape characters. This (hopefully) follows historic practice. + */ +#define ADDCH(ch) { \ + CHAR_T __ch = (ch); \ + u_int __value = term_key_val(sp, __ch); \ + if (__value == K_CR || __value == K_NL) { \ + NEEDNEWLINE(sp); \ + sp->newl[sp->newl_cnt++] = lbclen; \ + } else if (conv != C_NOTSET) { \ + switch (conv) { \ + case C_ONELOWER: \ + conv = C_NOTSET; \ + /* FALLTHROUGH */ \ + case C_LOWER: \ + if (isupper(__ch)) \ + __ch = tolower(__ch); \ + break; \ + case C_ONEUPPER: \ + conv = C_NOTSET; \ + /* FALLTHROUGH */ \ + case C_UPPER: \ + if (islower(__ch)) \ + __ch = toupper(__ch); \ + break; \ + default: \ + abort(); \ + } \ + } \ + NEEDSP(sp, 1, p); \ + *p++ = __ch; \ + ++lbclen; \ +} + conv = C_NOTSET; + for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) { + switch (ch = *rp++) { + case '&': + if (O_ISSET(sp, O_MAGIC)) { + no = 0; + goto subzero; + } + break; + case '\\': + if (rpl == 0) + break; + --rpl; + switch (ch = *rp) { + case '&': + if (!O_ISSET(sp, O_MAGIC)) { + ++rp; + no = 0; + goto subzero; + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + no = *rp++ - '0'; +subzero: if (sp->match[no].rm_so == -1 || + sp->match[no].rm_eo == -1) + continue; + mlen = + sp->match[no].rm_eo - sp->match[no].rm_so; + for (t = ip + sp->match[no].rm_so; mlen--; ++t) + ADDCH(*t); + continue; + case 'e': + case 'E': + ++rp; + conv = C_NOTSET; + continue; + case 'l': + ++rp; + conv = C_ONELOWER; + continue; + case 'L': + ++rp; + conv = C_LOWER; + continue; + case 'u': + ++rp; + conv = C_ONEUPPER; + continue; + case 'U': + ++rp; + conv = C_UPPER; + continue; + default: + ++rp; + break; + } + } + ADDCH(ch); + } + + *lbp = lb; /* Update caller's information. */ + *lbclenp = lbclen; + *lblenp = lblen; + return (0); +} + +static int +checkmatchsize(sp, re) + SCR *sp; + regex_t *re; +{ + /* Build nsub array as necessary. */ + if (sp->matchsize < re->re_nsub + 1) { + sp->matchsize = re->re_nsub + 1; + REALLOC(sp, sp->match, + regmatch_t *, sp->matchsize * sizeof(regmatch_t)); + if (sp->match == NULL) { + sp->matchsize = 0; + return (1); + } + } + return (0); +} + +/* + * subst_intr -- + * Set the interrupt bit in any screen that is interruptible. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one substitute running, it will be hard to decide which one to + * stop. + */ +static void +subst_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} diff --git a/usr.bin/vi/nex/ex_tag.c b/usr.bin/vi/nex/ex_tag.c new file mode 100644 index 000000000000..c47ab0de9c44 --- /dev/null +++ b/usr.bin/vi/nex/ex_tag.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_tag.c 8.31 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "tag.h" + +static char *binary_search __P((char *, char *, char *)); +static int compare __P((char *, char *, char *)); +static char *linear_search __P((char *, char *, char *)); +static int search __P((SCR *, char *, char *, char **)); +static int tag_get __P((SCR *, char *, char **, char **, char **)); + +/* + * ex_tagfirst -- + * The tag code can be entered from main, i.e. "vi -t tag". + */ +int +ex_tagfirst(sp, tagarg) + SCR *sp; + char *tagarg; +{ + FREF *frp; + MARK m; + long tl; + u_int flags; + int sval; + char *p, *tag, *name, *search; + + /* Taglength may limit the number of characters. */ + if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl) + tagarg[tl] = '\0'; + + /* Get the tag information. */ + if (tag_get(sp, tagarg, &tag, &name, &search)) + return (1); + + /* Create the file entry. */ + if ((frp = file_add(sp, NULL, name, 0)) == NULL) + return (1); + if (file_init(sp, frp, NULL, 0)) + return (1); + + /* + * !!! + * Historic vi accepted a line number as well as a search + * string, and people are apparently still using the format. + */ + if (isdigit(search[0])) { + m.lno = atoi(search); + m.cno = 0; + } else { + /* + * Search for the tag; cheap fallback for C functions if + * the name is the same but the arguments have changed. + */ + m.lno = 1; + m.cno = 0; + flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; + sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); + if (sval && (p = strrchr(search, '(')) != NULL) { + p[1] = '\0'; + sval = f_search(sp, sp->ep, + &m, &m, search, NULL, &flags); + } + if (sval) + msgq(sp, M_ERR, "%s: search pattern not found.", tag); + } + + /* Set up the screen. */ + frp->lno = m.lno; + frp->cno = m.cno; + F_SET(frp, FR_CURSORSET); + + /* Might as well make this the default tag. */ + if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} + +/* + * ex_tagpush -- :tag [file] + * Move to a new tag. + * + * The tags stacks in nvi are a bit tricky. Each tag contains a file name, + * search string, and line/column numbers. The search string is only used + * for the first access and for user display. The first record on the stack + * is the place where we first did a tag, so it has no search string. The + * second record is the first tag, and so on. Note, this means that the + * "current" tag is always on the stack. Each tag has a line/column which is + * the location from which the user tagged the following TAG entry, and which + * is used as the return location. + */ +int +ex_tagpush(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + enum {TC_CHANGE, TC_CURRENT} which; + EX_PRIVATE *exp; + FREF *frp; + MARK m; + TAG *tp; + u_int flags; + int sval; + long tl; + char *name, *p, *search, *tag; + + exp = EXP(sp); + switch (cmdp->argc) { + case 1: + if (exp->tlast != NULL) + FREE(exp->tlast, strlen(exp->tlast) + 1); + if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + break; + case 0: + if (exp->tlast == NULL) { + msgq(sp, M_ERR, "No previous tag entered."); + return (1); + } + break; + default: + abort(); + } + + /* Taglength may limit the number of characters. */ + if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl) + exp->tlast[tl] = '\0'; + + /* Get the tag information. */ + if (tag_get(sp, exp->tlast, &tag, &name, &search)) + return (1); + + /* Get a new FREF structure. */ + if ((frp = file_add(sp, sp->frp, name, 1)) == NULL) { + FREE(tag, strlen(tag)); + return (1); + } + + /* + * Get a tag structure -- if this is the first tag, push it on the + * stack as a placeholder and get another tag structure. Set the + * line/column of the most recent element on the stack to be the + * current values, including the file pointer. Then push the new + * TAG onto the stack with the new file and search string for user + * display. + */ + CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); + if (tp != NULL && exp->tagq.tqh_first == NULL) { + TAILQ_INSERT_HEAD(&exp->tagq, tp, q); + CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); + } + if (exp->tagq.tqh_first != NULL) { + exp->tagq.tqh_first->frp = sp->frp; + exp->tagq.tqh_first->lno = sp->lno; + exp->tagq.tqh_first->cno = sp->cno; + } + if (tp != NULL) { + if ((tp->search = strdup(search)) == NULL) + msgq(sp, M_SYSERR, NULL); + else + tp->slen = strlen(search); + tp->frp = frp; + TAILQ_INSERT_HEAD(&exp->tagq, tp, q); + } + + /* Switch to the new file. */ + if (sp->frp == frp) + which = TC_CURRENT; + else { + MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, frp, NULL, 0)) { + if (tp != NULL) + FREE(tp, sizeof(TAG)); + FREE(tag, strlen(tag)); + return (1); + } + which = TC_CHANGE; + } + + /* + * !!! + * Historic vi accepted a line number as well as a search + * string, and people are apparently still using the format. + */ + if (isdigit(search[0])) { + m.lno = atoi(search); + m.cno = 0; + sval = 0; + } else { + /* + * Search for the tag; cheap fallback for C functions + * if the name is the same but the arguments have changed. + */ + m.lno = 1; + m.cno = 0; + flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; + sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); + if (sval && (p = strrchr(search, '(')) != NULL) { + p[1] = '\0'; + sval = f_search(sp, sp->ep, + &m, &m, search, NULL, &flags); + p[1] = '('; + } + if (sval) + msgq(sp, M_ERR, "%s: search pattern not found.", tag); + } + free(tag); + + switch (which) { + case TC_CHANGE: + frp->lno = m.lno; + frp->cno = m.cno; + F_SET(frp, FR_CURSORSET); + F_SET(sp, S_FSWITCH); + break; + case TC_CURRENT: + if (sval) + return (1); + sp->lno = m.lno; + sp->cno = m.cno; + break; + } + return (0); +} + +/* Free a tag or tagf structure from a queue. */ +#define FREETAG(tp) { \ + TAILQ_REMOVE(&exp->tagq, (tp), q); \ + if ((tp)->search != NULL) \ + free((tp)->search); \ + FREE((tp), sizeof(TAGF)); \ +} +#define FREETAGF(tfp) { \ + TAILQ_REMOVE(&exp->tagfq, (tfp), q); \ + free((tfp)->name); \ + FREE((tfp), sizeof(TAGF)); \ +} + +/* + * ex_tagpop -- :tagp[op][!] [number | file] + * Pop the tag stack. + */ +int +ex_tagpop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + EX_PRIVATE *exp; + TAG *ntp, *tp; + recno_t lno; + long off, saved_off; + size_t arglen, cno; + char *arg, *p, *t; + + /* Check for an empty stack. */ + exp = EXP(sp); + if (exp->tagq.tqh_first == NULL) { + msgq(sp, M_INFO, "The tags stack is empty."); + return (1); + } + + switch (cmdp->argc) { + case 0: /* Pop one tag. */ + tp = exp->tagq.tqh_first; + FREETAG(tp); + break; + case 1: /* Name or number. */ + arg = cmdp->argv[0]->bp; + saved_off = strtol(arg, &p, 10); + if (*p == '\0') { + if (saved_off < 1) + return (0); + for (tp = exp->tagq.tqh_first, off = saved_off; + tp != NULL && off-- > 1; tp = tp->q.tqe_next); + if (tp == NULL) { + msgq(sp, M_ERR, +"Less than %d entries on the tags stack; use :display to see the tags stack.", + saved_off); + return (1); + } + for (off = saved_off; off-- > 1;) { + tp = exp->tagq.tqh_first; + FREETAG(tp); + } + } else { + arglen = strlen(arg); + for (tp = exp->tagq.tqh_first; + tp != NULL; tp = tp->q.tqe_next) { + /* Use the user's original file name. */ + p = tp->frp->name; + if ((t = strrchr(p, '/')) == NULL) + t = p; + else + ++t; + if (!strncmp(arg, t, arglen)) { + ntp = tp; + break; + } + } + if (tp == NULL) { + msgq(sp, M_ERR, +"No file named %s on the tags stack; use :display to see the tags stack.", + arg); + return (1); + } + for (;;) { + tp = exp->tagq.tqh_first; + if (tp == ntp) + break; + FREETAG(tp); + } + } + break; + default: + abort(); + } + + /* Update the cursor from the saved TAG information. */ + tp = exp->tagq.tqh_first; + if (tp->frp == sp->frp) { + sp->lno = tp->lno; + sp->cno = tp->cno; + } else { + MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, tp->frp, NULL, 0)) + return (1); + + tp->frp->lno = tp->lno; + tp->frp->cno = tp->cno; + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_FSWITCH); + } + + /* If returning to the first tag, the stack is now empty. */ + if (tp->q.tqe_next == NULL) + FREETAG(tp); + return (0); +} + +/* + * ex_tagtop -- :tagt[op][!] + * Clear the tag stack. + */ +int +ex_tagtop(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + EX_PRIVATE *exp; + TAG *tp, tmp; + int found; + + /* Pop to oldest saved information. */ + exp = EXP(sp); + for (found = 0; (tp = exp->tagq.tqh_first) != NULL; found = 1) { + if (exp->tagq.tqh_first == NULL) + tmp = *tp; + FREETAG(tp); + } + + if (!found) { + msgq(sp, M_INFO, "The tags stack is empty."); + return (1); + } + + /* If not switching files, it's easy; else do the work. */ + if (tmp.frp == sp->frp) { + sp->lno = tmp.lno; + sp->cno = tmp.cno; + } else { + MODIFY_CHECK(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); + + if (file_init(sp, tmp.frp, NULL, 0)) + return (1); + + tmp.frp->lno = tmp.lno; + tmp.frp->cno = tmp.cno; + + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_FSWITCH); + } + return (0); +} + +/* + * ex_tagdisplay -- + * Display the list of tags. + */ +int +ex_tagdisplay(sp, ep) + SCR *sp; + EXF *ep; +{ + EX_PRIVATE *exp; + TAG *tp; + size_t len, maxlen; + int cnt; + char *name; + + exp = EXP(sp); + if ((tp = exp->tagq.tqh_first) == NULL) { + (void)ex_printf(EXCOOKIE, "No tags to display.\n"); + return (0); + } + + /* + * Figure out the formatting. MNOC is the maximum + * number of file name columns before we split the line. + */ +#define MNOC 15 + for (maxlen = 0, + tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) { + len = strlen(name = tp->frp->name); /* The original name. */ + if (maxlen < len && len < MNOC) + maxlen = len; + } + + for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL; + ++cnt, tp = tp->q.tqe_next) { + len = strlen(name = tp->frp->name); /* The original name. */ + if (len > maxlen || len + tp->slen > sp->cols) + if (tp == NULL || tp->search == NULL) + (void)ex_printf(EXCOOKIE, + "%2d %s\n", cnt, name); + else + (void)ex_printf(EXCOOKIE, + "%2d %s\n** %*.*s %s\n", cnt, name, + (int)maxlen, (int)maxlen, "", tp->search); + else + if (tp == NULL || tp->search == NULL) + (void)ex_printf(EXCOOKIE, "%2d %*.*s\n", + cnt, (int)maxlen, (int)len, name); + else + (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n", + cnt, (int)maxlen, (int)len, name, + tp->search); + } + return (0); +} + +/* + * ex_tagalloc -- + * Create a new list of tag files. + */ +int +ex_tagalloc(sp, str) + SCR *sp; + char *str; +{ + EX_PRIVATE *exp; + TAGF *tp; + size_t len; + char *p, *t; + + /* Free current queue. */ + exp = EXP(sp); + while ((tp = exp->tagfq.tqh_first) != NULL) + FREETAGF(tp); + + /* Create new queue. */ + for (p = t = str;; ++p) { + if (*p == '\0' || isblank(*p)) { + if ((len = p - t) > 1) { + MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF)); + MALLOC(sp, tp->name, char *, len + 1); + if (tp->name == NULL) { + FREE(tp, sizeof(TAGF)); + return (1); + } + memmove(tp->name, t, len); + tp->name[len] = '\0'; + tp->flags = 0; + TAILQ_INSERT_TAIL(&exp->tagfq, tp, q); + } + t = p + 1; + } + if (*p == '\0') + break; + } + return (0); +} + /* Free previous queue. */ +/* + * ex_tagfree -- + * Free the tags file list. + */ +int +ex_tagfree(sp) + SCR *sp; +{ + EX_PRIVATE *exp; + TAG *tp; + TAGF *tfp; + + /* Free up tag information. */ + exp = EXP(sp); + while ((tp = exp->tagq.tqh_first) != NULL) + FREETAG(tp); + while ((tfp = exp->tagfq.tqh_first) != NULL) + FREETAGF(tfp); + FREE(exp->tlast, strlen(exp->tlast) + 1); + return (0); +} + +/* + * ex_tagcopy -- + * Copy a screen's tag structures. + */ +int +ex_tagcopy(orig, sp) + SCR *orig, *sp; +{ + EX_PRIVATE *oexp, *nexp; + TAG *ap, *tp; + TAGF *atfp, *tfp; + + /* Copy tag stack. */ + oexp = EXP(orig); + nexp = EXP(sp); + for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) { + MALLOC(sp, tp, TAG *, sizeof(TAG)); + if (tp == NULL) + goto nomem; + *tp = *ap; + if (ap->search != NULL && + (tp->search = strdup(ap->search)) == NULL) + goto nomem; + TAILQ_INSERT_TAIL(&nexp->tagq, tp, q); + } + + /* Copy list of tag files. */ + for (atfp = oexp->tagfq.tqh_first; + atfp != NULL; atfp = atfp->q.tqe_next) { + MALLOC(sp, tfp, TAGF *, sizeof(TAGF)); + if (tfp == NULL) + goto nomem; + *tfp = *atfp; + if ((tfp->name = strdup(atfp->name)) == NULL) + goto nomem; + TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q); + } + + /* Copy the last tag. */ + if (oexp->tlast != NULL && + (nexp->tlast = strdup(oexp->tlast)) == NULL) { +nomem: msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} + +/* + * tag_get -- + * Get a tag from the tags files. + */ +static int +tag_get(sp, tag, tagp, filep, searchp) + SCR *sp; + char *tag, **tagp, **filep, **searchp; +{ + EX_PRIVATE *exp; + TAGF *tfp; + int dne; + char *p; + + /* + * Find the tag, only display missing file messages once, and + * then only if we didn't find the tag. + */ + dne = 0; + exp = EXP(sp); + for (p = NULL, tfp = exp->tagfq.tqh_first; + tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) { + errno = 0; + F_CLR(tfp, TAGF_DNE); + if (search(sp, tfp->name, tag, &p)) + if (errno == ENOENT) { + if (!F_ISSET(tfp, TAGF_DNE_WARN)) { + dne = 1; + F_SET(tfp, TAGF_DNE); + } + } else + msgq(sp, M_SYSERR, tfp->name); + } + + if (p == NULL) { + msgq(sp, M_ERR, "%s: tag not found.", tag); + if (dne) + for (tfp = exp->tagfq.tqh_first; + tfp != NULL; tfp = tfp->q.tqe_next) + if (F_ISSET(tfp, TAGF_DNE)) { + errno = ENOENT; + msgq(sp, M_SYSERR, tfp->name); + F_SET(tfp, TAGF_DNE_WARN); + } + return (1); + } + + /* + * Set the return pointers; tagp points to the tag, and, incidentally + * the allocated string, filep points to the nul-terminated file name, + * searchp points to the nul-terminated search string. + */ + for (*tagp = p; *p && !isblank(*p); ++p); + if (*p == '\0') + goto malformed; + for (*p++ = '\0'; isblank(*p); ++p); + for (*filep = p; *p && !isblank(*p); ++p); + if (*p == '\0') + goto malformed; + for (*p++ = '\0'; isblank(*p); ++p); + *searchp = p; + if (*p == '\0') { +malformed: free(*tagp); + msgq(sp, M_ERR, "%s: corrupted tag in %s.", tag, tfp->name); + return (1); + } + return (0); +} + +#define EQUAL 0 +#define GREATER 1 +#define LESS (-1) + +/* + * search -- + * Search a file for a tag. + */ +static int +search(sp, name, tname, tag) + SCR *sp; + char *name, *tname, **tag; +{ + struct stat sb; + int fd, len; + char *endp, *back, *front, *map, *p; + + if ((fd = open(name, O_RDONLY, 0)) < 0) + return (1); + + /* + * XXX + * We'd like to test if the file is too big to mmap. Since we don't + * know what size or type off_t's or size_t's are, what the largest + * unsigned integral type is, or what random insanity the local C + * compiler will perpetrate, doing the comparison in a portable way + * is flatly impossible. Hope that malloc fails if the file is too + * large. + */ + if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size, + PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) { + (void)close(fd); + return (1); + } + front = map; + back = front + sb.st_size; + + front = binary_search(tname, front, back); + front = linear_search(tname, front, back); + + if (front == NULL || (endp = strchr(front, '\n')) == NULL) { + *tag = NULL; + goto done; + } + + len = endp - front; + MALLOC(sp, p, char *, len + 1); + if (p == NULL) { + *tag = NULL; + goto done; + } + memmove(p, front, len); + p[len] = '\0'; + *tag = p; + +done: if (munmap(map, (size_t)sb.st_size)) + msgq(sp, M_SYSERR, "munmap"); + if (close(fd)) + msgq(sp, M_SYSERR, "close"); + return (0); +} + +/* + * Binary search for "string" in memory between "front" and "back". + * + * This routine is expected to return a pointer to the start of a line at + * *or before* the first word matching "string". Relaxing the constraint + * this way simplifies the algorithm. + * + * Invariants: + * front points to the beginning of a line at or before the first + * matching string. + * + * back points to the beginning of a line at or after the first + * matching line. + * + * Base of the Invariants. + * front = NULL; + * back = EOF; + * + * Advancing the Invariants: + * + * p = first newline after halfway point from front to back. + * + * If the string at "p" is not greater than the string to match, + * p is the new front. Otherwise it is the new back. + * + * Termination: + * + * The definition of the routine allows it return at any point, + * since front is always at or before the line to print. + * + * In fact, it returns when the chosen "p" equals "back". This + * implies that there exists a string is least half as long as + * (back - front), which in turn implies that a linear search will + * be no more expensive than the cost of simply printing a string or two. + * + * Trying to continue with binary search at this point would be + * more trouble than it's worth. + */ +#define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n'); + +static char * +binary_search(string, front, back) + register char *string, *front, *back; +{ + register char *p; + + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + + while (p != back) { + if (compare(string, p, back) == GREATER) + front = p; + else + back = p; + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + } + return (front); +} + +/* + * Find the first line that starts with string, linearly searching from front + * to back. + * + * Return NULL for no such line. + * + * This routine assumes: + * + * o front points at the first character in a line. + * o front is before or at the first line to be printed. + */ +static char * +linear_search(string, front, back) + char *string, *front, *back; +{ + while (front < back) { + switch (compare(string, front, back)) { + case EQUAL: /* Found it. */ + return (front); + break; + case LESS: /* No such string. */ + return (NULL); + break; + case GREATER: /* Keep going. */ + break; + } + SKIP_PAST_NEWLINE(front, back); + } + return (NULL); +} + +/* + * Return LESS, GREATER, or EQUAL depending on how the string1 compares + * with string2 (s1 ??? s2). + * + * o Matches up to len(s1) are EQUAL. + * o Matches up to len(s2) are GREATER. + * + * The string "s1" is null terminated. The string s2 is '\t', space, (or + * "back") terminated. + * + * !!! + * Reasonably modern ctags programs use tabs as separators, not spaces. + * However, historic programs did use spaces, and, I got complaints. + */ +static int +compare(s1, s2, back) + register char *s1, *s2, *back; +{ + for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) + if (*s1 != *s2) + return (*s1 < *s2 ? LESS : GREATER); + return (*s1 ? GREATER : s2 < back && + (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL); +} diff --git a/usr.bin/vi/nex/ex_undo.c b/usr.bin/vi/nex/ex_undo.c new file mode 100644 index 000000000000..c8dedd8d401a --- /dev/null +++ b/usr.bin/vi/nex/ex_undo.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_undo.c 8.4 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_undol -- U + * Undo changes to this line. + */ +int +ex_undol(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + if (log_setline(sp, ep)) + return (1); + + sp->cno = 0; + return (0); +} + +/* + * ex_undo -- u + * Undo the last change. + */ +int +ex_undo(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + MARK m; + + /* + * !!! + * Multiple undo isn't available in ex, as there's no '.' command. + * Whether 'u' is undo or redo is toggled each time, unless there + * was a change since the last undo, in which case it's an undo. + */ + if (!F_ISSET(ep, F_UNDO)) { + F_SET(ep, F_UNDO); + ep->lundo = FORWARD; + } + switch (ep->lundo) { + case BACKWARD: + if (log_forward(sp, ep, &m)) + return (1); + ep->lundo = FORWARD; + break; + case FORWARD: + if (log_backward(sp, ep, &m)) + return (1); + ep->lundo = BACKWARD; + break; + case NOTSET: + abort(); + } + sp->lno = m.lno; + sp->cno = m.cno; + return (0); +} diff --git a/usr.bin/vi/nex/ex_usage.c b/usr.bin/vi/nex/ex_usage.c new file mode 100644 index 000000000000..da84995d2993 --- /dev/null +++ b/usr.bin/vi/nex/ex_usage.c @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_usage.c 8.11 (Berkeley) 12/17/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * ex_help -- :help + * Display help message. + */ +int +ex_help(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + (void)ex_printf(EXCOOKIE, + "To see the list of vi commands, enter \":viusage<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "To see the list of ex commands, enter \":exusage<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n"); + (void)ex_printf(EXCOOKIE, + "For a vi key usage statement enter \":viusage [key]<CR>\"\n"); + (void)ex_printf(EXCOOKIE, "To exit, enter \":q!\"\n"); + return (0); +} + +/* + * ex_usage -- :exusage [cmd] + * Display ex usage strings. + */ +int +ex_usage(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + ARGS *ap; + EXCMDLIST const *cp; + + switch (cmdp->argc) { + case 1: + ap = cmdp->argv[0]; + for (cp = cmds; cp->name != NULL && + memcmp(ap->bp, cp->name, ap->len); ++cp); + if (cp->name == NULL) + (void)ex_printf(EXCOOKIE, + "The %.*s command is unknown.", + (int)ap->len, ap->bp); + else { + (void)ex_printf(EXCOOKIE, + "Command: %s\n Usage: %s\n", cp->help, cp->usage); + /* + * !!! + * The "visual" command has two modes, one from ex, + * one from the vi colon line. Don't ask. + */ + if (cp != &cmds[C_VISUAL_EX] && + cp != &cmds[C_VISUAL_VI]) + break; + if (cp == &cmds[C_VISUAL_EX]) + cp = &cmds[C_VISUAL_VI]; + else + cp = &cmds[C_VISUAL_EX]; + (void)ex_printf(EXCOOKIE, + "Command: %s\n Usage: %s\n", cp->help, cp->usage); + } + break; + case 0: + for (cp = cmds; cp->name != NULL; ++cp) + (void)ex_printf(EXCOOKIE, + "%*s: %s\n", MAXCMDNAMELEN, cp->name, cp->help); + break; + default: + abort(); + } + return (0); +} + +/* + * ex_viusage -- :viusage [key] + * Display vi usage strings. + */ +int +ex_viusage(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + VIKEYS const *kp; + int key; + + switch (cmdp->argc) { + case 1: + key = cmdp->argv[0]->bp[0]; + if (key > MAXVIKEY) + goto nokey; + + /* Special case: '[' and ']' commands. */ + if ((key == '[' || key == ']') && cmdp->argv[0]->bp[1] != key) + goto nokey; + + kp = &vikeys[key]; + if (kp->func == NULL) +nokey: (void)ex_printf(EXCOOKIE, + "The %s key has no current meaning", + charname(sp, key)); + else + (void)ex_printf(EXCOOKIE, + " Key:%s%s\nUsage: %s\n", + isblank(*kp->help) ? "" : " ", kp->help, kp->usage); + break; + case 0: + for (key = 0; key <= MAXVIKEY; ++key) { + kp = &vikeys[key]; + if (kp->help != NULL) + (void)ex_printf(EXCOOKIE, "%s\n", kp->help); + } + break; + default: + abort(); + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_util.c b/usr.bin/vi/nex/ex_util.c new file mode 100644 index 000000000000..fa7e70b9dfcc --- /dev/null +++ b/usr.bin/vi/nex/ex_util.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_util.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_getline -- + * Return a line from the terminal. + */ +int +ex_getline(sp, fp, lenp) + SCR *sp; + FILE *fp; + size_t *lenp; +{ + EX_PRIVATE *exp; + size_t off; + int ch; + char *p; + + exp = EXP(sp); + for (off = 0, p = exp->ibp;; ++off) { + ch = getc(fp); + if (off >= exp->ibp_len) { + BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1); + p = exp->ibp + off; + } + if (ch == EOF || ch == '\n') { + if (ch == EOF && !off) + return (1); + *lenp = off; + return (0); + } + *p++ = ch; + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/nex/ex_version.c b/usr.bin/vi/nex/ex_version.c new file mode 100644 index 000000000000..230632d2de3c --- /dev/null +++ b/usr.bin/vi/nex/ex_version.c @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_version.c 8.32 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_version -- :version + * Display the program version. + */ +int +ex_version(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + static time_t then = 759365451; + + (void)ex_printf(EXCOOKIE, +"Version 1.03, %sThe CSRG, University of California, Berkeley.\n", + ctime(&then)); + return (0); +} diff --git a/usr.bin/vi/nex/ex_visual.c b/usr.bin/vi/nex/ex_visual.c new file mode 100644 index 000000000000..d53152bde8e8 --- /dev/null +++ b/usr.bin/vi/nex/ex_visual.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_visual.c 8.7 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags] + * + * Switch to visual mode. + */ +int +ex_visual(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + size_t len; + int pos; + char buf[256]; + + /* If open option off, disallow visual command. */ + if (!O_ISSET(sp, O_OPEN)) { + msgq(sp, M_ERR, + "The visual command requires that the open option be set."); + return (1); + } + + /* If a line specified, move to that line. */ + if (cmdp->addrcnt) + sp->lno = cmdp->addr1.lno; + + /* + * Push a command based on the line position flags. If no + * flag specified, the line goes at the top of the screen. + */ + switch (F_ISSET(cmdp, E_F_CARAT | E_F_DASH | E_F_DOT | E_F_PLUS)) { + case E_F_CARAT: + pos = '^'; + break; + case E_F_DASH: + pos = '-'; + break; + case E_F_DOT: + pos = '.'; + break; + case E_F_PLUS: + default: + pos = '+'; + break; + } + + if (F_ISSET(cmdp, E_COUNT)) + len = snprintf(buf, sizeof(buf), + "%luz%c%lu", sp->lno, pos, cmdp->count); + else + len = snprintf(buf, sizeof(buf), "%luz%c", sp->lno, pos); + (void)term_push(sp, buf, len, 0, CH_NOMAP | CH_QUOTED); + + /* + * !!! + * Historically, if no line address was specified, the [p#l] flags + * caused the cursor to be moved to the last line of the file, which + * was then positioned as described above. This seems useless, so + * I haven't implemented it. + */ + switch (F_ISSET(cmdp, E_F_HASH | E_F_LIST | E_F_PRINT)) { + case E_F_HASH: + O_SET(sp, O_NUMBER); + break; + case E_F_LIST: + O_SET(sp, O_LIST); + break; + case E_F_PRINT: + break; + } + + /* Switch modes. */ + F_CLR(sp, S_SCREENS); + F_SET(sp, sp->saved_vi_mode); + + return (0); +} diff --git a/usr.bin/vi/nex/ex_write.c b/usr.bin/vi/nex/ex_write.c new file mode 100644 index 000000000000..5a51b6dff914 --- /dev/null +++ b/usr.bin/vi/nex/ex_write.c @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_write.c 8.19 (Berkeley) 12/18/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" + +enum which {WQ, WRITE, XIT}; + +static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which)); + +/* + * ex_wq -- :wq[!] [>>] [file] + * Write to a file. + */ +int +ex_wq(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + if (exwr(sp, ep, cmdp, WQ)) + return (1); + + force = F_ISSET(cmdp, E_FORCE); + if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) { + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} + +/* + * ex_write -- :write[!] [>>] [file] + * :write [!] [cmd] + * Write to a file. + */ +int +ex_write(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (exwr(sp, ep, cmdp, WRITE)); +} + + +/* + * ex_xit -- :x[it]! [file] + * + * Write out any modifications and quit. + */ +int +ex_xit(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + int force; + + if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT)) + return (1); + + force = F_ISSET(cmdp, E_FORCE); + if (!force && ep->refcnt <= 1 && file_unedited(sp) != NULL) { + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, force ? S_EXIT_FORCE : S_EXIT); + return (0); +} + +/* + * exwr -- + * The guts of the ex write commands. + */ +static int +exwr(sp, ep, cmdp, cmd) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; + enum which cmd; +{ + EX_PRIVATE *exp; + MARK rm; + int flags; + char *name, *p; + + /* All write commands can have an associated '!'. */ + LF_INIT(FS_POSSIBLE); + if (F_ISSET(cmdp, E_FORCE)) + LF_SET(FS_FORCE); + + /* Skip any leading whitespace. */ + if (cmdp->argc != 0) + for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p); + + /* If no arguments, just write the file back. */ + if (cmdp->argc == 0 || *p == '\0') { + if (F_ISSET(cmdp, E_ADDR2_ALL)) + LF_SET(FS_ALL); + return (file_write(sp, ep, + &cmdp->addr1, &cmdp->addr2, NULL, flags)); + } + + /* If "write !" it's a pipe to a utility. */ + exp = EXP(sp); + if (cmd == WRITE && *p == '!') { + for (++p; *p && isblank(*p); ++p); + if (*p == '\0') { + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + /* Expand the argument. */ + if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0)) + return (1); + if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2, + &rm, cmdp->argv[1]->bp, FILTER_WRITE)) + return (1); + sp->lno = rm.lno; + return (0); + } + + /* If "write >>" it's an append to a file. */ + if (cmd != XIT && p[0] == '>' && p[1] == '>') { + LF_SET(FS_APPEND); + + /* Skip ">>" and whitespace. */ + for (p += 2; *p && isblank(*p); ++p); + } + + /* Build an argv so we get an argument count and file expansion. */ + if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0)) + return (1); + + switch (cmdp->argc) { + case 1: + /* + * Nothing to expand, write the current file. + * XXX + * Should never happen, already checked this case. + */ + name = NULL; + break; + case 2: + /* One new argument, write it. */ + name = cmdp->argv[exp->argsoff - 1]->bp; + set_alt_name(sp, name); + break; + default: + /* If expanded to more than one argument, object. */ + msgq(sp, M_ERR, "%s expanded into too many file names", + cmdp->argv[0]->bp); + msgq(sp, M_ERR, "Usage: %s.", cmdp->cmd->usage); + return (1); + } + + if (F_ISSET(cmdp, E_ADDR2_ALL)) + LF_SET(FS_ALL); + return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags)); +} + +/* + * ex_writefp -- + * Write a range of lines to a FILE *. + */ +int +ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch) + SCR *sp; + EXF *ep; + char *name; + FILE *fp; + MARK *fm, *tm; + u_long *nlno, *nch; +{ + register u_long ccnt, fline, tline; + size_t len; + char *p; + + fline = fm->lno; + tline = tm->lno; + + if (nlno != NULL) { + *nch = 0; + *nlno = 0; + } + ccnt = 0; + + /* + * The vi filter code has multiple processes running simultaneously, + * and one of them calls ex_writefp(). The "unsafe" function calls + * in this code are to file_gline() and msgq(). File_gline() is safe, + * see the comment in filter.c:filtercmd() for details. We don't call + * msgq if the multiple process bit in the EXF is set. + * + * !!! + * Historic vi permitted files of 0 length to be written. However, + * since the way vi got around dealing with "empty" files was to + * always have a line in the file no matter what, it wrote them as + * files of a single, empty line. We write empty files. + * + * "Alex, I'll take vi trivia for $1000." + */ + if (tline != 0) + for (; fline <= tline; ++fline) { + if ((p = file_gline(sp, ep, fline, &len)) == NULL) + break; + if (fwrite(p, 1, len, fp) != len) { + msgq(sp, M_SYSERR, name); + (void)fclose(fp); + return (1); + } + ccnt += len; + if (putc('\n', fp) != '\n') + break; + ++ccnt; + } + if (fclose(fp)) { + if (!F_ISSET(ep, F_MULTILOCK)) + msgq(sp, M_SYSERR, name); + return (1); + } + if (nlno != NULL) { + *nch = ccnt; + *nlno = tm->lno == 0 ? 0 : tm->lno - fm->lno + 1; + } + return (0); +} diff --git a/usr.bin/vi/nex/ex_yank.c b/usr.bin/vi/nex/ex_yank.c new file mode 100644 index 000000000000..0897034bf022 --- /dev/null +++ b/usr.bin/vi/nex/ex_yank.c @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_yank.c 8.3 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_yank -- :[line [,line]] ya[nk] [buffer] [count] + * + * Yank the lines into a buffer. + */ +int +ex_yank(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + return (cut(sp, ep, NULL, + F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL, + &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE)); +} diff --git a/usr.bin/vi/nex/ex_z.c b/usr.bin/vi/nex/ex_z.c new file mode 100644 index 000000000000..2842a22855a5 --- /dev/null +++ b/usr.bin/vi/nex/ex_z.c @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ex_z.c 8.4 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" + +/* + * ex_z -- :[line] z [^-.+=] [count] [flags] + * + * Adjust window. + */ +int +ex_z(sp, ep, cmdp) + SCR *sp; + EXF *ep; + EXCMDARG *cmdp; +{ + recno_t cnt, equals, lno; + int eofcheck; + + /* + * !!! + * If no count specified, use either two times the size of the + * scrolling region, or the size of the window option. POSIX + * 1003.2 claims that the latter is correct, but historic ex/vi + * documentation and practice appear to use the scrolling region. + * I'm using the window size as it means that the entire screen + * is used instead of losing a line to roundoff. Note, we drop + * a line from the cnt if using the window size to leave room for + * the next ex prompt. + */ + if (F_ISSET(cmdp, E_COUNT)) + cnt = cmdp->count; + else +#ifdef HISTORIC_PRACTICE + cnt = O_VAL(sp, O_SCROLL) * 2; +#else + cnt = O_VAL(sp, O_WINDOW) - 1; +#endif + + equals = 0; + eofcheck = 0; + lno = cmdp->addr1.lno; + + switch (F_ISSET(cmdp, + E_F_CARAT | E_F_DASH | E_F_DOT | E_F_EQUAL | E_F_PLUS)) { + case E_F_CARAT: /* Display cnt * 2 before the line. */ + eofcheck = 1; + if (lno > cnt * 2) + cmdp->addr1.lno = (lno - cnt * 2) + 1; + else + cmdp->addr1.lno = 1; + cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1; + break; + case E_F_DASH: /* Line goes at the bottom of the screen. */ + cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1; + cmdp->addr2.lno = lno; + break; + case E_F_DOT: /* Line goes in the middle of the screen. */ + /* + * !!! + * Historically, the "middleness" of the line overrode the + * count, so that "3z.19" or "3z.20" would display the first + * 12 lines of the file, i.e. (N - 1) / 2 lines before and + * after the specified line. + */ + eofcheck = 1; + cnt = (cnt - 1) / 2; + cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; + cmdp->addr2.lno = lno + cnt; + break; + case E_F_EQUAL: /* Center with hyphens. */ + /* + * !!! + * Strangeness. The '=' flag is like the '.' flag (see the + * above comment, it applies here as well) but with a special + * little hack. Print out lines of hyphens before and after + * the specified line. Additionally, the cursor remains set + * on that line. + */ + eofcheck = 1; + cnt = (cnt - 1) / 2; + cmdp->addr1.lno = lno > cnt ? lno - cnt : 1; + cmdp->addr2.lno = lno - 1; + if (ex_pr(sp, ep, cmdp)) + return (1); + (void)ex_printf(EXCOOKIE, + "%s", "----------------------------------------\n"); + cmdp->addr2.lno = cmdp->addr1.lno = equals = lno; + if (ex_pr(sp, ep, cmdp)) + return (1); + (void)ex_printf(EXCOOKIE, + "%s", "----------------------------------------\n"); + cmdp->addr1.lno = lno + 1; + cmdp->addr2.lno = (lno + cnt) - 1; + break; + default: + /* If no line specified, move to the next one. */ + if (F_ISSET(cmdp, E_ADDRDEF)) + ++lno; + /* FALLTHROUGH */ + case E_F_PLUS: /* Line goes at the top of the screen. */ + eofcheck = 1; + cmdp->addr1.lno = lno; + cmdp->addr2.lno = (lno + cnt) - 1; + break; + } + + if (eofcheck) { + if (file_lline(sp, ep, &lno)) + return (1); + if (cmdp->addr2.lno > lno) + cmdp->addr2.lno = lno; + } + + if (ex_pr(sp, ep, cmdp)) + return (1); + if (equals) + sp->lno = equals; + return (0); +} diff --git a/usr.bin/vi/nex/excmd.c b/usr.bin/vi/nex/excmd.c new file mode 100644 index 000000000000..27ebc39eebe8 --- /dev/null +++ b/usr.bin/vi/nex/excmd.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)excmd.c 8.36 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" + +/* + * This array maps ex command names to command functions. + * + * The order in which command names are listed below is important -- + * ambiguous abbreviations are resolved to be the first possible match, + * e.g. "r" means "read", not "rewind", because "read" is listed before + * "rewind". + * + * The syntax of the ex commands is unbelievably irregular, and a special + * case from beginning to end. Each command has an associated "syntax + * script" which describes the "arguments" that are possible. The script + * syntax is as follows: + * + * ! -- ! flag + * 1 -- flags: [+-]*[pl#][+-]* + * 2 -- flags: [-.+^] + * 3 -- flags: [-.+^=] + * b -- buffer + * c[01+a] -- count (0-N, 1-N, signed 1-N, address offset) + * f[N#][or] -- file (a number or N, optional or required) + * l -- line + * S -- string with file name expansion + * s -- string + * W -- word string + * w[N#][or] -- word (a number or N, optional or required) + */ +EXCMDLIST const cmds[] = { +/* C_BANG */ + {"!", ex_bang, E_ADDR2_NONE|E_NORC, + "S", + "[line [,line]] ! command", + "filter lines through commands or run commands"}, +/* C_HASH */ + {"#", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] # [count] [l]", + "display numbered lines"}, +/* C_SUBAGAIN */ + {"&", ex_subagain, E_ADDR2|E_NORC, + "s", + "[line [,line]] & [cgr] [count] [#lp]", + "repeat the last subsitution"}, +/* C_STAR */ + {"*", ex_at, 0, + "b", + "* [buffer]", + "execute a buffer"}, +/* C_SHIFTL */ + {"<", ex_shiftl, E_ADDR2|E_AUTOPRINT|E_NORC, + "ca1", + "[line [,line]] <[<...] [count] [flags]", + "shift lines left"}, +/* C_EQUAL */ + {"=", ex_equal, E_ADDR1|E_NORC, + "1", + "[line] = [flags]", + "display line number"}, +/* C_SHIFTR */ + {">", ex_shiftr, E_ADDR2|E_AUTOPRINT|E_NORC, + "ca1", + "[line [,line]] >[>...] [count] [flags]", + "shift lines right"}, +/* C_AT */ + {"@", ex_at, 0, + "b", + "@ [buffer]", + "execute a buffer"}, +/* C_APPEND */ + {"append", ex_append, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF, + "!", + "[line] a[ppend][!]", + "append input to a line"}, +/* C_ABBR */ + {"abbreviate", ex_abbr, E_NOGLOBAL, + "W", + "ab[brev] word replace", + "specify an input abbreviation"}, +/* C_ARGS */ + {"args", ex_args, E_NOGLOBAL|E_NORC, + "", + "ar[gs]", + "display file argument list"}, +/* C_BG */ + {"bg", ex_bg, E_NOGLOBAL|E_NORC, + "", + "bg", + "background the current screen"}, +/* C_CHANGE */ + {"change", ex_change, E_ADDR2|E_NORC|E_ZERODEF, + "!ca", + "[line [,line]] c[hange][!] [count]", + "change lines to input"}, +/* C_CD */ + {"cd", ex_cd, E_NOGLOBAL, + "!f1o", + "cd[!] [directory]", + "change the current directory"}, +/* C_CHDIR */ + {"chdir", ex_cd, E_NOGLOBAL, + "!f1o", + "chd[ir][!] [directory]", + "change the current directory"}, +/* C_COPY */ + {"copy", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC, + "l1", + "[line [,line]] co[py] line [flags]", + "copy lines elsewhere in the file"}, +/* C_DELETE */ + {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC, + "bca1", + "[line [,line]] d[elete] [buffer] [count] [flags]", + "delete lines from the file"}, +/* C_DISPLAY */ + {"display", ex_display, E_NOGLOBAL|E_NORC, + "w1r", + "display b[uffers] | s[creens] | t[ags]", + "display buffers, screens or tags"}, +/* C_DIGRAPH */ + {"digraph", ex_digraph, E_NOGLOBAL|E_NOPERM|E_NORC, + "", + "digraph", + "specify digraphs (not implemented)"}, +/* C_EDIT */ + {"edit", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "e[dit][!] [+cmd] [file]", + "begin editing another file"}, +/* C_EX */ + {"ex", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "ex[!] [+cmd] [file]", + "begin editing another file"}, +/* C_EXUSAGE */ + {"exusage", ex_usage, E_NOGLOBAL|E_NORC, + "w1o", + "[exu]sage [command]", + "display ex command usage statement"}, +/* C_FILE */ + {"file", ex_file, E_NOGLOBAL|E_NORC, + "f1o", + "f[ile] [name]", + "display (and optionally set) file name"}, +/* C_FG */ + {"fg", ex_fg, E_NOGLOBAL|E_NORC, + "f1o", + "fg [file]", + "switch the current screen and a backgrounded screen"}, +/* C_GLOBAL */ + {"global", ex_global, E_ADDR2_ALL|E_NOGLOBAL|E_NORC, + "!s", + "[line [,line]] g[lobal][!] [;/]pattern[;/] [commands]", + "execute a global command on lines matching a pattern"}, +/* C_HELP */ + {"help", ex_help, E_NOGLOBAL|E_NORC, + "", + "he[lp]", + "display help statement"}, +/* C_INSERT */ + {"insert", ex_insert, E_ADDR1|E_NORC, + "!", + "[line] i[nsert][!]", + "insert input before a line"}, +/* C_JOIN */ + {"join", ex_join, E_ADDR2|E_AUTOPRINT|E_NORC, + "!ca1", + "[line [,line]] j[oin][!] [count] [flags]", + "join lines into a single line"}, +/* C_K */ + {"k", ex_mark, E_ADDR1|E_NORC, + "w1r", + "[line] k key", + "mark a line position"}, +/* C_LIST */ + {"list", ex_list, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] l[ist] [count] [#]", + "display lines in an unambiguous form"}, +/* C_MOVE */ + {"move", ex_move, E_ADDR2|E_AUTOPRINT|E_NORC, + "l", + "[line [,line]] m[ove] line", + "move lines elsewhere in the file"}, +/* C_MARK */ + {"mark", ex_mark, E_ADDR1|E_NORC, + "w1r", + "[line] ma[rk] key", + "mark a line position"}, +/* C_MAP */ + {"map", ex_map, 0, + "!W", + "map[!] [keys replace]", + "map input or commands to one or more keys"}, +/* C_MKEXRC */ + {"mkexrc", ex_mkexrc, E_NOGLOBAL|E_NORC, + "!f1r", + "mkexrc[!] file", + "write a .exrc file"}, +/* C_NEXT */ + {"next", ex_next, E_NOGLOBAL|E_NORC, + "!fN", + "n[ext][!] [file ...]", + "edit (and optionally specify) the next file"}, +/* C_NUMBER */ + {"number", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] nu[mber] [count] [l]", + "change display to number lines"}, +/* C_OPEN */ + {"open", ex_open, E_ADDR1, + "s", + "[line] o[pen] [/pattern/] [flags]", + "enter \"open\" mode (not implemented)"}, +/* C_PRINT */ + {"print", ex_pr, E_ADDR2|E_F_PRCLEAR|E_NORC|E_SETLAST, + "ca1", + "[line [,line]] p[rint] [count] [#l]", + "display lines"}, +/* C_PRESERVE */ + {"preserve", ex_preserve, E_NOGLOBAL|E_NORC, + "", + "pre[serve]", + "preserve an edit session for recovery"}, +/* C_PREVIOUS */ + {"previous", ex_prev, E_NOGLOBAL|E_NORC, + "!", + "prev[ious][!]", + "edit the previous file in the file argument list"}, +/* C_PUT */ + {"put", ex_put, E_ADDR1|E_AUTOPRINT|E_NORC|E_ZERO, + "b", + "[line] pu[t] [buffer]", + "append a cut buffer to the line"}, +/* C_QUIT */ + {"quit", ex_quit, E_NOGLOBAL, + "!", + "q[uit][!]", + "exit ex/vi"}, +/* C_READ */ + {"read", ex_read, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF, + "!s", + "[line] r[ead] [!cmd | [file]]", + "append input from a command or file to the line"}, +/* C_RESIZE */ + {"resize", ex_resize, E_NOGLOBAL|E_NORC, + "c+", + "resize [change]", + "grow or shrink the current screen"}, +/* C_REWIND */ + {"rewind", ex_rew, E_NOGLOBAL|E_NORC, + "!", + "rew[ind][!]", + "re-edit all the files in the file argument list"}, +/* C_SUBSTITUTE */ + {"substitute", ex_substitute, E_ADDR2|E_NORC, + "s", +"[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]", + "substitute on lines matching a pattern"}, +/* C_SCRIPT */ + {"script", ex_script, E_NOGLOBAL|E_NORC, + "!f1o", + "sc[ript][!] [file]", + "run a shell in a screen"}, +/* C_SET */ + {"set", ex_set, E_NOGLOBAL, + "wN", + "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]", + "set options (use \":set all\" to see all options)"}, +/* C_SHELL */ + {"shell", ex_shell, E_NOGLOBAL|E_NORC, + "", + "sh[ell]", + "suspend editing and run a shell"}, +/* C_SOURCE */ + {"source", ex_source, E_NOGLOBAL, + "f1r", + "so[urce] file", + "read a file of ex commands"}, +/* C_SPLIT */ + {"split", ex_split, E_NOGLOBAL|E_NORC, + "fNo", + "sp[lit] [file ...]", + "split the current screen into two screens"}, +/* C_STOP */ + {"stop", ex_stop, E_NOGLOBAL|E_NORC, + "!", + "st[op][!]", + "suspend the edit session"}, +/* C_SUSPEND */ + {"suspend", ex_stop, E_NOGLOBAL|E_NORC, + "!", + "su[spend][!]", + "suspend the edit session"}, +/* C_T */ + {"t", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC, + "l1", + "[line [,line]] t line [flags]", + "move lines elsewhere in the file"}, +/* C_TAG */ + {"tag", ex_tagpush, E_NOGLOBAL, + "!w1o", + "ta[g][!] [string]", + "edit the file containing the tag"}, +/* C_TAGPOP */ + {"tagpop", ex_tagpop, E_NOGLOBAL|E_NORC, + "!w1o", + "tagp[op][!] [number | file]", + "return to a previous tag"}, +/* C_TAGTOP */ + {"tagtop", ex_tagtop, E_NOGLOBAL|E_NORC, + "!", + "tagt[op][!]", + "return to the first tag"}, +/* C_UNDOL */ + {"Undo", ex_undol, E_AUTOPRINT|E_NOGLOBAL|E_NORC, + "", + "U[ndo]", + "undo all the changes to this line"}, +/* C_UNDO */ + {"undo", ex_undo, E_AUTOPRINT|E_NOGLOBAL|E_NORC, + "", + "u[ndo]", + "undo the most recent change"}, +/* C_UNABBREVIATE */ + {"unabbreviate",ex_unabbr, E_NOGLOBAL, + "w1r", + "una[bbrev] word", + "delete an abbreviation"}, +/* C_UNMAP */ + {"unmap", ex_unmap, E_NOGLOBAL, + "!w1r", + "unm[ap][!] word", + "delete an input or command map"}, +/* C_VGLOBAL */ + {"vglobal", ex_vglobal, E_ADDR2_ALL|E_NOGLOBAL|E_NORC, + "s", + "[line [,line]] v[global] [;/]pattern[;/] [commands]", + "execute a global command on lines NOT matching a pattern"}, +/* C_VERSION */ + {"version", ex_version, E_NOGLOBAL|E_NORC, + "", + "version", + "display the program version information"}, +/* C_VISUAL_EX */ + {"visual", ex_visual, E_ADDR1|E_NOGLOBAL|E_NORC|E_ZERODEF, + "2c11", + "[line] vi[sual] [-|.|+|^] [window_size] [flags]", + "enter visual (vi) mode from ex mode"}, +/* C_VISUAL_VI */ + {"visual", ex_edit, E_NOGLOBAL|E_NORC, + "!f1o", + "vi[sual][!] [+cmd] [file]", + "edit another file (from vi mode only)"}, +/* C_VIUSAGE */ + {"viusage", ex_viusage, E_NOGLOBAL|E_NORC, + "w1o", + "[viu]sage [key]", + "display vi key usage statement"}, +/* C_WRITE */ + {"write", ex_write, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!s", + "[line [,line]] w[rite][!] [!cmd | [>>] [file]]", + "write the file"}, +/* C_WQ */ + {"wq", ex_wq, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!s", + "[line [,line]] wq[!] [>>] [file]", + "write the file and exit"}, +/* C_XIT */ + {"xit", ex_xit, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF, + "!f1o", + "[line [,line]] x[it][!] [file]", + "exit"}, +/* C_YANK */ + {"yank", ex_yank, E_ADDR2|E_NORC, + "bca", + "[line [,line]] ya[nk] [buffer] [count]", + "copy lines to a cut buffer"}, +/* C_Z */ + {"z", ex_z, E_ADDR1|E_NOGLOBAL|E_NORC, + "3c01", + "[line] z [-|.|+|^|=] [count] [flags]", + "display different screens of the file"}, +/* C_SUBTILDE */ + {"~", ex_subtilde, E_ADDR2|E_NORC, + "s", + "[line [,line]] ~ [cgr] [count] [#lp]", + "replace previous RE with previous replacement string,"}, + {NULL}, +}; diff --git a/usr.bin/vi/nex/excmd.h.stub b/usr.bin/vi/nex/excmd.h.stub new file mode 100644 index 000000000000..ce2715c8b086 --- /dev/null +++ b/usr.bin/vi/nex/excmd.h.stub @@ -0,0 +1,340 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)excmd.h.stub 8.44 (Berkeley) 12/29/93 + */ + +/* Ex command structure. */ +typedef struct _excmdlist { + char *name; /* Command name. */ + /* Underlying function. */ + int (*fn) __P((SCR *, EXF *, EXCMDARG *)); + +#define E_ADDR1 0x0000001 /* One address. */ +#define E_ADDR2 0x0000002 /* Two address. */ +#define E_ADDR2_ALL 0x0000004 /* Zero/two addresses; zero == all. */ +#define E_ADDR2_NONE 0x0000008 /* Zero/two addresses; zero == none. */ +#define E_ADDRDEF 0x0000010 /* Default addresses used. */ +#define E_AUTOPRINT 0x0000020 /* Command always sets autoprint. */ +#define E_BUFFER 0x0000040 /* Buffer name supplied. */ +#define E_COUNT 0x0000080 /* Count supplied. */ +#define E_FORCE 0x0000100 /* ! */ + +#define E_F_CARAT 0x0000200 /* ^ flag. */ +#define E_F_DASH 0x0000400 /* - flag. */ +#define E_F_DOT 0x0000800 /* . flag. */ +#define E_F_EQUAL 0x0001000 /* = flag. */ +#define E_F_HASH 0x0002000 /* # flag. */ +#define E_F_LIST 0x0004000 /* l flag. */ +#define E_F_PLUS 0x0008000 /* + flag. */ +#define E_F_PRINT 0x0010000 /* p flag. */ + +#define E_F_PRCLEAR 0x0020000 /* Clear the print (#, l, p) flags. */ +#define E_MODIFY 0x0040000 /* File name expansion modified arg. */ +#define E_NOGLOBAL 0x0080000 /* Not in a global. */ +#define E_NOPERM 0x0100000 /* Permission denied for now. */ +#define E_NORC 0x0200000 /* Not from a .exrc or EXINIT. */ +#define E_SETLAST 0x0400000 /* Reset last command. */ +#define E_ZERO 0x0800000 /* 0 is a legal addr1. */ +#define E_ZERODEF 0x1000000 /* 0 is default addr1 of empty files. */ + u_long flags; + char *syntax; /* Syntax script. */ + char *usage; /* Usage line. */ + char *help; /* Help line. */ +} EXCMDLIST; +#define MAXCMDNAMELEN 12 /* Longest command name. */ +extern EXCMDLIST const cmds[]; /* List of ex commands. */ + +/* Structure passed around to functions implementing ex commands. */ +struct _excmdarg { + EXCMDLIST const *cmd; /* Command entry in command table. */ + CHAR_T buffer; /* Named buffer. */ + recno_t lineno; /* Line number. */ + long count; /* Signed, specified count. */ + int addrcnt; /* Number of addresses (0, 1 or 2). */ + MARK addr1; /* 1st address. */ + MARK addr2; /* 2nd address. */ + ARGS **argv; /* Array of arguments. */ + int argc; /* Count of arguments. */ + u_int flags; /* Selected flags from EXCMDLIST. */ +}; + +/* Global ranges. */ +typedef struct _range RANGE; +struct _range { + CIRCLEQ_ENTRY(_range) q; /* Linked list of ranges. */ + recno_t start, stop; /* Start/stop of the range. */ +}; + +/* Ex private, per-screen memory. */ +typedef struct _ex_private { + ARGS **args; /* Arguments. */ + int argscnt; /* Argument count. */ + int argsoff; /* Offset into arguments. */ + + CHAR_T at_lbuf; /* Last executed at buffer's name. */ + int at_lbuf_set; /* If at_lbuf is set. */ + + char *ibp; /* Line input buffer. */ + size_t ibp_len; /* Line input buffer length. */ + + EXCMDLIST const *lastcmd; /* Last command. */ + + CHAR_T *lastbcomm; /* Last bang command. */ + + TAILQ_HEAD(_tagh, _tag) tagq; /* Tag stack. */ + TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag stack. */ + char *tlast; /* Saved last tag. */ + + /* Linked list of ranges. */ + CIRCLEQ_HEAD(_rangeh, _range) rangeq; + recno_t range_lno; /* Range set line number. */ + +#define EX_AUTOPRINT 0x01 /* Autoprint flag. */ + u_int flags; +} EX_PRIVATE; +#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private)) + +/* Macro to set up a command structure. */ +#define SETCMDARG(s, cmd_id, naddr, lno1, lno2, force, arg) { \ + ARGS *__ap[2], __a; \ + memset(&s, 0, sizeof(EXCMDARG)); \ + s.cmd = &cmds[cmd_id]; \ + s.addrcnt = (naddr); \ + s.addr1.lno = (lno1); \ + s.addr2.lno = (lno2); \ + s.addr1.cno = s.addr2.cno = 1; \ + if (force) \ + s.flags |= E_FORCE; \ + if ((__a.bp = arg) == NULL) { \ + s.argc = 0; \ + __a.len = 0; \ + } else { \ + s.argc = 1; \ + __a.len = strlen(arg); \ + } \ + __ap[0] = &__a; \ + __ap[1] = NULL; \ + s.argv = __ap; \ +} + +/* + * :next, :prev, :rewind, :tag, :tagpush, :tagpop modifications check. + * If force is set, the autowrite is skipped. + */ +#define MODIFY_CHECK(sp, ep, force) { \ + if (F_ISSET((ep), F_MODIFIED)) \ + if (O_ISSET((sp), O_AUTOWRITE)) { \ + if (!(force) && \ + file_write((sp), (ep), NULL, NULL, NULL, \ + FS_ALL | FS_POSSIBLE)) \ + return (1); \ + } else if (ep->refcnt <= 1 && !(force)) { \ + msgq(sp, M_ERR, \ + "Modified since last write; write or use ! to override."); \ + return (1); \ + } \ +} + +/* + * Macros to set and restore the terminal values, and note if the screen + * was modified. Specific to their uses in ex/filter.c and ex/ex_shell.c. + * + * The old terminal values almost certainly turn on VINTR, VQUIT and VSUSP. + * We don't want to interrupt the parent(s), so we ignore VINTR. VQUIT is + * ignored by main() because nvi never wants to catch it. A VSUSP handler + * have been installed by the screen code. + */ +#define EX_LEAVE(sp, isig, act, oact, sb, osb, term) \ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { \ + (act).sa_handler = SIG_IGN; \ + sigemptyset(&(act).sa_mask); \ + (act).sa_flags = 0; \ + if ((isig) = !sigaction(SIGINT, &(act), &(oact))) { \ + if (tcgetattr(STDIN_FILENO, &(term))) { \ + msgq(sp, M_SYSERR, "tcgetattr"); \ + rval = 1; \ + goto err; \ + } \ + if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, \ + &sp->gp->original_termios)) { \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + rval = 1; \ + goto err; \ + } \ + } \ + /* \ + * The process may write to the terminal. Save the \ + * access time (read) and modification time (write) \ + * of the tty; if they have changed when we restore \ + * the modes, will have to refresh the screen. \ + */ \ + sb.st_mtime = 1; \ + osb.st_mtime = 0; \ + (void)fstat(STDIN_FILENO, &osb); \ + } + +#define EX_RETURN(sp, isig, act, oact, sb, osb, term) \ + if (F_ISSET(sp->gp, G_ISFROMTTY) && (isig)) { \ + if (sigaction(SIGINT, &(oact), NULL)) { \ + msgq(sp, M_SYSERR, "signal"); \ + rval = 1; \ + } \ + if (tcsetattr(STDIN_FILENO, \ + TCSANOW | TCSASOFT, &(term))) { \ + msgq(sp, M_SYSERR, "tcsetattr"); \ + rval = 1; \ + } \ + /* If the terminal was used, refresh the screen. */ \ + (void)fstat(STDIN_FILENO, &(sb)); \ + if ((sb).st_mtime != (osb).st_mtime || \ + (sb).st_atime != (osb).st_atime) \ + F_SET(sp, S_REFRESH); \ + } + +/* + * Filter actions: + * + * FILTER Filter text through the utility. + * FILTER_READ Read from the utility into the file. + * FILTER_WRITE Write to the utility, display its output. + */ +enum filtertype { FILTER, FILTER_READ, FILTER_WRITE }; +int filtercmd __P((SCR *, EXF *, + MARK *, MARK *, MARK *, char *, enum filtertype)); + +/* Argument expansion routines. */ +int argv_init __P((SCR *, EXF *, EXCMDARG *)); +int argv_exp0 __P((SCR *, EXF *, EXCMDARG *, char *, size_t)); +int argv_exp1 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int)); +int argv_exp2 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int)); +int argv_exp3 __P((SCR *, EXF *, EXCMDARG *, char *, size_t)); +int argv_free __P((SCR *)); + +/* Ex function prototypes. */ +int ex __P((SCR *, EXF *)); +int ex_cfile __P((SCR *, EXF *, char *)); +int ex_cmd __P((SCR *, EXF *, char *, size_t)); +int ex_end __P((SCR *)); +int ex_exec_proc __P((SCR *, char *, char *, char *)); +int ex_gb __P((SCR *, EXF *, TEXTH *, int, u_int)); +int ex_getline __P((SCR *, FILE *, size_t *)); +int ex_icmd __P((SCR *, EXF *, char *, size_t)); +int ex_init __P((SCR *, EXF *)); +int ex_is_abbrev __P((char *, size_t)); +int ex_optchange __P((SCR *, int)); +int ex_print __P((SCR *, EXF *, MARK *, MARK *, int)); +int ex_readfp __P((SCR *, EXF *, char *, FILE *, MARK *, recno_t *, int)); +void ex_refresh __P((SCR *, EXF *)); +int ex_screen_copy __P((SCR *, SCR *)); +int ex_screen_end __P((SCR *)); +int ex_sdisplay __P((SCR *, EXF *)); +int ex_suspend __P((SCR *)); +int ex_tdisplay __P((SCR *, EXF *)); +int ex_writefp __P((SCR *, EXF *, + char *, FILE *, MARK *, MARK *, u_long *, u_long *)); +void global_insdel __P((SCR *, EXF *, enum operation, recno_t)); +int proc_wait __P((SCR *, long, const char *, int)); +int sscr_end __P((SCR *)); +int sscr_exec __P((SCR *, EXF *, recno_t)); +int sscr_input __P((SCR *)); + +#define EXPROTO(type, name) \ + type name __P((SCR *, EXF *, EXCMDARG *)) + +EXPROTO(int, ex_abbr); +EXPROTO(int, ex_append); +EXPROTO(int, ex_args); +EXPROTO(int, ex_at); +EXPROTO(int, ex_bang); +EXPROTO(int, ex_bg); +EXPROTO(int, ex_cd); +EXPROTO(int, ex_change); +EXPROTO(int, ex_color); +EXPROTO(int, ex_copy); +EXPROTO(int, ex_debug); +EXPROTO(int, ex_delete); +EXPROTO(int, ex_digraph); +EXPROTO(int, ex_display); +EXPROTO(int, ex_edit); +EXPROTO(int, ex_equal); +EXPROTO(int, ex_fg); +EXPROTO(int, ex_file); +EXPROTO(int, ex_global); +EXPROTO(int, ex_help); +EXPROTO(int, ex_insert); +EXPROTO(int, ex_join); +EXPROTO(int, ex_list); +EXPROTO(int, ex_map); +EXPROTO(int, ex_mark); +EXPROTO(int, ex_mkexrc); +EXPROTO(int, ex_move); +EXPROTO(int, ex_next); +EXPROTO(int, ex_number); +EXPROTO(int, ex_open); +EXPROTO(int, ex_pr); +EXPROTO(int, ex_preserve); +EXPROTO(int, ex_prev); +EXPROTO(int, ex_put); +EXPROTO(int, ex_quit); +EXPROTO(int, ex_read); +EXPROTO(int, ex_resize); +EXPROTO(int, ex_rew); +EXPROTO(int, ex_script); +EXPROTO(int, ex_set); +EXPROTO(int, ex_shell); +EXPROTO(int, ex_shiftl); +EXPROTO(int, ex_shiftr); +EXPROTO(int, ex_source); +EXPROTO(int, ex_split); +EXPROTO(int, ex_stop); +EXPROTO(int, ex_subagain); +EXPROTO(int, ex_substitute); +EXPROTO(int, ex_subtilde); +EXPROTO(int, ex_tagpop); +EXPROTO(int, ex_tagpush); +EXPROTO(int, ex_tagtop); +EXPROTO(int, ex_unabbr); +EXPROTO(int, ex_undo); +EXPROTO(int, ex_undol); +EXPROTO(int, ex_unmap); +EXPROTO(int, ex_usage); +EXPROTO(int, ex_validate); +EXPROTO(int, ex_version); +EXPROTO(int, ex_vglobal); +EXPROTO(int, ex_visual); +EXPROTO(int, ex_viusage); +EXPROTO(int, ex_wq); +EXPROTO(int, ex_write); +EXPROTO(int, ex_xit); +EXPROTO(int, ex_yank); +EXPROTO(int, ex_z); diff --git a/usr.bin/vi/nex/filter.c b/usr.bin/vi/nex/filter.c new file mode 100644 index 000000000000..d50e1e665d8d --- /dev/null +++ b/usr.bin/vi/nex/filter.c @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)filter.c 8.26 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +static int filter_ldisplay __P((SCR *, FILE *)); + +/* + * filtercmd -- + * Run a range of lines through a filter utility and optionally + * replace the original text with the stdout/stderr output of + * the utility. + */ +int +filtercmd(sp, ep, fm, tm, rp, cmd, ftype) + SCR *sp; + EXF *ep; + MARK *fm, *tm, *rp; + char *cmd; + enum filtertype ftype; +{ + struct sigaction act, oact; + struct stat osb, sb; + struct termios term; + FILE *ifp, *ofp; /* GCC: can't be uninitialized. */ + pid_t parent_writer_pid, utility_pid; + recno_t lno, nread; + int input[2], isig, output[2], rval; + char *name; + + /* Set return cursor position; guard against a line number of zero. */ + *rp = *fm; + if (fm->lno == 0) + rp->lno = 1; + + /* + * There are three different processes running through this code. + * They are the utility, the parent-writer and the parent-reader. + * The parent-writer is the process that writes from the file to + * the utility, the parent reader is the process that reads from + * the utility. + * + * Input and output are named from the utility's point of view. + * The utility reads from input[0] and the parent(s) write to + * input[1]. The parent(s) read from output[0] and the utility + * writes to output[1]. + * + * In the FILTER_READ case, the utility isn't expected to want + * input. Redirect its input from /dev/null. Otherwise open + * up utility input pipe. + */ + ifp = ofp = NULL; + input[0] = input[1] = output[0] = output[1] = -1; + if (ftype == FILTER_READ) { + if ((input[0] = open(_PATH_DEVNULL, O_RDONLY, 0)) < 0) { + msgq(sp, M_ERR, + "filter: %s: %s", _PATH_DEVNULL, strerror(errno)); + return (1); + } + } else { + if (pipe(input) < 0) { + msgq(sp, M_SYSERR, "pipe"); + goto err; + } + if ((ifp = fdopen(input[1], "w")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + } + + /* Open up utility output pipe. */ + if (pipe(output) < 0) { + msgq(sp, M_SYSERR, "pipe"); + goto err; + } + if ((ofp = fdopen(output[0], "r")) == NULL) { + msgq(sp, M_SYSERR, "fdopen"); + goto err; + } + + /* + * Save ex/vi terminal settings, and restore the original ones. + * Restoration so that users can do things like ":r! cat /dev/tty". + */ + EX_LEAVE(sp, isig, act, oact, sb, osb, term); + + /* Fork off the utility process. */ + switch (utility_pid = vfork()) { + case -1: /* Error. */ + msgq(sp, M_SYSERR, "vfork"); +err: if (input[0] != -1) + (void)close(input[0]); + if (ifp != NULL) + (void)fclose(ifp); + else if (input[1] != -1) + (void)close(input[1]); + if (ofp != NULL) + (void)fclose(ofp); + else if (output[0] != -1) + (void)close(output[0]); + if (output[1] != -1) + (void)close(output[1]); + rval = 1; + goto ret; + case 0: /* Utility. */ + /* + * The utility has default signal behavior. Don't bother + * using sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + /* + * Redirect stdin from the read end of the input pipe, + * and redirect stdout/stderr to the write end of the + * output pipe. + */ + (void)dup2(input[0], STDIN_FILENO); + (void)dup2(output[1], STDOUT_FILENO); + (void)dup2(output[1], STDERR_FILENO); + + /* Close the utility's file descriptors. */ + (void)close(input[0]); + (void)close(input[1]); + (void)close(output[0]); + (void)close(output[1]); + + if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) + name = O_STR(sp, O_SHELL); + else + ++name; + + execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL); + msgq(sp, M_ERR, "Error: execl: %s: %s", + O_STR(sp, O_SHELL), strerror(errno)); + _exit (127); + /* NOTREACHED */ + default: /* Parent-reader, parent-writer. */ + /* Close the pipe ends neither parent will use. */ + (void)close(input[0]); + (void)close(output[1]); + break; + } + + /* + * FILTER_READ: + * + * Reading is the simple case -- we don't need a parent writer, + * so the parent reads the output from the read end of the output + * pipe until it finishes, then waits for the child. Ex_readfp + * appends to the MARK, and closes ofp. + * + * !!! + * Set the return cursor to the last line read in. Historically, + * this behaves differently from ":r file" command, which leaves + * the cursor at the first line read in. Check to make sure that + * it's not past EOF because we were reading into an empty file. + */ + if (ftype == FILTER_READ) { + rval = ex_readfp(sp, ep, "filter", ofp, fm, &nread, 0); + sp->rptlines[L_ADDED] += nread; + if (fm->lno == 0) + rp->lno = nread; + else + rp->lno += nread; + goto uwait; + } + + /* + * FILTER, FILTER_WRITE + * + * Here we need both a reader and a writer. Temporary files are + * expensive and we'd like to avoid disk I/O. Using pipes has the + * obvious starvation conditions. It's done as follows: + * + * fork + * child + * write lines out + * exit + * parent + * FILTER: + * read lines into the file + * delete old lines + * FILTER_WRITE + * read and display lines + * wait for child + * + * XXX + * We get away without locking the underlying database because we know + * that none of the records that we're reading will be modified until + * after we've read them. This depends on the fact that the current + * B+tree implementation doesn't balance pages or similar things when + * it inserts new records. When the DB code has locking, we should + * treat vi as if it were multiple applications sharing a database, and + * do the required locking. If necessary a work-around would be to do + * explicit locking in the line.c:file_gline() code, based on the flag + * set here. + */ + rval = 0; + F_SET(ep, F_MULTILOCK); + switch (parent_writer_pid = fork()) { + case -1: /* Error. */ + rval = 1; + msgq(sp, M_SYSERR, "fork"); + (void)close(input[1]); + (void)close(output[0]); + break; + case 0: /* Parent-writer. */ + /* + * Write the selected lines to the write end of the + * input pipe. Ifp is closed by ex_writefp. + */ + (void)close(output[0]); + _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL)); + + /* NOTREACHED */ + default: /* Parent-reader. */ + (void)close(input[1]); + if (ftype == FILTER_WRITE) + /* + * Read the output from the read end of the output + * pipe and display it. Filter_ldisplay closes ofp. + */ + rval = filter_ldisplay(sp, ofp); + else { + /* + * Read the output from the read end of the output + * pipe. Ex_readfp appends to the MARK and closes + * ofp. + */ + rval = ex_readfp(sp, ep, "filter", ofp, tm, &nread, 0); + sp->rptlines[L_ADDED] += nread; + } + + /* Wait for the parent-writer. */ + rval |= proc_wait(sp, + (long)parent_writer_pid, "parent-writer", 1); + + /* Delete any lines written to the utility. */ + if (ftype == FILTER && rval == 0) { + for (lno = tm->lno; lno >= fm->lno; --lno) + if (file_dline(sp, ep, lno)) { + rval = 1; + break; + } + if (rval == 0) + sp->rptlines[L_DELETED] += + (tm->lno - fm->lno) + 1; + } + /* + * If the filter had no output, we may have just deleted + * the cursor. Don't do any real error correction, we'll + * try and recover later. + */ + if (rp->lno > 1 && file_gline(sp, ep, rp->lno, NULL) == NULL) + --rp->lno; + break; + } + F_CLR(ep, F_MULTILOCK); + +uwait: rval |= proc_wait(sp, (long)utility_pid, cmd, 0); + + /* Restore ex/vi terminal settings. */ +ret: EX_RETURN(sp, isig, act, oact, sb, osb, term); + + return (rval); +} + +/* + * proc_wait -- + * Wait for one of the processes. + * + * !!! + * The pid_t type varies in size from a short to a long depending on the + * system. It has to be cast into something or the standard promotion + * rules get you. I'm using a long based on the belief that nobody is + * going to make it unsigned and it's unlikely to be a quad. + */ +int +proc_wait(sp, pid, cmd, okpipe) + SCR *sp; + long pid; + const char *cmd; + int okpipe; +{ + extern const char *const sys_siglist[]; + size_t len; + int pstat; + + /* Wait for the utility to finish. */ + (void)waitpid((pid_t)pid, &pstat, 0); + + /* + * Display the utility's exit status. Ignore SIGPIPE from the + * parent-writer, as that only means that the utility chose to + * exit before reading all of its input. + */ + if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) { + for (; isblank(*cmd); ++cmd); + len = strlen(cmd); + msgq(sp, M_ERR, "%.*s%s: received signal: %s%s.", + MIN(len, 20), cmd, len > 20 ? "..." : "", + sys_siglist[WTERMSIG(pstat)], + WCOREDUMP(pstat) ? "; core dumped" : ""); + return (1); + } + if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) { + for (; isblank(*cmd); ++cmd); + len = strlen(cmd); + msgq(sp, M_ERR, "%.*s%s: exited with status %d", + MIN(len, 20), cmd, len > 20 ? "..." : "", + WEXITSTATUS(pstat)); + return (1); + } + return (0); +} + +/* + * filter_ldisplay -- + * Display a line output from a utility. + * + * XXX + * This should probably be combined with some of the ex_print() + * routines into a single display routine. + */ +static int +filter_ldisplay(sp, fp) + SCR *sp; + FILE *fp; +{ + EX_PRIVATE *exp; + size_t len; + + exp = EXP(sp); + while (!ex_getline(sp, fp, &len)) { + (void)ex_printf(EXCOOKIE, "%.*s\n", (int)len, exp->ibp); + if (ferror(sp->stdfp)) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + } + if (fclose(fp)) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + return (0); +} diff --git a/usr.bin/vi/nex/script.h b/usr.bin/vi/nex/script.h new file mode 100644 index 000000000000..a04f14459165 --- /dev/null +++ b/usr.bin/vi/nex/script.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)script.h 8.1 (Berkeley) 12/19/93 + */ + +struct _script { + pid_t sh_pid; /* Shell pid. */ + int sh_master; /* Master pty fd. */ + int sh_slave; /* Slave pty fd. */ + char *sh_prompt; /* Prompt. */ + size_t sh_prompt_len; /* Prompt length. */ + char sh_name[64]; /* Pty name */ + struct winsize sh_win; /* Window size. */ + struct termios sh_term; /* Terminal information. */ +}; diff --git a/usr.bin/vi/nex/tag.h b/usr.bin/vi/nex/tag.h new file mode 100644 index 000000000000..f75d9b3fd466 --- /dev/null +++ b/usr.bin/vi/nex/tag.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tag.h 8.11 (Berkeley) 11/22/93 + */ + +struct _tagf { /* Tag file. */ + TAILQ_ENTRY(_tagf) q; /* Linked list of tag files. */ + char *name; /* Tag file name. */ + +#define TAGF_DNE 0x01 /* Didn't exist. */ +#define TAGF_DNE_WARN 0x02 /* DNE error reported. */ + u_char flags; +}; + +struct _tag { /* Tag stack. */ + TAILQ_ENTRY(_tag) q; /* Linked list of tags. */ + FREF *frp; /* Saved file name. */ + recno_t lno; /* Saved line number. */ + size_t cno; /* Saved column number. */ + char *search; /* Search string. */ + size_t slen; /* Search string length. */ +}; + +int ex_tagalloc __P((SCR *, char *)); +int ex_tagcopy __P((SCR *, SCR *)); +int ex_tagdisplay __P((SCR *, EXF *)); +int ex_tagfirst __P((SCR *, char *)); +int ex_tagfree __P((SCR *)); diff --git a/usr.bin/vi/nvi/getc.c b/usr.bin/vi/nvi/getc.c new file mode 100644 index 000000000000..d4ac2304b914 --- /dev/null +++ b/usr.bin/vi/nvi/getc.c @@ -0,0 +1,252 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)getc.c 8.6 (Berkeley) 10/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * Character stream routines -- + * These routines return the file a character at a time. There are two + * special cases. First, the end of a line, end of a file, start of a + * file and empty lines are returned as special cases, and no character + * is returned. Second, empty lines include lines that have only white + * space in them, because the vi search functions don't care about white + * space, and this makes it easier for them to be consistent. + */ + +/* + * cs_init -- + * Initialize character stream routines. + */ +int +cs_init(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t lno; + + if ((csp->cs_bp = + file_gline(sp, ep, csp->cs_lno, &csp->cs_len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + msgq(sp, M_BERR, "Empty file."); + else + GETLINE_ERR(sp, csp->cs_lno); + return (1); + } + if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_ch = csp->cs_bp[csp->cs_cno]; + } + return (0); +} + +/* + * cs_next -- + * Retrieve the next character. + */ +int +cs_next(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t slno; + + switch (csp->cs_flags) { + case CS_EMP: /* EMP; get next line. */ + case CS_EOL: /* EOL; get next line. */ + slno = csp->cs_lno; /* Save current line. */ + if ((csp->cs_bp = + file_gline(sp, ep, ++csp->cs_lno, &csp->cs_len)) == NULL) { + csp->cs_lno = slno; + if (file_lline(sp, ep, &slno)) + return (1); + if (slno > csp->cs_lno) { + GETLINE_ERR(sp, csp->cs_lno); + return (1); + } + csp->cs_flags = CS_EOF; + } else if (csp->cs_len == 0 || + v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_ch = csp->cs_bp[csp->cs_cno = 0]; + } + break; + case 0: + if (csp->cs_cno == csp->cs_len - 1) + csp->cs_flags = CS_EOL; + else + csp->cs_ch = csp->cs_bp[++csp->cs_cno]; + break; + case CS_EOF: /* EOF; only returned once. */ + default: + abort(); + /* NOTREACHED */ + } + return (0); +} + +/* + * cs_fspace -- + * If on a space, eat forward until something other than a + * whitespace character. + * + * XXX + * Semantics of checking the current character were coded for the fword() + * function -- once the other word routines are converted, they may have + * to change. + */ +int +cs_fspace(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + if (csp->cs_flags != 0 || !isblank(csp->cs_ch)) + return (0); + for (;;) { + if (cs_next(sp, ep, csp)) + return (1); + if (csp->cs_flags != 0 || !isblank(csp->cs_ch)) + break; + } + return (0); +} + +/* + * cs_fblank -- + * Eat forward to the next non-whitespace character. + */ +int +cs_fblank(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + for (;;) { + if (cs_next(sp, ep, csp)) + return (1); + if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP || + csp->cs_flags == 0 && isblank(csp->cs_ch)) + continue; + break; + } + return (0); +} + +/* + * cs_prev -- + * Retrieve the previous character. + */ +int +cs_prev(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + recno_t slno; + + switch (csp->cs_flags) { + case CS_EMP: /* EMP; get previous line. */ + case CS_EOL: /* EOL; get previous line. */ + if (csp->cs_lno == 1) { /* SOF. */ + csp->cs_flags = CS_SOF; + break; + } + slno = csp->cs_lno; /* Save current line. */ + if ((csp->cs_bp = /* Line should exist. */ + file_gline(sp, ep, --csp->cs_lno, &csp->cs_len)) == NULL) { + GETLINE_ERR(sp, csp->cs_lno); + csp->cs_lno = slno; + return (1); + } + if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) { + csp->cs_cno = 0; + csp->cs_flags = CS_EMP; + } else { + csp->cs_flags = 0; + csp->cs_cno = csp->cs_len - 1; + csp->cs_ch = csp->cs_bp[csp->cs_cno]; + } + break; + case 0: + if (csp->cs_cno == 0) + csp->cs_flags = CS_EOL; + else + csp->cs_ch = csp->cs_bp[--csp->cs_cno]; + break; + case CS_SOF: /* SOF; only returned once. */ + default: + abort(); + /* NOTREACHED */ + } + return (0); +} + +/* + * cs_bblank -- + * Eat backward to the next non-whitespace character. + */ +int +cs_bblank(sp, ep, csp) + SCR *sp; + EXF *ep; + VCS *csp; +{ + for (;;) { + if (cs_prev(sp, ep, csp)) + return (1); + if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP || + csp->cs_flags == 0 && isblank(csp->cs_ch)) + continue; + break; + } + return (0); +} diff --git a/usr.bin/vi/nvi/v_again.c b/usr.bin/vi/nvi/v_again.c new file mode 100644 index 000000000000..7e9ea5650d83 --- /dev/null +++ b/usr.bin/vi/nvi/v_again.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_again.c 8.2 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_again -- & + * Repeat the previous substitution. + */ +int +v_again(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SUBAGAIN, 2, fm->lno, fm->lno, 1, ""); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_at.c b/usr.bin/vi/nvi/v_at.c new file mode 100644 index 000000000000..3b21fc3186d5 --- /dev/null +++ b/usr.bin/vi/nvi/v_at.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_at.c 8.3 (Berkeley) 8/25/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +int +v_at(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_AT, 0, OOBLNO, OOBLNO, 0, NULL); + cmd.buffer = vp->buffer; + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_ch.c b/usr.bin/vi/nvi/v_ch.c new file mode 100644 index 000000000000..40807d1db6b1 --- /dev/null +++ b/usr.bin/vi/nvi/v_ch.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ch.c 8.2 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> + +#include "vi.h" +#include "vcmd.h" + +#define NOPREV { \ + msgq(sp, M_BERR, "No previous F, f, T or t search."); \ + return (1); \ +} + +#define NOTFOUND(ch) { \ + msgq(sp, M_BERR, "%s not found.", charname(sp, ch)); \ + return (1); \ +} + +/* + * v_chrepeat -- [count]; + * Repeat the last F, f, T or t search. + */ +int +v_chrepeat(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + vp->character = sp->lastckey; + + switch (sp->csearchdir) { + case CNOTSET: + NOPREV; + case FSEARCH: + return (v_chF(sp, ep, vp, fm, tm, rp)); + case fSEARCH: + return (v_chf(sp, ep, vp, fm, tm, rp)); + case TSEARCH: + return (v_chT(sp, ep, vp, fm, tm, rp)); + case tSEARCH: + return (v_cht(sp, ep, vp, fm, tm, rp)); + default: + abort(); + } + /* NOTREACHED */ +} + +/* + * v_chrrepeat -- [count], + * Repeat the last F, f, T or t search in the reverse direction. + */ +int +v_chrrepeat(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + enum cdirection savedir; + + vp->character = sp->lastckey; + savedir = sp->csearchdir; + + switch (sp->csearchdir) { + case CNOTSET: + NOPREV; + case FSEARCH: + rval = v_chf(sp, ep, vp, fm, tm, rp); + break; + case fSEARCH: + rval = v_chF(sp, ep, vp, fm, tm, rp); + break; + case TSEARCH: + rval = v_cht(sp, ep, vp, fm, tm, rp); + break; + case tSEARCH: + rval = v_chT(sp, ep, vp, fm, tm, rp); + break; + default: + abort(); + } + sp->csearchdir = savedir; + return (rval); +} + +/* + * v_cht -- [count]tc + * Search forward in the line for the next occurrence of the character. + * Place the cursor on it if a motion command, to its left if its not. + */ +int +v_cht(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + + rval = v_chf(sp, ep, vp, fm, tm, rp); + if (!rval) + --rp->cno; /* XXX: Motion interaction with v_chf. */ + sp->csearchdir = tSEARCH; + return (rval); +} + +/* + * v_chf -- [count]fc + * Search forward in the line for the next occurrence of the character. + * Place the cursor to its right if a motion command, on it if its not. + */ +int +v_chf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t lno; + u_long cnt; + int key; + char *endp, *p, *startp; + + /* + * !!! + * If it's a dot command, it doesn't reset the key for which + * we're searching, e.g. in "df1|f2|.|;", the ';' searches + * for a '2'. + */ + key = vp->character; + if (!F_ISSET(vp, VC_ISDOT)) + sp->lastckey = key; + sp->csearchdir = fSEARCH; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NOTFOUND(key); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NOTFOUND(key); + + startp = p; + endp = p + len; + p += fm->cno; + for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + while (++p < endp && *p != key); + if (p == endp) + NOTFOUND(key); + } + rp->lno = fm->lno; + rp->cno = p - startp; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); +} + +/* + * v_chT -- [count]Tc + * Search backward in the line for the next occurrence of the character. + * Place the cursor to its right. + */ +int +v_chT(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int rval; + + rval = v_chF(sp, ep, vp, fm, tm, rp); + if (!rval) + ++rp->cno; + sp->csearchdir = TSEARCH; + return (0); +} + +/* + * v_chF -- [count]Fc + * Search backward in the line for the next occurrence of the character. + * Place the cursor on it. + */ +int +v_chF(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_long cnt; + int key; + char *p, *endp; + + /* + * !!! + * If it's a dot command, it doesn't reset the key for which + * we're searching, e.g. in "df1|f2|.|;", the ';' searches + * for a '2'. + */ + key = vp->character; + if (!F_ISSET(vp, VC_ISDOT)) + sp->lastckey = key; + sp->csearchdir = FSEARCH; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NOTFOUND(key); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NOTFOUND(key); + + endp = p - 1; + p += fm->cno; + for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + while (--p > endp && *p != key); + if (p == endp) + NOTFOUND(key); + } + rp->lno = fm->lno; + rp->cno = (p - endp) - 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_delete.c b/usr.bin/vi/nvi/v_delete.c new file mode 100644 index 000000000000..241ddf654f2e --- /dev/null +++ b/usr.bin/vi/nvi/v_delete.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_delete.c 8.7 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Delete -- [buffer][count]D + * Delete line command. + */ +int +v_Delete(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + return (0); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + return (0); + + tm->lno = fm->lno; + tm->cno = len; + + /* Yank the lines. */ + if (cut(sp, ep, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, CUT_DELETE)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + rp->lno = fm->lno; + rp->cno = fm->cno ? fm->cno - 1 : 0; + return (0); +} + +/* + * v_delete -- [buffer][count]d[count]motion + * Delete a range of text. + */ +int +v_delete(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t nlines; + size_t len; + int lmode; + + /* Yank the lines. */ + lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0; + if (cut(sp, ep, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, lmode | CUT_DELETE)) + return (1); + if (delete(sp, ep, fm, tm, lmode)) + return (1); + + /* Check for deleting the file. */ + if (file_lline(sp, ep, &nlines)) + return (1); + if (nlines == 0) { + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* + * If deleting lines, leave the cursor at the lowest line deleted, + * else, leave the cursor where it started. Always correct for EOL. + * + * The historic vi would delete the line the cursor was on (even if + * not in line mode) if the motion from the cursor was past the EOF + * and the cursor didn't originate on the last line of the file. A + * strange special case. We never delete the line the cursor is on. + * We'd have to pass a flag down to the delete() routine which would + * have to special case it. + */ + if (lmode) { + rp->lno = MIN(fm->lno, tm->lno); + if (rp->lno > nlines) + rp->lno = nlines; + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + return (0); + } + + rp->lno = fm->lno; + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (fm->cno >= len) + rp->cno = len ? len - 1 : 0; + else + rp->cno = fm->cno; + return (0); +} diff --git a/usr.bin/vi/nvi/v_ex.c b/usr.bin/vi/nvi/v_ex.c new file mode 100644 index 000000000000..631cd342d010 --- /dev/null +++ b/usr.bin/vi/nvi/v_ex.c @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ex.c 8.1 (Berkeley) 6/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_ex -- + * Run ex. + */ +int +v_ex(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_ex_run(sp, ep, rp)); +} diff --git a/usr.bin/vi/nvi/v_exit.c b/usr.bin/vi/nvi/v_exit.c new file mode 100644 index 000000000000..f308c480d3d7 --- /dev/null +++ b/usr.bin/vi/nvi/v_exit.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_exit.c 8.5 (Berkeley) 12/10/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_exit -- ZZ + * Save the file and exit. + */ +int +v_exit(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(ep, F_MODIFIED) && + file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + + /* + * !!! + * Historic practice: quit! or two quit's done in succession + * (where ZZ counts as a quit) didn't check for other files. + * + * Also check for related screens; if they exist, quit, the + * user will get the message on the last screen. + */ + if (sp->ccnt != sp->q_ccnt + 1 && + ep->refcnt <= 1 && file_unedited(sp) != NULL) { + sp->q_ccnt = sp->ccnt; + msgq(sp, M_ERR, + "More files to edit; use \":n\" to go to the next file"); + return (1); + } + + F_SET(sp, S_EXIT); + return (0); +} diff --git a/usr.bin/vi/nvi/v_exmode.c b/usr.bin/vi/nvi/v_exmode.c new file mode 100644 index 000000000000..150ec52ccf9c --- /dev/null +++ b/usr.bin/vi/nvi/v_exmode.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_exmode.c 8.3 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_exmode -- + * Put the editor in EX mode. + */ +int +v_exmode(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + sp->saved_vi_mode = F_ISSET(sp, S_VI_CURSES | S_VI_XAW); + F_CLR(sp, S_SCREENS); + F_SET(sp, S_EX); + return (0); +} diff --git a/usr.bin/vi/nvi/v_filter.c b/usr.bin/vi/nvi/v_filter.c new file mode 100644 index 000000000000..52ff2ae77b8d --- /dev/null +++ b/usr.bin/vi/nvi/v_filter.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_filter.c 8.10 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +/* + * v_filter -- [count]!motion command(s) + * Run range through shell commands, replacing text. + */ +int +v_filter(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + TEXT *tp; + + /* + * !!! + * Historical vi permitted "!!" in an empty file. This is + * handled as a special case in the ex_bang routine. Don't + * modify this setup without understanding that one. In + * particular, note that we're manipulating the ex argument + * structures behind ex's back. + */ + SETCMDARG(cmd, C_BANG, 2, fm->lno, tm->lno, 0, NULL); + EXP(sp)->argsoff = 0; /* XXX */ + if (F_ISSET(vp, VC_ISDOT)) { + if (argv_exp1(sp, ep, &cmd, "!", 1, 1)) + return (1); + } else { + /* Get the command from the user. */ + if (sp->s_get(sp, ep, &sp->tiq, + '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK) + return (1); + /* + * Len is 0 if backspaced over the prompt, + * 1 if only CR entered. + */ + tp = sp->tiq.cqh_first; + if (tp->len <= 1) + return (0); + + if (argv_exp1(sp, ep, &cmd, tp->lb + 1, tp->len - 1, 1)) + return (1); + } + cmd.argc = EXP(sp)->argsoff; /* XXX */ + cmd.argv = EXP(sp)->args; /* XXX */ + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_increment.c b/usr.bin/vi/nvi/v_increment.c new file mode 100644 index 000000000000..dfae29ea463e --- /dev/null +++ b/usr.bin/vi/nvi/v_increment.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_increment.c 8.6 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static char * const fmt[] = { +#define DEC 0 + "%ld", +#define SDEC 1 + "%+ld", +#define HEXC 2 + "%#0.*lX", +#define HEXL 3 + "%#0.*lx", +#define OCTAL 4 + "%#0.*lo", +}; + +/* + * v_increment -- [count]#[#+-] + * Increment/decrement a keyword number. + */ +int +v_increment(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + VI_PRIVATE *vip; + u_long ulval; + long lval; + size_t blen, len, nlen; + int rval; + char *bp, *ntype, *p, nbuf[100]; + + vip = VIP(sp); + + /* Do repeat operations. */ + if (vp->character == '#') + vp->character = vip->inc_lastch; + + /* Get new value. */ + if (F_ISSET(vp, VC_C1SET)) + vip->inc_lastval = vp->count; + + if (vp->character != '+' && vp->character != '-') { + msgq(sp, M_ERR, "usage: %s.", vp->kp->usage); + return (1); + } + vip->inc_lastch = vp->character; + + /* Figure out the resulting type and number. */ + p = vp->keyword; + len = vp->klen; + if (len > 1 && p[0] == '0') { + if (vp->character == '+') { + ulval = strtoul(vp->keyword, NULL, 0); + if (ULONG_MAX - ulval < vip->inc_lastval) + goto overflow; + ulval += vip->inc_lastval; + } else { + ulval = strtoul(vp->keyword, NULL, 0); + if (ulval < vip->inc_lastval) + goto underflow; + ulval -= vip->inc_lastval; + } + ntype = fmt[OCTAL]; + if (len > 2) + if (p[1] == 'X') + ntype = fmt[HEXC]; + else if (p[1] == 'x') + ntype = fmt[HEXL]; + nlen = snprintf(nbuf, sizeof(nbuf), ntype, len, ulval); + } else { + if (vp->character == '+') { + lval = strtol(vp->keyword, NULL, 0); + if (lval > 0 && LONG_MAX - lval < vip->inc_lastval) { +overflow: msgq(sp, M_ERR, "Resulting number too large."); + return (1); + } + lval += vip->inc_lastval; + } else { + lval = strtol(vp->keyword, NULL, 0); + if (lval < 0 && -(LONG_MIN - lval) < vip->inc_lastval) { +underflow: msgq(sp, M_ERR, "Resulting number too small."); + return (1); + } + lval -= vip->inc_lastval; + } + ntype = lval != 0 && + (*vp->keyword == '+' || *vp->keyword == '-') ? + fmt[SDEC] : fmt[DEC]; + nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval); + } + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + + GET_SPACE_RET(sp, bp, blen, len + nlen); + memmove(bp, p, fm->cno); + memmove(bp + fm->cno, nbuf, nlen); + memmove(bp + fm->cno + nlen, + p + fm->cno + vp->klen, len - fm->cno - vp->klen); + len = len - vp->klen + nlen; + + rval = file_sline(sp, ep, fm->lno, bp, len); + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_init.c b/usr.bin/vi/nvi/v_init.c new file mode 100644 index 000000000000..f0d2facf4ea3 --- /dev/null +++ b/usr.bin/vi/nvi/v_init.c @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_init.c 8.18 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +static int v_comment __P((SCR *, EXF *)); + +/* + * v_screen_copy -- + * Copy vi screen. + */ +int +v_screen_copy(orig, sp) + SCR *orig, *sp; +{ + VI_PRIVATE *ovip, *nvip; + + /* Create the private vi structure. */ + CALLOC_RET(orig, nvip, VI_PRIVATE *, 1, sizeof(VI_PRIVATE)); + sp->vi_private = nvip; + + if (orig == NULL) { + nvip->inc_lastch = '+'; + nvip->inc_lastval = 1; + } else { + ovip = VIP(orig); + + /* User can replay the last input, but nothing else. */ + if (ovip->rep_len != 0) { + MALLOC(orig, nvip->rep, char *, ovip->rep_len); + if (nvip->rep != NULL) { + memmove(nvip->rep, ovip->rep, ovip->rep_len); + nvip->rep_len = ovip->rep_len; + } + } + + nvip->inc_lastch = ovip->inc_lastch; + nvip->inc_lastval = ovip->inc_lastval; + + if (ovip->paragraph != NULL && + (nvip->paragraph = strdup(ovip->paragraph)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + } + return (0); +} + +/* + * v_screen_end -- + * End a vi screen. + */ +int +v_screen_end(sp) + SCR *sp; +{ + VI_PRIVATE *vip; + + vip = VIP(sp); + + if (vip->rep != NULL) + FREE(vip->rep, vip->rep_len); + + if (vip->paragraph != NULL) + FREE(vip->paragraph, vip->paragraph_len); + + /* Free private memory. */ + FREE(vip, sizeof(VI_PRIVATE)); + sp->vi_private = NULL; + + return (0); +} + +/* + * v_init -- + * Initialize vi. + */ +int +v_init(sp, ep) + SCR *sp; + EXF *ep; +{ + size_t len; + + /* + * The default address is line 1, column 0. If the address set + * bit is on for this file, load the address, ensuring that it + * exists. + */ + if (F_ISSET(sp->frp, FR_CURSORSET)) { + sp->lno = sp->frp->lno; + sp->cno = sp->frp->cno; + + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (sp->lno != 1 || sp->cno != 0) { + if (file_lline(sp, ep, &sp->lno)) + return (1); + if (sp->lno == 0) + sp->lno = 1; + sp->cno = 0; + } + } else if (sp->cno >= len) + sp->cno = 0; + + } else { + sp->lno = 1; + sp->cno = 0; + + if (O_ISSET(sp, O_COMMENT) && v_comment(sp, ep)) + return (1); + } + + /* Reset strange attraction. */ + sp->rcm = 0; + sp->rcmflags = 0; + + /* Make ex display to a special function. */ + if ((sp->stdfp = fwopen(sp, sp->s_ex_write)) == NULL) { + msgq(sp, M_SYSERR, "ex output"); + return (1); + } +#ifdef MAKE_EX_OUTPUT_LINE_BUFFERED + (void)setvbuf(sp->stdfp, NULL, _IOLBF, 0); +#endif + + /* Display the status line. */ + return (status(sp, ep, sp->lno, 0)); +} + +/* + * v_end -- + * End vi session. + */ +int +v_end(sp) + SCR *sp; +{ + /* Close down ex output file descriptor. */ + (void)fclose(sp->stdfp); + + return (0); +} + +/* + * v_optchange -- + * Handle change of options for vi. + */ +int +v_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_PARAGRAPHS: + case O_SECTIONS: + return (v_buildparagraph(sp)); + } + return (0); +} + +/* + * v_comment -- + * Skip the first comment. + */ +static int +v_comment(sp, ep) + SCR *sp; + EXF *ep; +{ + recno_t lno; + size_t len; + char *p; + + for (lno = 1; + (p = file_gline(sp, ep, lno, &len)) != NULL && len == 0; ++lno); + if (p == NULL || len <= 1 || memcmp(p, "/*", 2)) + return (0); + do { + for (; len; --len, ++p) + if (p[0] == '*' && len > 1 && p[1] == '/') { + sp->lno = lno; + return (0); + } + } while ((p = file_gline(sp, ep, ++lno, &len)) != NULL); + return (0); +} diff --git a/usr.bin/vi/nvi/v_join.c b/usr.bin/vi/nvi/v_join.c new file mode 100644 index 000000000000..c3f81d6d1a5c --- /dev/null +++ b/usr.bin/vi/nvi/v_join.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_join.c 8.3 (Berkeley) 8/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_join -- [count]J + * Join lines together. + */ +int +v_join(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + int lno; + + /* + * YASC. + * The general rule is that '#J' joins # lines, counting the current + * line. However, 'J' and '1J' are the same as '2J', i.e. join the + * current and next lines. This doesn't map well into the ex command + * (which takes two line numbers), so we handle it here. Note that + * we never test for EOF -- historically going past the end of file + * worked just fine. + */ + lno = fm->lno + 1; + if (F_ISSET(vp, VC_C1SET) && vp->count > 2) + lno = fm->lno + (vp->count - 1); + + SETCMDARG(cmd, C_JOIN, 2, fm->lno, lno, 0, NULL); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_left.c b/usr.bin/vi/nvi/v_left.c new file mode 100644 index 000000000000..cc80b379b06d --- /dev/null +++ b/usr.bin/vi/nvi/v_left.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_left.c 8.3 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_left -- [count]^H, [count]h + * Move left by columns. + */ +int +v_left(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t cnt; + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + if (fm->cno == 0) { + msgq(sp, M_BERR, "Already in the first column."); + return (1); + } + + rp->lno = fm->lno; + if (fm->cno > cnt) + rp->cno = fm->cno - cnt; + else + rp->cno = 0; + return (0); +} + +/* + * v_cfirst -- [count]_ + * + * Move to the first non-blank column on a line. + */ +int +v_cfirst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t cnt; + + /* + * A count moves down count - 1 rows, so, "3_" is the same as "2j_". + * + * !!! + * Historically, if the _ is a motion, it is always a line motion, + * and the line motion flag is set. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (cnt != 1) { + --vp->count; + if (v_down(sp, ep, vp, fm, tm, rp)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + F_SET(vp, VC_LMODE); + } else + rp->lno = fm->lno; + rp->cno = 0; + if (nonblank(sp, ep, rp->lno, &rp->cno)) + return (1); + return (0); +} + +/* + * v_first -- ^ + * Move to the first non-blank column on this line. + */ +int +v_first(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Yielding to none in our quest for compatibility with every + * historical blemish of vi, no matter how strange it might be, + * we permit the user to enter a count and then ignore it. + */ + rp->cno = 0; + if (nonblank(sp, ep, fm->lno, &rp->cno)) + return (1); + rp->lno = fm->lno; + return (0); +} + +/* + * v_ncol -- [count]| + * Move to column count or the first column on this line. If the + * requested column is past EOL, move to EOL. The nasty part is + * that we have to know character column widths to make this work. + */ +int +v_ncol(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_C1SET) && vp->count > 1) + rp->cno = + sp->s_chposition(sp, ep, fm->lno, (size_t)--vp->count); + else + rp->cno = 0; + rp->lno = fm->lno; + return (0); +} + +/* + * v_zero -- 0 + * Move to the first column on this line. + */ +int +v_zero(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + rp->lno = fm->lno; + rp->cno = 0; + return (0); +} diff --git a/usr.bin/vi/nvi/v_mark.c b/usr.bin/vi/nvi/v_mark.c new file mode 100644 index 000000000000..714242d9331e --- /dev/null +++ b/usr.bin/vi/nvi/v_mark.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_mark.c 8.3 (Berkeley) 10/31/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_mark -- m[a-z] + * Set a mark. + */ +int +v_mark(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + rp->lno = fm->lno; + rp->cno = fm->cno; + return (mark_set(sp, ep, vp->character, fm, 1)); +} + +/* + * v_gomark -- '['`a-z], or `['`a-z] + * Move to a mark. + * + * The single quote form moves to the first nonblank character of a line + * containing a mark. The back quote form moves to a mark, setting both + * row and column. We use a single routine for both forms, taking care + * of the nonblank behavior using a flag for the command. + * + * Although not commonly known, the "'`" and "'`" forms are historically + * valid. The behavior is determined by the first character, so "`'" is + * the same as "``". Remember this fact -- you'll be amazed at how many + * people don't know it and will be delighted that you are able to tell + * them. + */ +int +v_gomark(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + MARK *mp; + + if ((mp = mark_get(sp, ep, vp->character)) == NULL) + return (1); + *rp = *mp; + return (0); +} diff --git a/usr.bin/vi/nvi/v_match.c b/usr.bin/vi/nvi/v_match.c new file mode 100644 index 000000000000..963cca551fe8 --- /dev/null +++ b/usr.bin/vi/nvi/v_match.c @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_match.c 8.7 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_match -- % + * Search to matching character. + */ +int +v_match(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + register int cnt, matchc, startc; + VCS cs; + recno_t lno; + size_t len, off; + int (*gc)__P((SCR *, EXF *, VCS *)); + char *p; + + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + goto nomatch; + GETLINE_ERR(sp, fm->lno); + return (1); + } + + /* + * !!! + * Historical practice was to search in the forward direction only. + */ + for (off = fm->cno;; ++off) { + if (off >= len) { +nomatch: msgq(sp, M_BERR, "No match character on this line."); + return (1); + } + switch (startc = p[off]) { + case '(': + matchc = ')'; + gc = cs_next; + break; + case ')': + matchc = '('; + gc = cs_prev; + break; + case '[': + matchc = ']'; + gc = cs_next; + break; + case ']': + matchc = '['; + gc = cs_prev; + break; + case '{': + matchc = '}'; + gc = cs_next; + break; + case '}': + matchc = '{'; + gc = cs_prev; + break; + default: + continue; + } + break; + } + + cs.cs_lno = fm->lno; + cs.cs_cno = off; + if (cs_init(sp, ep, &cs)) + return (1); + for (cnt = 1;;) { + if (gc(sp, ep, &cs)) + return (1); + if (cs.cs_flags != 0) { + if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) + break; + continue; + } + if (cs.cs_ch == startc) + ++cnt; + else if (cs.cs_ch == matchc && --cnt == 0) + break; + } + if (cnt) { + msgq(sp, M_BERR, "Matching character not found."); + return (1); + } + rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * Movement commands go one space further. Increment the return + * MARK or from MARK depending on the direction of the search. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (len) + if (gc == cs_next) + ++rp->cno; + else + ++fm->cno; + } + return (0); +} diff --git a/usr.bin/vi/nvi/v_ntext.c b/usr.bin/vi/nvi/v_ntext.c new file mode 100644 index 000000000000..1c17ffc5bdd0 --- /dev/null +++ b/usr.bin/vi/nvi/v_ntext.c @@ -0,0 +1,1728 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ntext.c 8.80 (Berkeley) 1/13/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "seq.h" +#include "vcmd.h" +#include "excmd.h" + +static int txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T, int, int *, int *)); +static void txt_ai_resolve __P((SCR *, TEXT *)); +static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int)); +static void txt_err __P((SCR *, EXF *, TEXTH *)); +static int txt_hex __P((SCR *, TEXT *, int *, ARG_CHAR_T)); +static int txt_indent __P((SCR *, TEXT *)); +static int txt_margin __P((SCR *, TEXT *, int *, ARG_CHAR_T)); +static int txt_outdent __P((SCR *, TEXT *)); +static void txt_showmatch __P((SCR *, EXF *)); +static int txt_resolve __P((SCR *, EXF *, TEXTH *)); + +/* Cursor character (space is hard to track on the screen). */ +#if defined(DEBUG) && 0 +#undef CURSOR_CH +#define CURSOR_CH '+' +#endif + +/* Local version of BINC. */ +#define TBINC(sp, lp, llen, nlen) { \ + if ((nlen) > llen && binc(sp, &(lp), &(llen), nlen)) \ + goto err; \ +} + +/* + * newtext -- + * Read in text from the user. + * + * !!! + * Historic vi always used: + * + * ^D: autoindent deletion + * ^H: last character deletion + * ^W: last word deletion + * ^V: quote the next character + * + * regardless of the user's choices for these characters. The user's erase + * and kill characters worked in addition to these characters. Ex was not + * completely consistent with this, as it did map the scroll command to the + * user's EOF character. + * + * This implementation does not use fixed characters, but uses whatever the + * user specified as described by the termios structure. I'm getting away + * with something here, but I think I'm unlikely to get caught. + * + * !!! + * Historic vi did a special screen optimization for tab characters. For + * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of + * the string when it was displayed. Because this implementation redisplays + * the entire line on each keystroke, the "bcd" gets pushed to the right as + * we ignore that the user has "promised" to change the rest of the characters. + * Users have noticed, but this isn't worth fixing, and, the way that the + * historic vi did it results in an even worse bug. Given the keystrokes + * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears + * on the second <esc> key. + */ +int +v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + MARK *tm; /* To MARK. */ + const char *lp; /* Input line. */ + const size_t len; /* Input line length. */ + MARK *rp; /* Return MARK. */ + int prompt; /* Prompt to display. */ + recno_t ai_line; /* Line number to use for autoindent count. */ + u_int flags; /* TXT_ flags. */ +{ + /* State of abbreviation checks. */ + enum { A_NOTSET, A_SPACE, A_NOTSPACE } abb; + /* State of the "[^0]^D" sequences. */ + enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st; + /* State of the hex input character. */ + enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex; + /* State of quotation. */ + enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted; + CH ikey; /* Input character structure. */ + CHAR_T ch; /* Input character. */ + GS *gp; /* Global pointer. */ + TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */ + size_t rcol; /* 0-N: insert offset in the replay buffer. */ + size_t col; /* Current column. */ + u_long margin; /* Wrapmargin value. */ + u_int iflags; /* Input flags. */ + int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */ + int eval; /* Routine return value. */ + int replay; /* If replaying a set of input. */ + int showmatch; /* Showmatch set on this character. */ + int testnr; /* Test first character for nul replay. */ + int max, tmp; + char *p; + + /* + * Set the input flag, so tabs get displayed correctly + * and everyone knows that the text buffer is in use. + */ + F_SET(sp, S_INPUT); + + /* Local initialization. */ + eval = 0; + gp = sp->gp; + + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) If changing a line, + * copy it into the TEXT buffer. + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) { + text_lfree(tiqh); + goto newtp; + } + tp->ai = tp->insert = tp->offset = tp->owrite = 0; + if (lp != NULL) { + tp->len = len; + memmove(tp->lb, lp, len); + } else + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) + return (1); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + /* Set the starting line number. */ + tp->lno = sp->lno; + + /* + * Set the insert and overwrite counts. If overwriting characters, + * do insertion afterward. If not overwriting characters, assume + * doing insertion. If change is to a mark, emphasize it with an + * END_CH. + */ + if (len) { + if (LF_ISSET(TXT_OVERWRITE)) { + tp->owrite = tm->cno - sp->cno; + tp->insert = len - tm->cno; + } else + tp->insert = len - sp->cno; + + if (LF_ISSET(TXT_EMARK)) + tp->lb[tm->cno - 1] = END_CH; + } + + /* + * Many of the special cases in this routine are to handle autoindent + * support. Somebody decided that it would be a good idea if "^^D" + * and "0^D" deleted all of the autoindented characters. In an editor + * that takes single character input from the user, this wasn't a very + * good idea. Note also that "^^D" resets the next lines' autoindent, + * but "0^D" doesn't. + * + * We assume that autoindent only happens on empty lines, so insert + * and overwrite will be zero. If doing autoindent, figure out how + * much indentation we need and fill it in. Update input column and + * screen cursor as necessary. + */ + if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { + if (txt_auto(sp, ep, ai_line, NULL, 0, tp)) + return (1); + sp->cno = tp->ai; + } else { + /* + * The cc and S commands have a special feature -- leading + * <blank> characters are handled as autoindent characters. + * Beauty! + */ + if (LF_ISSET(TXT_AICHARS)) { + tp->offset = 0; + tp->ai = sp->cno; + } else + tp->offset = sp->cno; + } + + /* If getting a command buffer from the user, there may be a prompt. */ + if (LF_ISSET(TXT_PROMPT)) { + tp->lb[sp->cno++] = prompt; + ++tp->len; + ++tp->offset; + } + + /* + * If appending after the end-of-line, add a space into the buffer + * and move the cursor right. This space is inserted, i.e. pushed + * along, and then deleted when the line is resolved. Assumes that + * the cursor is already positioned at the end of the line. This + * avoids the nastiness of having the cursor reside on a magical + * column, i.e. a column that doesn't really exist. The only down + * side is that we may wrap lines or scroll the screen before it's + * strictly necessary. Not a big deal. + */ + if (LF_ISSET(TXT_APPENDEOL)) { + tp->lb[sp->cno] = CURSOR_CH; + ++tp->len; + ++tp->insert; + } + + /* + * Historic practice is that the wrapmargin value was a distance + * from the RIGHT-HAND column, not the left. It's more useful to + * us as a distance from the left-hand column. + * + * !!! + * Replay commands are not affected by wrapmargin values. What + * I found surprising was that people actually depend on it, as + * in this gem of a macro which centers lines: + * + * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p + * + * XXX + * Setting margin causes a significant performance hit. Normally + * we don't update the screen if there are keys waiting, but we + * have to if margin is set, otherwise the screen routines don't + * know where the cursor is. + */ + if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN)) + margin = 0; + else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) + margin = sp->cols - margin; + + /* Initialize abbreviations checks. */ + if (F_ISSET(gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) { + abb = A_NOTSPACE; + ab_cnt = ab_turnoff = 0; + } else + abb = A_NOTSET; + + /* + * Set up the dot command. Dot commands are done by saving the + * actual characters and replaying the input. We have to push + * the characters onto the key stack and then handle them normally, + * otherwise things like wrapmargin will fail. + * + * XXX + * It would be nice if we could swallow backspaces and such, but + * it's not all that easy to do. Another possibility would be to + * recognize full line insertions, which could be performed quickly, + * without replay. + */ +nullreplay: + rcol = 0; + if (replay = LF_ISSET(TXT_REPLAY)) { + /* + * !!! + * Historically, it wasn't an error to replay non-existent + * input. This test is necessary, we get here by the user + * doing an input command followed by a nul. + * + * !!! + * Historically, vi did not remap or reabbreviate replayed + * input. It did, however, beep at you if you changed an + * abbreviation and then replayed the input. We're not that + * compatible. + */ + if (VIP(sp)->rep == NULL) + return (0); + if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, 0, CH_NOMAP)) + return (1); + testnr = 0; + abb = A_NOTSET; + LF_CLR(TXT_RECORD); + } else + testnr = 1; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (gp, showmatch = 0, + carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) { + /* + * Reset the line and update the screen. (The txt_showmatch() + * code refreshes the screen for us.) Don't refresh unless + * we're about to wait on a character or we need to know where + * the cursor really is. + */ + if (showmatch || margin || !KEYS_WAITING(sp)) { + if (sp->s_change(sp, ep, tp->lno, LINE_RESET)) + goto err; + if (showmatch) { + showmatch = 0; + txt_showmatch(sp, ep); + } else if (sp->s_refresh(sp, ep)) + goto err; + } + + /* Get the next character. */ +next_ch: if (term_key(sp, &ikey, iflags) != INP_OK) + goto err; + ch = ikey.ch; + + /* Abbreviation check. See comment in txt_abbrev(). */ +#define MAX_ABBREVIATION_EXPANSION 256 + if (ikey.flags & CH_ABBREVIATED) { + if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) { + term_ab_flush(sp, + "Abbreviation exceeded maximum number of characters"); + ab_cnt = 0; + continue; + } + } else + ab_cnt = 0; + + /* + * !!! + * Historic feature. If the first character of the input is + * a nul, replay the previous input. This isn't documented + * anywhere, and is a great test of vi clones. + */ + if (ch == '\0' && testnr) { + LF_SET(TXT_REPLAY); + goto nullreplay; + } + testnr = 0; + + /* + * Check to see if the character fits into the input (and + * replay, if necessary) buffers. It isn't necessary to + * have tp->len bytes, since it doesn't consider overwrite + * characters, but not worth fixing. + */ + if (LF_ISSET(TXT_RECORD)) { + TBINC(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1); + VIP(sp)->rep[rcol++] = ch; + } + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + + /* + * If the character was quoted, replace the last character + * (the literal mark) with the new character. If quoted + * by someone else, simply insert the character. + * + * !!! + * Extension -- if the quoted character is HEX_CH, enter hex + * mode. If the user enters "<HEX_CH>[isxdigit()]*" we will + * try to use the value as a character. Anything else resets + * hex mode. + */ + if (ikey.flags & CH_QUOTED) + goto ins_ch; + if (quoted == Q_THISCHAR) { + --sp->cno; + ++tp->owrite; + quoted = Q_NOTSET; + + if (ch == HEX_CH) + hex = H_NEXTCHAR; + goto ins_ch; + } + + switch (ikey.value) { + case K_CR: + case K_NL: /* New line. */ +#define LINE_RESOLVE { \ + /* \ + * Handle abbreviations. If there was one, \ + * discard the replay characters. \ + */ \ + if (abb == A_NOTSPACE && !replay) { \ + if (txt_abbrev(sp, tp, ch, \ + LF_ISSET(TXT_INFOLINE), &tmp, \ + &ab_turnoff)) \ + goto err; \ + if (tmp) { \ + if (LF_ISSET(TXT_RECORD)) \ + rcol -= tmp; \ + goto next_ch; \ + } \ + } \ + if (abb != A_NOTSET) \ + abb = A_SPACE; \ + /* Handle hex numbers. */ \ + if (hex == H_INHEX) { \ + if (txt_hex(sp, tp, &tmp, ch)) \ + goto err; \ + if (tmp) { \ + hex = H_NOTSET; \ + goto next_ch; \ + } \ + } \ + /* \ + * The 'R' command returns any overwriteable \ + * characters in the first line to the original \ + * characters. + */ \ + if (LF_ISSET(TXT_REPLACE) && tp->owrite && \ + tp == tiqh->cqh_first) { \ + memmove(tp->lb + sp->cno, \ + lp + sp->cno, tp->owrite); \ + tp->insert += tp->owrite; \ + tp->owrite = 0; \ + } \ + /* Delete any appended cursor. */ \ + if (LF_ISSET(TXT_APPENDEOL)) { \ + --tp->len; \ + --tp->insert; \ + } \ +} + LINE_RESOLVE; + + /* CR returns from the vi command line. */ + if (LF_ISSET(TXT_CR)) { + /* + * If a script window and not the colon + * line, push a <cr> so it gets executed. + */ + if (F_ISSET(sp, S_SCRIPT) && + !LF_ISSET(TXT_INFOLINE)) + (void)term_push(sp, + "\r", 1, 0, CH_NOMAP); + goto k_escape; + } + + /* + * Historic practice was to delete any <blank> + * characters following the inserted newline. + * This affects the 'R', 'c', and 's' commands. + */ + for (p = tp->lb + sp->cno + tp->owrite; + tp->insert && isblank(*p); + ++p, ++tp->owrite, --tp->insert); + + /* + * Move any remaining insert characters into + * a new TEXT structure. + */ + if ((ntp = text_init(sp, + tp->lb + sp->cno + tp->owrite, + tp->insert, tp->insert + 32)) == NULL) + goto err; + + /* Set bookkeeping for the new line. */ + ntp->lno = tp->lno + 1; + ntp->insert = tp->insert; + + /* + * Note if the user inserted any characters on this + * line. Done before calling txt_ai_resolve() because + * it changes the value of sp->cno without making the + * corresponding changes to tp->ai. + */ + tmp = sp->cno <= tp->ai; + + /* + * Resolve autoindented characters for the old line. + * Reset the autoindent line value. 0^D keeps the ai + * line from changing, ^D changes the level, even if + * there are no characters in the old line. Note, + * if using the current tp structure, use the cursor + * as the length, the user may have erased autoindent + * characters. + */ + if (LF_ISSET(TXT_AUTOINDENT)) { + txt_ai_resolve(sp, tp); + + if (carat_st == C_NOCHANGE) { + if (txt_auto(sp, ep, + OOBLNO, &ait, ait.ai, ntp)) + goto err; + FREE_SPACE(sp, ait.lb, ait.lb_len); + } else + if (txt_auto(sp, ep, + OOBLNO, tp, sp->cno, ntp)) + goto err; + carat_st = C_NOTSET; + } + + /* + * If the user hasn't entered any characters, delete + * any autoindent characters. + * + * !!! + * Historic vi didn't get the insert test right, if + * there were characters after the cursor, entering + * a <cr> left the autoindent characters on the line. + */ + if (tmp) + sp->cno = 0; + + /* Reset bookkeeping for the old line. */ + tp->len = sp->cno; + tp->ai = tp->insert = tp->owrite = 0; + + /* New cursor position. */ + sp->cno = ntp->ai; + + /* New lines are TXT_APPENDEOL if nothing to insert. */ + if (ntp->insert == 0) { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + LF_SET(TXT_APPENDEOL); + ntp->lb[sp->cno] = CURSOR_CH; + ++ntp->insert; + ++ntp->len; + } + + /* Update the old line. */ + if (sp->s_change(sp, ep, tp->lno, LINE_RESET)) + goto err; + + /* + * Swap old and new TEXT's, and insert the new TEXT + * into the queue. (DON'T insert until the old line + * has been updated, or the inserted line count in + * line.c:file_gline() will be wrong.) + */ + tp = ntp; + CIRCLEQ_INSERT_TAIL(tiqh, tp, q); + + /* Reset the cursor. */ + sp->lno = tp->lno; + + /* Update the new line. */ + if (sp->s_change(sp, ep, tp->lno, LINE_INSERT)) + goto err; + + /* Set the renumber bit. */ + F_SET(sp, S_RENUMBER); + + /* Refresh if nothing waiting. */ + if ((margin || !KEYS_WAITING(sp)) && + sp->s_refresh(sp, ep)) + goto err; + goto next_ch; + case K_ESCAPE: /* Escape. */ + if (!LF_ISSET(TXT_ESCAPE)) + goto ins_ch; + + LINE_RESOLVE; + + /* + * If there aren't any trailing characters in the line + * and the user hasn't entered any characters, delete + * the autoindent characters. + */ + if (!tp->insert && sp->cno <= tp->ai) { + tp->len = tp->owrite = 0; + sp->cno = 0; + } else if (LF_ISSET(TXT_AUTOINDENT)) + txt_ai_resolve(sp, tp); + + /* If there are insert characters, copy them down. */ +k_escape: if (tp->insert && tp->owrite) + memmove(tp->lb + sp->cno, + tp->lb + sp->cno + tp->owrite, tp->insert); + tp->len -= tp->owrite; + + /* + * Delete any lines that were inserted into the text + * structure and then erased. + */ + while (tp->q.cqe_next != (void *)tiqh) { + ntp = tp->q.cqe_next; + CIRCLEQ_REMOVE(tiqh, ntp, q); + text_free(ntp); + } + + /* + * If not resolving the lines into the file, end + * it with a nul. + * + * XXX + * This is wrong, should pass back a length. + */ + if (LF_ISSET(TXT_RESOLVE)) { + if (txt_resolve(sp, ep, tiqh)) + goto err; + /* + * Clear input flag -- input buffer no longer + * valid. + */ + F_CLR(sp, S_INPUT); + } else { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + tp->lb[tp->len] = '\0'; + } + + /* + * Set the return cursor position to rest on the last + * inserted character. + */ + if (rp != NULL) { + rp->lno = tp->lno; + rp->cno = sp->cno ? sp->cno - 1 : 0; + if (sp->s_change(sp, ep, rp->lno, LINE_RESET)) + goto err; + } + goto ret; + case K_CARAT: /* Delete autoindent chars. */ + if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai) + carat_st = C_CARATSET; + goto ins_ch; + case K_ZERO: /* Delete autoindent chars. */ + if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai) + carat_st = C_ZEROSET; + goto ins_ch; + case K_VEOF: /* Delete autoindent char. */ + /* + * If in the first column or no characters to erase, + * ignore the ^D (this matches historic practice). If + * not doing autoindent or already inserted non-ai + * characters, it's a literal. The latter test is done + * in the switch, as the CARAT forms are N + 1, not N. + */ + if (!LF_ISSET(TXT_AUTOINDENT)) + goto ins_ch; + if (sp->cno == 0 || tp->ai == 0) + break; + switch (carat_st) { + case C_CARATSET: /* ^^D */ + if (sp->cno > tp->ai + tp->offset + 1) + goto ins_ch; + + /* Save the ai string for later. */ + ait.lb = NULL; + ait.lb_len = 0; + TBINC(sp, ait.lb, ait.lb_len, tp->ai); + memmove(ait.lb, tp->lb, tp->ai); + ait.ai = ait.len = tp->ai; + + carat_st = C_NOCHANGE; + goto leftmargin; + case C_ZEROSET: /* 0^D */ + if (sp->cno > tp->ai + tp->offset + 1) + goto ins_ch; + carat_st = C_NOTSET; +leftmargin: tp->lb[sp->cno - 1] = ' '; + tp->owrite += sp->cno - tp->offset; + tp->ai = 0; + sp->cno = tp->offset; + break; + case C_NOTSET: /* ^D */ + if (sp->cno > tp->ai + tp->offset) + goto ins_ch; + (void)txt_outdent(sp, tp); + break; + default: + abort(); + } + break; + case K_VERASE: /* Erase the last character. */ + /* + * If can erase over the prompt, return. Len is 0 + * if backspaced over the prompt, 1 if only CR entered. + */ + if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) { + tp->len = 0; + goto ret; + } + + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + break; + } + + /* If nothing to erase, bell the user. */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* Drop back one character. */ + --sp->cno; + + /* + * Increment overwrite, decrement ai if deleted. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + ++tp->owrite; + if (sp->cno < tp->ai) + --tp->ai; + break; + case K_VINTR: + /* + * !!! + * Historically, <interrupt> exited the user from + * editing the infoline, and returned to the main + * screen. It also beeped the terminal, but that + * seems excessive. + */ + if (LF_ISSET(TXT_INFOLINE)) { + tp->lb[tp->len = 0] = '\0'; + goto ret; + } + goto ins_ch; + case K_VWERASE: /* Skip back one word. */ + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + } + + /* + * If at offset, nothing to erase so bell the user. + */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* + * First werase goes back to any autoindent + * and second werase goes back to the offset. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + if (tp->ai && sp->cno > tp->ai) + max = tp->ai; + else { + tp->ai = 0; + max = tp->offset; + } + + /* Skip over trailing space characters. */ + while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) { + --sp->cno; + ++tp->owrite; + } + if (sp->cno == max) + break; + /* + * There are three types of word erase found on UNIX + * systems. They can be identified by how the string + * /a/b/c is treated -- as 1, 3, or 6 words. Historic + * vi had two classes of characters, and strings were + * delimited by them and <blank>'s, so, 6 words. The + * historic tty interface used <blank>'s to delimit + * strings, so, 1 word. The algorithm offered in the + * 4.4BSD tty interface (as stty altwerase) treats it + * as 3 words -- there are two classes of characters, + * and strings are delimited by them and <blank>'s. + * The difference is that the type of the first erased + * character erased is ignored, which is exactly right + * when erasing pathname components. Here, the options + * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD + * tty interface and the historic tty driver behavior, + * respectively, and the default is the same as the + * historic vi behavior. + */ + if (LF_ISSET(TXT_TTYWERASE)) + while (sp->cno > max) { + --sp->cno; + ++tp->owrite; + if (isblank(tp->lb[sp->cno - 1])) + break; + } + else { + if (LF_ISSET(TXT_ALTWERASE)) { + --sp->cno; + ++tp->owrite; + if (isblank(tp->lb[sp->cno - 1])) + break; + } + if (sp->cno > max) + tmp = inword(tp->lb[sp->cno - 1]); + while (sp->cno > max) { + --sp->cno; + ++tp->owrite; + if (tmp != inword(tp->lb[sp->cno - 1]) + || isblank(tp->lb[sp->cno - 1])) + break; + } + } + break; + case K_VKILL: /* Restart this line. */ + /* + * If at the beginning of the line, try and drop back + * to a previously inserted line. + */ + if (sp->cno == 0) { + if ((ntp = txt_backup(sp, + ep, tiqh, tp, flags)) == NULL) + goto err; + tp = ntp; + } + + /* If at offset, nothing to erase so bell the user. */ + if (sp->cno <= tp->offset) { + msgq(sp, M_BERR, + "No more characters to erase."); + break; + } + + /* + * First kill goes back to any autoindent + * and second kill goes back to the offset. + * + * !!! + * Historic vi did not permit users to use erase + * characters to delete autoindent characters. + */ + if (tp->ai && sp->cno > tp->ai) + max = tp->ai; + else { + tp->ai = 0; + max = tp->offset; + } + tp->owrite += sp->cno - max; + sp->cno = max; + break; + case K_CNTRLT: /* Add autoindent char. */ + if (!LF_ISSET(TXT_CNTRLT)) + goto ins_ch; + if (txt_indent(sp, tp)) + goto err; + goto ebuf_chk; + case K_CNTRLZ: + (void)sp->s_suspend(sp); + break; +#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT + case K_FORMFEED: + F_SET(sp, S_REFRESH); + break; +#endif + case K_RIGHTBRACE: + case K_RIGHTPAREN: + showmatch = LF_ISSET(TXT_SHOWMATCH); + goto ins_ch; + case K_VLNEXT: /* Quote the next character. */ + /* If in hex mode, see if we've entered a hex value. */ + if (hex == H_INHEX) { + if (txt_hex(sp, tp, &tmp, ch)) + goto err; + if (tmp) { + hex = H_NOTSET; + goto next_ch; + } + } + ch = '^'; + quoted = Q_NEXTCHAR; + /* FALLTHROUGH */ + default: /* Insert the character. */ +ins_ch: /* + * If entering a space character after a word, check + * for abbreviations. If there was one, discard the + * replay characters. + */ + if (isblank(ch) && abb == A_NOTSPACE && !replay) { + if (txt_abbrev(sp, tp, ch, + LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) + goto err; + if (tmp) { + if (LF_ISSET(TXT_RECORD)) + rcol -= tmp; + goto next_ch; + } + } + /* If in hex mode, see if we've entered a hex value. */ + if (hex == H_INHEX && !isxdigit(ch)) { + if (txt_hex(sp, tp, &tmp, ch)) + goto err; + if (tmp) { + hex = H_NOTSET; + goto next_ch; + } + } + /* Check to see if we've crossed the margin. */ + if (margin) { + if (sp->s_column(sp, ep, &col)) + goto err; + if (col >= margin) { + if (txt_margin(sp, tp, &tmp, ch)) + goto err; + if (tmp) + goto next_ch; + } + } + if (abb != A_NOTSET) + abb = isblank(ch) ? A_SPACE : A_NOTSPACE; + + if (tp->owrite) /* Overwrite a character. */ + --tp->owrite; + else if (tp->insert) { /* Insert a character. */ + ++tp->len; + if (tp->insert == 1) + tp->lb[sp->cno + 1] = tp->lb[sp->cno]; + else + memmove(tp->lb + sp->cno + 1, + tp->lb + sp->cno, tp->insert); + } + + tp->lb[sp->cno++] = ch; + + /* + * If we've reached the end of the buffer, then we + * need to switch into insert mode. This happens + * when there's a change to a mark and the user puts + * in more characters than the length of the motion. + */ +ebuf_chk: if (sp->cno >= tp->len) { + TBINC(sp, tp->lb, tp->lb_len, tp->len + 1); + LF_SET(TXT_APPENDEOL); + tp->lb[sp->cno] = CURSOR_CH; + ++tp->insert; + ++tp->len; + } + + if (hex == H_NEXTCHAR) + hex = H_INHEX; + if (quoted == Q_NEXTCHAR) + quoted = Q_THISCHAR; + break; + } +#if defined(DEBUG) && 1 + if (sp->cno + tp->insert + tp->owrite != tp->len) + msgq(sp, M_ERR, + "len %u != cno: %u ai: %u insert %u overwrite %u", + tp->len, sp->cno, tp->ai, tp->insert, tp->owrite); + tp->len = sp->cno + tp->insert + tp->owrite; +#endif + } + + /* Clear input flag. */ +ret: F_CLR(sp, S_INPUT); + + if (LF_ISSET(TXT_RECORD)) + VIP(sp)->rep_cnt = rcol; + return (eval); + + /* Error jump. */ +err: eval = 1; + txt_err(sp, ep, tiqh); + goto ret; +} + +/* + * txt_abbrev -- + * Handle abbreviations. + */ +static int +txt_abbrev(sp, tp, pushc, isinfoline, didsubp, turnoffp) + SCR *sp; + TEXT *tp; + ARG_CHAR_T pushc; + int isinfoline, *didsubp, *turnoffp; +{ + CHAR_T ch; + SEQ *qp; + size_t len, off; + char *p; + + /* Find the beginning of this "word". */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (isblank(*p)) { + ++p; + break; + } + ++len; + if (off == tp->ai || off == tp->offset) + break; + } + + /* + * !!! + * Historic vi exploded abbreviations on the command line. This has + * obvious problems in that unabbreviating the string can be extremely + * tricky, particularly if the string has, say, an embedded escape + * character. Personally, I think it's a stunningly bad idea. Other + * examples of problems this caused in historic vi are: + * :ab foo bar + * :ab foo baz + * results in "bar" being abbreviated to "baz", which wasn't what the + * user had in mind at all. Also, the commands: + * :ab foo bar + * :unab foo<space> + * resulted in an error message that "bar" wasn't mapped. Finally, + * since the string was already exploded by the time the unabbreviate + * command got it, all it knew was that an abbreviation had occurred. + * Cleverly, it checked the replacement string for its unabbreviation + * match, which meant that the commands: + * :ab foo1 bar + * :ab foo2 bar + * :unab foo2 + * unabbreviates "foo1", and the commands: + * :ab foo bar + * :ab bar baz + * unabbreviates "foo"! + * + * Anyway, people neglected to first ask my opinion before they wrote + * macros that depend on this stuff, so, we make this work as follows. + * When checking for an abbreviation on the command line, if we get a + * string which is <blank> terminated and which starts at the beginning + * of the line, we check to see it is the abbreviate or unabbreviate + * commands. If it is, turn abbreviations off and return as if no + * abbreviation was found. Not also, minor trickiness, so that if the + * user erases the line and starts another command, we go ahead an turn + * abbreviations back on. + * + * This makes the layering look like a Nachos Supreme. + */ + *didsubp = 0; + if (isinfoline) + if (off == tp->ai || off == tp->offset) + if (ex_is_abbrev(p, len)) { + *turnoffp = 1; + return (0); + } else + *turnoffp = 0; + else + if (*turnoffp) + return (0); + + /* Check for any abbreviations. */ + if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) + return (0); + + /* + * Push the abbreviation onto the tty stack. Historically, characters + * resulting from an abbreviation expansion were themselves subject to + * map expansions, O_SHOWMATCH matching etc. This means the expanded + * characters will be re-tested for abbreviations. It's difficult to + * know what historic practice in this case was, since abbreviations + * were applied to :colon command lines, so entering abbreviations that + * looped was tricky, although possible. In addition, obvious loops + * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will + * silently only implement and/or display the last abbreviation.) + * + * This implementation doesn't recover well from such abbreviations. + * The main input loop counts abbreviated characters, and, when it + * reaches a limit, discards any abbreviated characters on the queue. + * It's difficult to back up to the original position, as the replay + * queue would have to be adjusted, and the line state when an initial + * abbreviated character was received would have to be saved. + */ + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_ABBREVIATED)) + return (1); + if (term_push(sp, qp->output, qp->olen, 0, CH_ABBREVIATED)) + return (1); + + /* + * Move the cursor to the start of the abbreviation, + * adjust the length. + */ + sp->cno -= len; + tp->len -= len; + + /* Copy any insert characters back. */ + if (tp->insert) + memmove(tp->lb + sp->cno + tp->owrite, + tp->lb + sp->cno + tp->owrite + len, tp->insert); + + /* + * We return the length of the abbreviated characters. This is so + * the calling routine can replace the replay characters with the + * abbreviation. This means that subsequent '.' commands will produce + * the same text, regardless of intervening :[un]abbreviate commands. + * This is historic practice. + */ + *didsubp = len; + return (0); +} + +/* Offset to next column of stop size. */ +#define STOP_OFF(c, stop) (stop - (c) % stop) + +/* + * txt_ai_resolve -- + * When a line is resolved by <esc> or <cr>, review autoindent + * characters. + */ +static void +txt_ai_resolve(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long ts; + int del; + size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; + char *p; + + /* + * If the line is empty, has an offset, or no autoindent + * characters, we're done. + */ + if (!tp->len || tp->offset || !tp->ai) + return; + + /* + * The autoindent characters plus any leading <blank> characters + * in the line are resolved into the minimum number of characters. + * Historic practice. + */ + ts = O_VAL(sp, O_TABSTOP); + + /* Figure out the last <blank> screen column. */ + for (p = tp->lb, scno = 0, len = tp->len, + spaces = tab_after_sp = 0; len-- && isblank(*p); ++p) + if (*p == '\t') { + if (spaces) + tab_after_sp = 1; + scno += STOP_OFF(scno, ts); + } else { + ++spaces; + ++scno; + } + + /* + * If there are no spaces, or no tabs after spaces and less than + * ts spaces, it's already minimal. + */ + if (!spaces || !tab_after_sp && spaces < ts) + return; + + /* Count up spaces/tabs needed to get to the target. */ + for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs) + cno += STOP_OFF(cno, ts); + spaces = scno - cno; + + /* + * Figure out how many characters we're dropping -- if we're not + * dropping any, it's already minimal, we're done. + */ + old = p - tp->lb; + new = spaces + tabs; + if (old == new) + return; + + /* Shift the rest of the characters down, adjust the counts. */ + del = old - new; + memmove(p - del, p, tp->len - old); + sp->cno -= del; + tp->len -= del; + + /* Fill in space/tab characters. */ + for (p = tp->lb; tabs--;) + *p++ = '\t'; + while (spaces--) + *p++ = ' '; +} + +/* + * txt_auto -- + * Handle autoindent. If aitp isn't NULL, use it, otherwise, + * retrieve the line. + */ +int +txt_auto(sp, ep, lno, aitp, len, tp) + SCR *sp; + EXF *ep; + recno_t lno; + size_t len; + TEXT *aitp, *tp; +{ + size_t nlen; + char *p, *t; + + if (aitp == NULL) { + if ((p = t = file_gline(sp, ep, lno, &len)) == NULL) + return (0); + } else + p = t = aitp->lb; + for (nlen = 0; len; ++p) { + if (!isblank(*p)) + break; + /* If last character is a space, it counts. */ + if (--len == 0) { + ++p; + break; + } + } + + /* No indentation. */ + if (p == t) + return (0); + + /* Set count. */ + nlen = p - t; + + /* Make sure the buffer's big enough. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen); + + /* Copy the indentation into the new buffer. */ + memmove(tp->lb + nlen, tp->lb, tp->len); + memmove(tp->lb, t, nlen); + tp->len += nlen; + + /* Return the additional length. */ + tp->ai = nlen; + return (0); +} + +/* + * txt_backup -- + * Back up to the previously edited line. + */ +static TEXT * +txt_backup(sp, ep, tiqh, tp, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + TEXT *tp; + u_int flags; +{ + TEXT *ntp; + recno_t lno; + size_t total; + + /* Get a handle on the previous TEXT structure. */ + if ((ntp = tp->q.cqe_prev) == (void *)tiqh) { + msgq(sp, M_BERR, "Already at the beginning of the insert"); + return (tp); + } + + /* Make sure that we have enough space. */ + total = ntp->len + tp->insert; + if (LF_ISSET(TXT_APPENDEOL)) + ++total; + if (total > ntp->lb_len && + binc(sp, &ntp->lb, &ntp->lb_len, total)) + return (NULL); + + /* + * Append a cursor or copy inserted bytes to the end of the old line. + * Test for appending a cursor first, because the TEXT insert field + * will be 1 if we're appending a cursor. I don't think there's a + * third case, so abort() if there is. + */ + if (LF_ISSET(TXT_APPENDEOL)) { + ntp->lb[ntp->len] = CURSOR_CH; + ntp->insert = 1; + } else if (tp->insert) { + memmove(ntp->lb + ntp->len, tp->lb + tp->owrite, tp->insert); + ntp->insert = tp->insert; + } else + abort(); + + /* Set bookkeeping information. */ + sp->lno = ntp->lno; + sp->cno = ntp->len; + ntp->len += ntp->insert; + + /* Release the current TEXT. */ + lno = tp->lno; + CIRCLEQ_REMOVE(tiqh, tp, q); + text_free(tp); + + /* Update the old line on the screen. */ + if (sp->s_change(sp, ep, lno, LINE_DELETE)) + return (NULL); + + /* Return the old line. */ + return (ntp); +} + +/* + * txt_err -- + * Handle an error during input processing. + */ +static void +txt_err(sp, ep, tiqh) + SCR *sp; + EXF *ep; + TEXTH *tiqh; +{ + recno_t lno; + size_t len; + + /* + * The problem with input processing is that the cursor is at an + * indeterminate position since some input may have been lost due + * to a malloc error. So, try to go back to the place from which + * the cursor started, knowing that it may no longer be available. + * + * We depend on at least one line number being set in the text + * chain. + */ + for (lno = tiqh->cqh_first->lno; + file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno); + + sp->lno = lno == 0 ? 1 : lno; + sp->cno = 0; + + /* Redraw the screen, just in case. */ + F_SET(sp, S_REDRAW); +} + +/* + * txt_hex -- + * Let the user insert any character value they want. + * + * !!! + * This is an extension. The pattern "^Vx[0-9a-fA-F]*" is a way + * for the user to specify a character value which their keyboard + * may not be able to enter. + */ +static int +txt_hex(sp, tp, was_hex, pushc) + SCR *sp; + TEXT *tp; + int *was_hex; + ARG_CHAR_T pushc; +{ + CHAR_T ch, savec; + size_t len, off; + u_long value; + char *p, *wp; + + /* + * Null-terminate the string. Since nul isn't a legal hex value, + * this should be okay, and lets us use a local routine, which + * presumably understands the character set, to convert the value. + */ + savec = tp->lb[sp->cno]; + tp->lb[sp->cno] = 0; + + /* Find the previous HEX_CH. */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (*p == HEX_CH) { + wp = p + 1; + break; + } + ++len; + /* If not on this line, there's nothing to do. */ + if (off == tp->ai || off == tp->offset) + goto nothex; + } + + /* If no length, then it wasn't a hex value. */ + if (len == 0) + goto nothex; + + /* Get the value. */ + value = strtol(wp, NULL, 16); + if (value == LONG_MIN || value == LONG_MAX || value > MAX_CHAR_T) { +nothex: tp->lb[sp->cno] = savec; + *was_hex = 0; + return (0); + } + + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED)) + return (1); + ch = value; + if (term_push(sp, &ch, 1, 0, CH_NOMAP | CH_QUOTED)) + return (1); + + tp->lb[sp->cno] = savec; + + /* Move the cursor to the start of the hex value, adjust the length. */ + sp->cno -= len + 1; + tp->len -= len + 1; + + /* Copy any insert characters back. */ + if (tp->insert) + memmove(tp->lb + sp->cno + tp->owrite, + tp->lb + sp->cno + tp->owrite + len + 1, tp->insert); + + *was_hex = 1; + return (0); +} + +/* + * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements + * to the next or previous shiftwidth value, i.e. for a 1-based numbering, + * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to + * the 10th column, and ^D moves it back. + * + * !!! + * The ^T and ^D characters in historical vi only had special meaning when + * they were the first characters typed after entering text input mode. + * Since normal erase characters couldn't erase autoindent (in this case + * ^T) characters, this meant that inserting text into previously existing + * text was quite strange, ^T only worked if it was the first keystroke, + * and then it could only be erased by using ^D. This implementation treats + * ^T specially anywhere it occurs in the input, and permits the standard + * erase characters to erase characters inserted using it. + * + * XXX + * Technically, txt_indent, txt_outdent should part of the screen interface, + * as they require knowledge of the size of a space character on the screen. + * (Not the size of tabs, because tabs are logically composed of spaces.) + * They're left in the text code because they're complicated, not to mention + * the gruesome awareness that if spaces aren't a single column on the screen + * for any language, we're into some serious, ah, for lack of a better word, + * "issues". + */ + +/* + * txt_indent -- + * Handle ^T indents. + */ +static int +txt_indent(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long sw, ts; + size_t cno, off, scno, spaces, tabs; + + ts = O_VAL(sp, O_TABSTOP); + sw = O_VAL(sp, O_SHIFTWIDTH); + + /* Get the current screen column. */ + for (off = scno = 0; off < sp->cno; ++off) + if (tp->lb[off] == '\t') + scno += STOP_OFF(scno, ts); + else + ++scno; + + /* Count up spaces/tabs needed to get to the target. */ + for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0; + cno + STOP_OFF(cno, ts) <= scno; ++tabs) + cno += STOP_OFF(cno, ts); + spaces = scno - cno; + + /* Put space/tab characters in place of any overwrite characters. */ + for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai) + tp->lb[sp->cno++] = '\t'; + for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai) + tp->lb[sp->cno++] = ' '; + + if (!tabs && !spaces) + return (0); + + /* Make sure there's enough room. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs); + + /* Move the insert characters out of the way. */ + if (tp->insert) + memmove(tp->lb + sp->cno + spaces + tabs, + tp->lb + sp->cno, tp->insert); + + /* Add new space/tab characters. */ + for (; tabs--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = '\t'; + for (; spaces--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = ' '; + return (0); +} + +/* + * txt_outdent -- + * Handle ^D outdents. + * + */ +static int +txt_outdent(sp, tp) + SCR *sp; + TEXT *tp; +{ + u_long sw, ts; + size_t cno, off, scno, spaces; + + ts = O_VAL(sp, O_TABSTOP); + sw = O_VAL(sp, O_SHIFTWIDTH); + + /* Get the current screen column. */ + for (off = scno = 0; off < sp->cno; ++off) + if (tp->lb[off] == '\t') + scno += STOP_OFF(scno, ts); + else + ++scno; + + /* Get the previous shiftwidth column. */ + for (cno = scno; --scno % sw != 0;); + + /* Decrement characters until less than or equal to that slot. */ + for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite) + if (tp->lb[--off] == '\t') + cno -= STOP_OFF(cno, ts); + else + --cno; + + /* Spaces needed to get to the target. */ + spaces = scno - cno; + + /* Maybe just a delete. */ + if (spaces == 0) + return (0); + + /* Make sure there's enough room. */ + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces); + + /* Use up any overwrite characters. */ + for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite) + tp->lb[sp->cno++] = ' '; + + /* Maybe that was enough. */ + if (spaces == 0) + return (0); + + /* Move the insert characters out of the way. */ + if (tp->insert) + memmove(tp->lb + sp->cno + spaces, + tp->lb + sp->cno, tp->insert); + + /* Add new space characters. */ + for (; spaces--; ++tp->len, ++tp->ai) + tp->lb[sp->cno++] = ' '; + return (0); +} + +/* + * txt_resolve -- + * Resolve the input text chain into the file. + */ +static int +txt_resolve(sp, ep, tiqh) + SCR *sp; + EXF *ep; + TEXTH *tiqh; +{ + TEXT *tp; + recno_t lno; + + /* The first line replaces a current line. */ + tp = tiqh->cqh_first; + if (file_sline(sp, ep, tp->lno, tp->lb, tp->len)) + return (1); + + /* All subsequent lines are appended into the file. */ + for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)&sp->tiq; ++lno) + if (file_aline(sp, ep, 0, lno, tp->lb, tp->len)) + return (1); + return (0); +} + +/* + * txt_showmatch -- + * Show a character match. + * + * !!! + * Historic vi tried to display matches even in the :colon command line. + * I think not. + */ +static void +txt_showmatch(sp, ep) + SCR *sp; + EXF *ep; +{ + struct timeval second; + VCS cs; + MARK m; + fd_set zero; + int cnt, endc, startc; + + /* + * Do a refresh first, in case the v_ntext() code hasn't done + * one in awhile, so the user can see what we're complaining + * about. + */ + if (sp->s_refresh(sp, ep)) + return; + /* + * We don't display the match if it's not on the screen. Find + * out what the first character on the screen is. + */ + if (sp->s_position(sp, ep, &m, 0, P_TOP)) + return; + + /* Initialize the getc() interface. */ + cs.cs_lno = sp->lno; + cs.cs_cno = sp->cno - 1; + if (cs_init(sp, ep, &cs)) + return; + startc = (endc = cs.cs_ch) == ')' ? '(' : '{'; + + /* Search for the match. */ + for (cnt = 1;;) { + if (cs_prev(sp, ep, &cs)) + return; + if (cs.cs_lno < m.lno || + cs.cs_lno == m.lno && cs.cs_cno < m.cno) + return; + if (cs.cs_flags != 0) { + if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) { + (void)sp->s_bell(sp); + return; + } + continue; + } + if (cs.cs_ch == endc) + ++cnt; + else if (cs.cs_ch == startc && --cnt == 0) + break; + } + + /* Move to the match. */ + m.lno = sp->lno; + m.cno = sp->cno; + sp->lno = cs.cs_lno; + sp->cno = cs.cs_cno; + (void)sp->s_refresh(sp, ep); + + /* + * Sleep(3) is eight system calls. Do it fast -- besides, + * I don't want to wait an entire second. + */ + FD_ZERO(&zero); + second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10; + second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L; + (void)select(0, &zero, &zero, &zero, &second); + + /* Return to the current location. */ + sp->lno = m.lno; + sp->cno = m.cno; + (void)sp->s_refresh(sp, ep); +} + +/* + * txt_margin -- + * Handle margin wrap. + * + * !!! + * Historic vi belled the user each time a character was entered after + * crossing the margin until a space was entered which could be used to + * break the line. I don't, it tends to wake the cats. + */ +static int +txt_margin(sp, tp, didbreak, pushc) + SCR *sp; + TEXT *tp; + int *didbreak; + ARG_CHAR_T pushc; +{ + CHAR_T ch; + size_t len, off, tlen; + char *p, *wp; + + /* Find the closest previous blank. */ + for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { + if (isblank(*p)) { + wp = p + 1; + break; + } + ++len; + /* If it's the beginning of the line, there's nothing to do. */ + if (off == tp->ai || off == tp->offset) { + *didbreak = 0; + return (0); + } + } + + /* + * Historic practice is to delete any trailing whitespace + * from the previous line. + */ + for (tlen = len;; --p, --off) { + if (!isblank(*p)) + break; + ++tlen; + if (off == tp->ai || off == tp->offset) + break; + } + + ch = pushc; + if (term_push(sp, &ch, 1, 0, CH_NOMAP)) + return (1); + if (len && term_push(sp, wp, len, 0, CH_NOMAP | CH_QUOTED)) + return (1); + ch = '\n'; + if (term_push(sp, &ch, 1, 0, CH_NOMAP)) + return (1); + + sp->cno -= tlen; + tp->owrite += tlen; + *didbreak = 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_paragraph.c b/usr.bin/vi/nvi/v_paragraph.c new file mode 100644 index 000000000000..00591cbe899e --- /dev/null +++ b/usr.bin/vi/nvi/v_paragraph.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_paragraph.c 8.4 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * Paragraphs are empty lines after text or values from the paragraph or + * section options. + */ + +/* + * v_paragraphf -- [count]} + * Move forward count paragraphs. + */ +int +v_paragraphf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { P_INTEXT, P_INBLANK } pstate; + size_t lastlen, len; + recno_t cnt, lastlno, lno; + char *p, *lp; + + /* Figure out what state we're currently in. */ + lno = fm->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) + goto eof; + + /* + * If we start in text, we want to switch states 2 * N - 1 + * times, in non-text, 2 * N times. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + cnt *= 2; + if (len == 0 || v_isempty(p, len)) + pstate = P_INBLANK; + else { + --cnt; + pstate = P_INTEXT; + } + + for (;;) { + lastlno = lno; + lastlen = len; + if ((p = file_gline(sp, ep, ++lno, &len)) == NULL) + goto eof; + switch (pstate) { + case P_INTEXT: + if (p[0] == '.' && len >= 2) + for (lp = VIP(sp)->paragraph; *lp; lp += 2) + if (lp[0] == p[1] && + (lp[1] == ' ' || lp[1] == p[2]) && + !--cnt) + goto found; + if (len == 0 || v_isempty(p, len)) { + if (!--cnt) + goto found; + pstate = P_INBLANK; + } + break; + case P_INBLANK: + if (len == 0 || v_isempty(p, len)) + break; + if (--cnt) { + pstate = P_INTEXT; + break; + } + /* + * Historically, a motion command was up to the end + * of the previous line, whereas the movement command + * was to the start of the new "paragraph". + */ +found: if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + rp->lno = lastlno; + rp->cno = lastlen ? lastlen + 1 : 0; + } else { + rp->lno = lno; + rp->cno = 0; + } + return (0); + default: + abort(); + } + } + + /* + * EOF is a movement sink, however, the } command historically + * moved to the end of the last line if repeatedly invoked. + */ +eof: if (fm->lno != lno - 1) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) + GETLINE_ERR(sp, fm->lno); + if (fm->cno != (len ? len - 1 : 0)) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + v_eof(sp, ep, NULL); + return (1); +} + +/* + * v_paragraphb -- [count]{ + * Move forward count paragraph. + */ +int +v_paragraphb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { P_INTEXT, P_INBLANK } pstate; + size_t len; + recno_t cnt, lno; + char *p, *lp; + + /* + * The { command historically moved to the beginning of the first + * line if invoked on the first line. + * + * Check for SOF. + */ + if (fm->lno <= 1) { + if (fm->cno == 0) { + v_sof(sp, NULL); + return (1); + } + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* Figure out what state we're currently in. */ + lno = fm->lno; + if ((p = file_gline(sp, ep, lno, &len)) == NULL) + goto sof; + + /* + * If we start in text, we want to switch states 2 * N - 1 + * times, in non-text, 2 * N times. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + cnt *= 2; + if (len == 0 || v_isempty(p, len)) + pstate = P_INBLANK; + else { + --cnt; + pstate = P_INTEXT; + } + + for (;;) { + if ((p = file_gline(sp, ep, --lno, &len)) == NULL) + goto sof; + switch (pstate) { + case P_INTEXT: + if (p[0] == '.' && len >= 2) + for (lp = VIP(sp)->paragraph; *lp; lp += 2) + if (lp[0] == p[1] && + (lp[1] == ' ' || lp[1] == p[2]) && + !--cnt) + goto found; + if (len == 0 || v_isempty(p, len)) { + if (!--cnt) + goto found; + pstate = P_INBLANK; + } + break; + case P_INBLANK: + if (len != 0 && !v_isempty(p, len)) { + if (!--cnt) { +found: rp->lno = lno; + rp->cno = 0; + return (0); + } + pstate = P_INTEXT; + } + break; + default: + abort(); + } + } + + /* SOF is a movement sink. */ +sof: rp->lno = 1; + rp->cno = 0; + return (0); +} + +/* + * v_buildparagraph -- + * Build the paragraph command search pattern. + */ +int +v_buildparagraph(sp) + SCR *sp; +{ + VI_PRIVATE *vip; + size_t p_len, s_len; + char *p, *p_p, *s_p; + + /* + * The vi paragraph command searches for either a paragraph or + * section option macro. + */ + p_len = (p_p = O_STR(sp, O_PARAGRAPHS)) == NULL ? 0 : strlen(p_p); + s_len = (s_p = O_STR(sp, O_SECTIONS)) == NULL ? 0 : strlen(s_p); + + if (p_len == 0 && s_len == 0) + return (0); + + MALLOC_RET(sp, p, char *, p_len + s_len + 1); + + vip = VIP(sp); + if (vip->paragraph != NULL) + FREE(vip->paragraph, vip->paragraph_len); + + if (p_p != NULL) + memmove(p, p_p, p_len + 1); + if (s_p != NULL) + memmove(p + p_len, s_p, s_len + 1); + vip->paragraph = p; + vip->paragraph_len = p_len + s_len + 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_put.c b/usr.bin/vi/nvi/v_put.c new file mode 100644 index 000000000000..60227ea4ccfd --- /dev/null +++ b/usr.bin/vi/nvi/v_put.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_put.c 8.6 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +static void inc_buf __P((SCR *, VICMDARG *)); + +/* + * v_Put -- [buffer]P + * Insert the contents of the buffer before the cursor. + */ +int +v_Put(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_ISDOT)) + inc_buf(sp, vp); + return (put(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 0)); +} + +/* + * v_put -- [buffer]p + * Insert the contents of the buffer after the cursor. + */ +int +v_put(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_ISDOT)) + inc_buf(sp, vp); + + return (put(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, rp, 1)); +} + +/* + * Historical whackadoo. The dot command `puts' the numbered buffer + * after the last one put. For example, `"4p.' would put buffer #4 + * and buffer #5. If the user continued to enter '.', the #9 buffer + * would be repeatedly output. This was not documented, and is a bit + * tricky to reconstruct. Historical versions of vi also dropped the + * contents of the default buffer after each put, so after `"4p' the + * default buffer would be empty. This makes no sense to me, so we + * don't bother. Don't assume sequential order of numeric characters. + * + * And, if that weren't exciting enough, failed commands don't normally + * set the dot command. Well, boys and girls, an exception is that + * the buffer increment gets done regardless of the success of the put. + */ +static void +inc_buf(sp, vp) + SCR *sp; + VICMDARG *vp; +{ + CHAR_T v; + + switch (vp->buffer) { + case '1': + v = '2'; + break; + case '2': + v = '3'; + break; + case '3': + v = '4'; + break; + case '4': + v = '5'; + break; + case '5': + v = '6'; + break; + case '6': + v = '7'; + break; + case '7': + v = '8'; + break; + case '8': + v = '9'; + break; + default: + return; + } + VIP(sp)->sdot.buffer = vp->buffer = v; +} diff --git a/usr.bin/vi/nvi/v_redraw.c b/usr.bin/vi/nvi/v_redraw.c new file mode 100644 index 000000000000..fca5ba33b713 --- /dev/null +++ b/usr.bin/vi/nvi/v_redraw.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_redraw.c 8.2 (Berkeley) 8/25/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_redraw -- + * Redraw the screen. + */ +int +v_redraw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + F_SET(sp, S_REFRESH); + return (0); +} diff --git a/usr.bin/vi/nvi/v_replace.c b/usr.bin/vi/nvi/v_replace.c new file mode 100644 index 000000000000..f9378e7ac4ed --- /dev/null +++ b/usr.bin/vi/nvi/v_replace.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_replace.c 8.12 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_replace -- [count]rc + * The r command in historic vi was almost beautiful in its badness. + * For example, "r<erase>" and "r<word erase>" beeped the terminal + * and deleted a single character. "Nr<carriage return>", where N + * was greater than 1, inserted a single carriage return. This may + * not be right, but at least it's not insane. + */ +int +v_replace(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + CH ikey; + TEXT *tp; + recno_t lno; + size_t blen, len; + u_long cnt; + int rval; + char *bp, *p; + + /* + * If the line doesn't exist, or it's empty, replacement isn't + * allowed. It's not hard to implement, but: + * + * 1: It's historic practice. + * 2: For consistency, this change would require that the more + * general case, "Nr", when the user is < N characters from + * the end of the line, also work. + * 3: Replacing a newline has somewhat odd semantics. + */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + goto nochar; + } + if (len == 0) { +nochar: msgq(sp, M_BERR, "No characters to replace"); + return (1); + } + + /* + * Figure out how many characters to be replace; for no particular + * reason other than that the semantics of replacing the newline + * are confusing, only permit the replacement of the characters in + * the current line. I suppose we could simply append the replacement + * characters to the line, but I see no compelling reason to do so. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + rp->cno = fm->cno + cnt - 1; + if (rp->cno > len - 1) { + v_eol(sp, ep, fm); + return (1); + } + + /* Get the character, literal escapes, escape terminates. */ + if (F_ISSET(vp, VC_ISDOT)) { + ikey.ch = VIP(sp)->rlast; + ikey.value = term_key_val(sp, ikey.ch); + } else { + if (term_key(sp, &ikey, 0) != INP_OK) + return (1); + switch (ikey.value) { + case K_ESCAPE: + *rp = *fm; + return (0); + case K_VLNEXT: + if (term_key(sp, &ikey, 0) != INP_OK) + return (1); + break; + } + VIP(sp)->rlast = ikey.ch; + } + + /* Copy the line. */ + GET_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, len); + p = bp; + + if (ikey.value == K_CR || ikey.value == K_NL) { + /* Set return line. */ + rp->lno = fm->lno + cnt; + + /* The first part of the current line. */ + if (file_sline(sp, ep, fm->lno, p, fm->cno)) + goto err_ret; + + /* + * The rest of the current line. And, of course, now it gets + * tricky. Any white space after the replaced character is + * stripped, and autoindent is applied. Put the cursor on the + * last indent character as did historic vi. + */ + for (p += fm->cno + cnt, len -= fm->cno + cnt; + len && isblank(*p); --len, ++p); + + if ((tp = text_init(sp, p, len, len)) == NULL) + goto err_ret; + if (txt_auto(sp, ep, fm->lno, NULL, 0, tp)) + goto err_ret; + rp->cno = tp->ai ? tp->ai - 1 : 0; + if (file_aline(sp, ep, 1, fm->lno, tp->lb, tp->len)) + goto err_ret; + text_free(tp); + + rval = 0; + + /* All of the middle lines. */ + while (--cnt) + if (file_aline(sp, ep, 1, fm->lno, "", 0)) { +err_ret: rval = 1; + break; + } + } else { + memset(bp + fm->cno, ikey.ch, cnt); + rval = file_sline(sp, ep, fm->lno, bp, len); + + rp->lno = fm->lno; + rp->cno = fm->cno + cnt - 1; + } + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_right.c b/usr.bin/vi/nvi/v_right.c new file mode 100644 index 000000000000..54b7be92d005 --- /dev/null +++ b/usr.bin/vi/nvi/v_right.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_right.c 8.3 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_right -- [count]' ', [count]l + * Move right by columns. + * + * Special case: the 'c' and 'd' commands can move past the end of line. + */ +int +v_right(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eol(sp, ep, NULL); + else + GETLINE_ERR(sp, fm->lno); + return (1); + } + + rp->lno = fm->lno; + if (len == 0 || fm->cno == len - 1) { + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) { + rp->cno = len; + return (0); + } + v_eol(sp, ep, NULL); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + rp->cno = fm->cno + cnt; + if (rp->cno > len - 1) + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + rp->cno = len; + else + rp->cno = len - 1; + return (0); +} + +/* + * v_dollar -- [count]$ + * Move to the last column. + */ +int +v_dollar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_long cnt; + + /* + * A count moves down count - 1 rows, so, "3$" is the same as "2j$". + * + * !!! + * Historically, if the $ is a motion, and deleting from at or before + * the first non-blank of the line, it's a line motion, and the line + * motion flag is set. + */ + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (cnt != 1) { + --vp->count; + if (v_down(sp, ep, vp, fm, tm, rp)) + return (1); + rp->cno = 0; + if (nonblank(sp, ep, rp->lno, &rp->cno)) + return (1); + if (fm->cno <= rp->cno && F_ISSET(vp, VC_C | VC_D | VC_Y)) + F_SET(vp, VC_LMODE); + } else + rp->lno = fm->lno; + + if (file_gline(sp, ep, rp->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eol(sp, ep, NULL); + else + GETLINE_ERR(sp, rp->lno); + return (1); + } + + if (len == 0) { + v_eol(sp, ep, NULL); + return (1); + } + + /* If it's a motion component, move one past the end of the line. */ + rp->cno = F_ISSET(vp, VC_C | VC_D | VC_Y) ? len : len - 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_screen.c b/usr.bin/vi/nvi/v_screen.c new file mode 100644 index 000000000000..986bfa4f29a2 --- /dev/null +++ b/usr.bin/vi/nvi/v_screen.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_screen.c 8.8 (Berkeley) 11/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_screen -- + * Switch screens. + */ +int +v_screen(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Try for the next lower screen, or, go back to the first + * screen on the stack. + */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) + sp->nextdisp = sp->q.cqe_next; + else if (sp->gp->dq.cqh_first == sp) { + msgq(sp, M_ERR, "No other screen to switch to."); + return (1); + } else + sp->nextdisp = sp->gp->dq.cqh_first; + + /* + * Display the old screen's status line so the user can + * find the screen they want. + */ + (void)status(sp, ep, fm->lno, 0); + + /* Save the old screen's cursor information. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + F_SET(sp, S_SSWITCH); + return (0); +} diff --git a/usr.bin/vi/nvi/v_scroll.c b/usr.bin/vi/nvi/v_scroll.c new file mode 100644 index 000000000000..f69ea0fb4e87 --- /dev/null +++ b/usr.bin/vi/nvi/v_scroll.c @@ -0,0 +1,354 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_scroll.c 8.8 (Berkeley) 12/16/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * The historic vi had a problem in that all movements were by physical + * lines, not by logical, or screen lines. Arguments can be made that this + * is the right thing to do. For example, single line movements, such as + * 'j' or 'k', should probably work on physical lines. Commands like "dj", + * or "j.", where '.' is a change command, make more sense for physical lines + * than they do for logical lines. + * + * These arguments, however, don't apply to scrolling commands like ^D and + * ^F -- if the window is fairly small, using physical lines can result in + * a half-page scroll repainting the entire screen, which is not what the + * user wanted. Second, if the line is larger than the screen, using physical + * lines can make it impossible to display parts of the line -- there aren't + * any commands that don't display the beginning of the line in historic vi, + * and if both the beginning and end of the line can't be on the screen at + * the same time, you lose. This is even worse in the case of the H, L, and + * M commands -- for large lines, they may all refer to the same line and + * will result in no movement at all. + * + * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the + * cursor positioning commands (H, L, M) commands using logical lines, not + * physical. + * + * Another issue is that page and half-page scrolling commands historically + * moved to the first non-blank character in the new line. If the line is + * approximately the same size as the screen, this loses because the cursor + * before and after a ^D, may refer to the same location on the screen. In + * this implementation, scrolling commands set the cursor to the first non- + * blank character if the line changes because of the scroll. Otherwise, + * the cursor is left alone. + */ + +/* + * v_lgoto -- [count]G + * Go to first non-blank character of the line count, the last line + * of the file by default. + */ +int +v_lgoto(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t last; + + if (file_lline(sp, ep, &last)) + return (1); + if (F_ISSET(vp, VC_C1SET)) { + if (last < vp->count) { + v_eof(sp, ep, fm); + return (1); + } + rp->lno = vp->count; + } else + rp->lno = last ? last : 1; + return (0); +} + +/* + * v_home -- [count]H + * Move to the first non-blank character of the logical line + * count from the top of the screen, 1 by default. + */ +int +v_home(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_position(sp, ep, rp, + F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_TOP)); +} + +/* + * v_middle -- M + * Move to the first non-blank character of the logical line + * in the middle of the screen. + */ +int +v_middle(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Yielding to none in our quest for compatibility with every + * historical blemish of vi, no matter how strange it might be, + * we permit the user to enter a count and then ignore it. + */ + return (sp->s_position(sp, ep, rp, 0, P_MIDDLE)); +} + +/* + * v_bottom -- [count]L + * Move to the first non-blank character of the logical line + * count from the bottom of the screen, 1 by default. + */ +int +v_bottom(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (sp->s_position(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 0, P_BOTTOM)); +} + +/* + * v_up -- [count]^P, [count]k, [count]- + * Move up by lines. + */ +int +v_up(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + + lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + if (fm->lno <= lno) { + v_sof(sp, fm); + return (1); + } + rp->lno = fm->lno - lno; + return (0); +} + +/* + * v_cr -- [count]^M + * In a script window, send the line to the shell. + * In a regular window, move down by lines. + */ +int +v_cr(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * If it's a script window, exec the line, + * otherwise it's the same as v_down(). + */ + return (F_ISSET(sp, S_SCRIPT) ? + sscr_exec(sp, ep, fm->lno) : v_down(sp, ep, vp, fm, tm, rp)); +} + +/* + * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+ + * Move down by lines. + */ +int +v_down(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + + lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1); + + if (file_gline(sp, ep, lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + rp->lno = lno; + return (0); +} + +/* + * v_hpageup -- [count]^U + * Page up half screens. + */ +int +v_hpageup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Half screens always succeed unless already at SOF. Half screens + * set the scroll value, even if the command ultimately failed, in + * historic vi. It's probably a don't care. + */ + if (F_ISSET(vp, VC_C1SET)) + O_VAL(sp, O_SCROLL) = vp->count; + else + vp->count = O_VAL(sp, O_SCROLL); + + return (sp->s_down(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1)); +} + +/* + * v_hpagedown -- [count]^D + * Page down half screens. + */ +int +v_hpagedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Half screens always succeed unless already at EOF. Half screens + * set the scroll value, even if the command ultimately failed, in + * historic vi. It's probably a don't care. + */ + if (F_ISSET(vp, VC_C1SET)) + O_VAL(sp, O_SCROLL) = vp->count; + else + vp->count = O_VAL(sp, O_SCROLL); + + return (sp->s_up(sp, ep, rp, (recno_t)O_VAL(sp, O_SCROLL), 1)); +} + +/* + * v_pageup -- [count]^B + * Page up full screens. + * + * !!! + * Historic vi did not move to the SOF if the screen couldn't move, i.e. + * if SOF was already displayed on the screen. This implementation does + * move to SOF in that case, making ^B more like the the historic ^U. + */ +int +v_pageup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t count; + + /* Calculation from POSIX 1003.2/D8. */ + count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1); + + return (sp->s_down(sp, ep, rp, count, 1)); +} + +/* + * v_pagedown -- [count]^F + * Page down full screens. + * !!! + * Historic vi did not move to the EOF if the screen couldn't move, i.e. + * if EOF was already displayed on the screen. This implementation does + * move to EOF in that case, making ^F more like the the historic ^D. + */ +int +v_pagedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t count; + + /* Calculation from POSIX 1003.2/D8. */ + count = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (sp->t_rows - 1); + + return (sp->s_up(sp, ep, rp, count, 1)); +} + +/* + * v_lineup -- [count]^Y + * Page up by lines. + */ +int +v_lineup(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * The cursor moves down, staying with its original line, unless it + * reaches the bottom of the screen. + */ + return (sp->s_down(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0)); +} + +/* + * v_linedown -- [count]^E + * Page down by lines. + */ +int +v_linedown(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * The cursor moves up, staying with its original line, unless it + * reaches the top of the screen. + */ + return (sp->s_up(sp, ep, + rp, F_ISSET(vp, VC_C1SET) ? vp->count : 1, 0)); +} diff --git a/usr.bin/vi/nvi/v_search.c b/usr.bin/vi/nvi/v_search.c new file mode 100644 index 000000000000..25dcaf5e2e10 --- /dev/null +++ b/usr.bin/vi/nvi/v_search.c @@ -0,0 +1,364 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_search.c 8.16 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static int bcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int)); +static int fcorrect __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, u_int)); +static int getptrn __P((SCR *, EXF *, int, char **)); + +/* + * v_searchn -- n + * Repeat last search. + */ +int +v_searchn(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + + flags = SEARCH_MSG; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + switch (sp->searchdir) { + case BACKWARD: + if (b_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case FORWARD: + if (f_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y| VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case NOTSET: + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + default: + abort(); + } + return (0); +} + +/* + * v_searchN -- N + * Reverse last search. + */ +int +v_searchN(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + + flags = SEARCH_MSG; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + switch (sp->searchdir) { + case BACKWARD: + if (f_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case FORWARD: + if (b_search(sp, ep, fm, rp, NULL, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + break; + case NOTSET: + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + default: + abort(); + } + return (0); +} + +/* + * v_searchw -- [count]^A + * Search for the word under the cursor. + */ +int +v_searchw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t blen, len; + u_int flags; + int rval; + char *bp; + + len = vp->kbuflen + sizeof(RE_WSTART) + sizeof(RE_WSTOP); + GET_SPACE_RET(sp, bp, blen, len); + (void)snprintf(bp, blen, "%s%s%s", RE_WSTART, vp->keyword, RE_WSTOP); + + flags = SEARCH_MSG; + rval = f_search(sp, ep, fm, rp, bp, NULL, &flags); + + FREE_SPACE(sp, bp, blen); + if (rval) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * v_searchb -- [count]?RE[? offset] + * Search backward. + */ +int +v_searchb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + char *ptrn; + + if (F_ISSET(vp, VC_ISDOT)) + ptrn = NULL; + else { + if (getptrn(sp, ep, '?', &ptrn)) + return (1); + if (ptrn == NULL) + return (0); + } + + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + if (b_search(sp, ep, fm, rp, ptrn, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + bcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * v_searchf -- [count]/RE[/ offset] + * Search forward. + */ +int +v_searchf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + int flags; + char *ptrn; + + if (F_ISSET(vp, VC_ISDOT)) + ptrn = NULL; + else { + if (getptrn(sp, ep, '/', &ptrn)) + return (1); + if (ptrn == NULL) + return (0); + } + + flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET | SEARCH_TERM; + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + flags |= SEARCH_EOL; + if (f_search(sp, ep, fm, rp, ptrn, NULL, &flags)) + return (1); + if (F_ISSET(vp, VC_C | VC_D | VC_Y | VC_SH) && + fcorrect(sp, ep, vp, fm, rp, flags)) + return (1); + return (0); +} + +/* + * getptrn -- + * Get the search pattern. + */ +static int +getptrn(sp, ep, prompt, storep) + SCR *sp; + EXF *ep; + int prompt; + char **storep; +{ + TEXT *tp; + + if (sp->s_get(sp, ep, &sp->tiq, prompt, + TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK) + return (1); + + /* Len is 0 if backspaced over the prompt, 1 if only CR entered. */ + tp = sp->tiq.cqh_first; + if (tp->len == 0) + *storep = NULL; + else + *storep = tp->lb; + return (0); +} + +/* + * !!! + * Historically, commands didn't affect the line searched to if the motion + * command was a search and the pattern match was the start or end of the + * line. There were some special cases, however, concerning search to the + * start of end of a line. + * + * Vi was not, however, consistent, and it was fairly easy to confuse it. + * For example, given the two lines: + * + * abcdefghi + * ABCDEFGHI + * + * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h' + * 'k' and put would no longer work correctly. In any case, we try to do + * the right thing, but it's not likely exactly match historic practice. + */ + +/* + * bcorrect -- + * Handle command with a backward search as the motion. + */ +static int +bcorrect(sp, ep, vp, fm, rp, flags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + u_int flags; +{ + size_t len; + char *p; + + /* + * !!! + * Correct backward searches which start at column 0 to be one + * past the last column of the previous line. + * + * Backward searches become line mode operations if they start + * at column 0 and end at column 0 of another line. + */ + if (fm->lno > rp->lno && fm->cno == 0) { + if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (rp->cno == 0) + F_SET(vp, VC_LMODE); + fm->cno = len; + } + + /* + * !!! + * Commands would become line mode operations if there was a delta + * specified to the search pattern. + */ + if (LF_ISSET(SEARCH_DELTA)) { + F_SET(vp, VC_LMODE); + return (0); + } + return (0); +} + +/* + * fcorrect -- + * Handle command with a forward search as the motion. + */ +static int +fcorrect(sp, ep, vp, fm, rp, flags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + u_int flags; +{ + size_t len; + char *p; + + /* + * !!! + * Correct forward searches which end at column 0 to be one + * past the last column of the previous line. + * + * Forward searches become line mode operations if they start + * at column 0 and end at column 0 of another line. + */ + if (fm->lno < rp->lno && rp->cno == 0) { + if ((p = file_gline(sp, ep, --rp->lno, &len)) == NULL) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + if (fm->cno == 0) + F_SET(vp, VC_LMODE); + rp->cno = len; + } + + /* + * !!! + * Commands would become line mode operations if there was a delta + * specified to the search pattern. + */ + if (LF_ISSET(SEARCH_DELTA)) { + F_SET(vp, VC_LMODE); + return (0); + } + + return (0); +} diff --git a/usr.bin/vi/nvi/v_section.c b/usr.bin/vi/nvi/v_section.c new file mode 100644 index 000000000000..8d97e2f10b4e --- /dev/null +++ b/usr.bin/vi/nvi/v_section.c @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_section.c 8.4 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * In historic vi, the section commands ignored empty lines, unlike the + * paragraph commands, which was probably okay. However, they also moved + * to the start of the last line when there where no more sections instead + * of the end of the last line like the paragraph commands. I've changed + * the latter behaviore to match the paragraphs command. + * + * In historic vi, a "function" was defined as the first character of the + * line being an open brace, which could be followed by anything. This + * implementation follows that historic practice. + */ + +/* Macro to do a check on each line. */ +#define CHECK { \ + if (len == 0) \ + continue; \ + if (p[0] == '{') { \ + if (!--cnt) { \ + rp->cno = 0; \ + rp->lno = lno; \ + return (0); \ + } \ + continue; \ + } \ + if (p[0] != '.' || len < 3) \ + continue; \ + for (lp = list; *lp; lp += 2) \ + if (lp[0] == p[1] && \ + (lp[1] == ' ' || lp[1] == p[2]) && !--cnt) { \ + rp->cno = 0; \ + rp->lno = lno; \ + return (0); \ + } \ +} + +/* + * v_sectionf -- [count]]] + * Move forward count sections/functions. + */ +int +v_sectionf(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t cnt, lno; + char *p, *list, *lp; + + /* Get macro list. */ + if ((list = O_STR(sp, O_SECTIONS)) == NULL) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + for (lno = fm->lno; (p = file_gline(sp, ep, ++lno, &len)) != NULL;) + CHECK; + + /* EOF is a movement sink. */ + if (fm->lno != lno - 1) { + rp->lno = lno - 1; + rp->cno = len ? len - 1 : 0; + return (0); + } + v_eof(sp, ep, NULL); + return (1); +} + +/* + * v_sectionb -- [count][[ + * Move backward count sections/functions. + */ +int +v_sectionb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + size_t len; + recno_t cnt, lno; + char *p, *list, *lp; + + /* Check for SOF. */ + if (fm->lno <= 1) { + v_sof(sp, NULL); + return (1); + } + + /* Get macro list. */ + if ((list = O_STR(sp, O_SECTIONS)) == NULL) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + for (lno = fm->lno; (p = file_gline(sp, ep, --lno, &len)) != NULL;) + CHECK; + + /* SOF is a movement sink. */ + rp->lno = 1; + rp->cno = 0; + return (0); +} diff --git a/usr.bin/vi/nvi/v_sentence.c b/usr.bin/vi/nvi/v_sentence.c new file mode 100644 index 000000000000..684952124f14 --- /dev/null +++ b/usr.bin/vi/nvi/v_sentence.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_sentence.c 8.7 (Berkeley) 8/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * In historic vi, a sentence was delimited by a '.', '?' or '!' character + * followed by TWO spaces or a newline. One or more empty lines was also + * treated as a separate sentence. The Berkeley documentation for historical + * vi states that any number of ')', ']', '"' and '\'' characters can be + * between the delimiter character and the spaces or end of line, however, + * the historical implementation did not handle additional '"' characters. + * We follow the documentation here, not the implementation. + * + * Once again, historical vi didn't do sentence movements associated with + * counts consistently, mostly in the presence of lines containing only + * white-space characters. + * + * This implementation also permits a single tab to delimit sentences, and + * treats lines containing only white-space characters as empty lines. + * And, tabs are eaten (along with spaces) when skipping to the start of the + * text follow a "sentence". + */ + +/* + * v_sentencef -- [count]) + * Move forward count sentences. + */ +int +v_sentencef(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + enum { BLANK, NONE, PERIOD } state; + VCS cs; + u_long cnt; + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If in white-space, the next start of sentence counts as one. + * This may not handle " . " correctly, but it's real unclear + * what correctly means in that case. + */ + if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) { + if (cs_fblank(sp, ep, &cs)) + return (1); + if (--cnt == 0) { + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + return (1); + } + } + for (state = NONE;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + break; + if (cs.cs_flags == CS_EOL) { + if ((state == PERIOD || state == BLANK) && --cnt == 0) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == 0 && + isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs)) + return (1); + goto okret; + } + state = NONE; + continue; + } + if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */ + if (--cnt == 0) + goto okret; + if (cs_fblank(sp, ep, &cs)) + return (1); + if (--cnt == 0) + goto okret; + state = NONE; + continue; + } + switch (cs.cs_ch) { + case '.': + case '?': + case '!': + state = PERIOD; + break; + case ')': + case ']': + case '"': + case '\'': + if (state != PERIOD) + state = NONE; + break; + case '\t': + if (state == PERIOD) + state = BLANK; + /* FALLTHROUGH */ + case ' ': + if (state == PERIOD) { + state = BLANK; + break; + } + if (state == BLANK && --cnt == 0) { + if (cs_fblank(sp, ep, &cs)) + return (1); + goto okret; + } + /* FALLTHROUGH */ + default: + state = NONE; + break; + } + } + + /* EOF is a movement sink. */ + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + v_eof(sp, ep, NULL); + return (1); + +okret: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * Historic, uh, features, yeah, that's right, call 'em features. + * If the sentence movement is cutting an entire line, the buffer + * is in line mode. Reach up into the caller's VICMDARG structure, + * and whack the flags. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && + fm->cno == 0 && (rp->cno == 0 || cs.cs_flags != 0)) { + if (rp->cno == 0) + --rp->lno; + F_SET(vp, VC_LMODE); + } + return (0); +} + +/* + * v_sentenceb -- [count]( + * Move backward count sentences. + */ +int +v_sentenceb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + VCS cs; + recno_t slno; + size_t len, scno; + u_long cnt; + int last1, last2; + + if (fm->lno == 1 && fm->cno == 0) { + v_sof(sp, NULL); + return (1); + } + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If on an empty line, skip to the next previous + * non-white-space character. + */ + if (cs.cs_flags == CS_EMP) { + if (cs_bblank(sp, ep, &cs)) + return (1); + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags != CS_EOL) + break; + } + } + + for (last1 = last2 = 0;;) { + if (cs_prev(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */ + break; + if (cs.cs_flags == CS_EOL) { + last2 = last1; + last1 = 1; + continue; + } + if (cs.cs_flags == CS_EMP) { + if (--cnt == 0) + goto ret; + if (cs_bblank(sp, ep, &cs)) + return (1); + last1 = last2 = 0; + continue; + } + switch (cs.cs_ch) { + case '.': + case '?': + case '!': + if (!last1 || --cnt != 0) { + last2 = last1 = 0; + continue; + } + +ret: slno = cs.cs_lno; + scno = cs.cs_cno; + + /* + * Move to the start of the sentence, skipping blanks + * and special characters. + */ + do { + if (cs_next(sp, ep, &cs)) + return (1); + } while (!cs.cs_flags && + (cs.cs_ch == ')' || cs.cs_ch == ']' || + cs.cs_ch == '"' || cs.cs_ch == '\'')); + if ((cs.cs_flags || isblank(cs.cs_ch)) && + cs_fblank(sp, ep, &cs)) + return (1); + + /* + * If it was ". xyz", with the cursor on the 'x', or + * "end. ", with the cursor in the spaces, or the + * beginning of a sentence preceded by an empty line, + * we can end up where we started. Fix it. + */ + if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno) + goto okret; + + /* + * Well, if an empty line preceded possible blanks + * and the sentence, it could be a real sentence. + */ + for (;;) { + if (cs_prev(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOL) + continue; + if (cs.cs_flags == 0 && isblank(cs.cs_ch)) + continue; + break; + } + if (cs.cs_flags == CS_EMP) + goto okret; + + /* But it wasn't; try again. */ + ++cnt; + cs.cs_lno = slno; + cs.cs_cno = scno; + last2 = last1 = 0; + break; + case '\t': + last1 = last2 = 1; + break; + default: + last2 = last1; + last1 = + cs.cs_flags == CS_EOL || isblank(cs.cs_ch) || + cs.cs_ch == ')' || cs.cs_ch == ']' || + cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0; + } + } + +okret: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + + /* + * See comment in v_sentencef(). Ignore errors, they should + * never occur, and they'll get caught later. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && rp->cno == 0 && + file_gline(sp, ep, fm->lno, &len) != NULL && (len == 0 || + fm->cno == len - 1)) + F_SET(vp, VC_LMODE); + return (0); +} diff --git a/usr.bin/vi/nvi/v_shift.c b/usr.bin/vi/nvi/v_shift.c new file mode 100644 index 000000000000..2e984147a1c1 --- /dev/null +++ b/usr.bin/vi/nvi/v_shift.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_shift.c 8.3 (Berkeley) 11/13/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_shiftl -- [count]<motion + * Shift lines left. + */ +int +v_shiftl(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SHIFTL, 2, fm->lno, tm->lno, 0, "<"); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} + +/* + * v_shiftr -- [count]>motion + * Shift lines right. + */ +int +v_shiftr(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_SHIFTR, 2, fm->lno, tm->lno, 0, ">"); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_status.c b/usr.bin/vi/nvi/v_status.c new file mode 100644 index 000000000000..9109c0196a80 --- /dev/null +++ b/usr.bin/vi/nvi/v_status.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_status.c 8.10 (Berkeley) 11/20/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_status -- ^G + * Show the file status. + */ +int +v_status(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + + /* + * ^G in historic vi reset the cursor column to the first + * non-blank character in the line. This doesn't seem of + * any usefulness whatsoever, so I don't bother. + */ + return (status(sp, ep, fm->lno, 1)); +} + +int +status(sp, ep, lno, showlast) + SCR *sp; + EXF *ep; + recno_t lno; + int showlast; +{ + recno_t last; + char *mo, *nc, *nf, *ro, *pid; +#ifdef DEBUG + char pbuf[50]; + + (void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid()); + pid = pbuf; +#else + pid = ""; +#endif + /* + * See nvi/exf.c:file_init() for a description of how and + * when the read-only bit is set. Possible displays are: + * + * new file + * new file, readonly + * [un]modified + * [un]modified, readonly + * name changed, [un]modified + * name changed, [un]modified, readonly + * + * !!! + * The historic display for "name changed" was "[Not edited]". + */ + if (F_ISSET(sp->frp, FR_NEWFILE)) { + F_CLR(sp->frp, FR_NEWFILE); + nf = "new file"; + mo = nc = ""; + } else { + nf = ""; + if (sp->frp->cname != NULL) { + nc = "name changed"; + mo = F_ISSET(ep, F_MODIFIED) ? + ", modified" : ", unmodified"; + } else { + nc = ""; + mo = F_ISSET(ep, F_MODIFIED) ? + "modified" : "unmodified"; + } + } + ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : ""; + if (showlast) { + if (file_lline(sp, ep, &last)) + return (1); + if (last >= 1) + msgq(sp, M_INFO, + "%s: %s%s%s%s: line %lu of %lu [%ld%%]%s", + FILENAME(sp->frp), nf, nc, mo, ro, lno, + last, (lno * 100) / last, pid); + else + msgq(sp, M_INFO, "%s: %s%s%s%s: empty file%s", + FILENAME(sp->frp), nf, nc, mo, ro, pid); + } else + msgq(sp, M_INFO, "%s: %s%s%s%s: line %lu%s", + FILENAME(sp->frp), nf, nc, mo, ro, lno, pid); + return (0); +} diff --git a/usr.bin/vi/nvi/v_stop.c b/usr.bin/vi/nvi/v_stop.c new file mode 100644 index 000000000000..2f64bfedf195 --- /dev/null +++ b/usr.bin/vi/nvi/v_stop.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_stop.c 8.5 (Berkeley) 10/28/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +int +v_stop(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* If autowrite is set, write out the file. */ + if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + if (sp->s_refresh(sp, ep)) + return (1); + } + return (sp->s_suspend(sp)); +} diff --git a/usr.bin/vi/nvi/v_switch.c b/usr.bin/vi/nvi/v_switch.c new file mode 100644 index 000000000000..d0d000facb2b --- /dev/null +++ b/usr.bin/vi/nvi/v_switch.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_switch.c 8.5 (Berkeley) 11/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" + +/* + * v_switch -- ^^ + * Switch to the previous file. + */ +int +v_switch(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + char *name; + + /* + * Try the alternate file name, then the previous file + * name. Use the real name, not the user's current name. + */ + if (sp->alt_name != NULL) + name = sp->alt_name; + else if (sp->p_frp != NULL) + name = sp->p_frp->name; + else { + msgq(sp, M_ERR, "No previous file to edit."); + return (1); + } + + /* If autowrite is set, write out the file. */ + if (F_ISSET(ep, F_MODIFIED)) + if (O_ISSET(sp, O_AUTOWRITE)) { + if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) + return (1); + } else { + msgq(sp, M_ERR, + "Modified since last write; write or use :edit! to override."); + return (1); + } + + SETCMDARG(cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, name); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_tag.c b/usr.bin/vi/nvi/v_tag.c new file mode 100644 index 000000000000..530e35279420 --- /dev/null +++ b/usr.bin/vi/nvi/v_tag.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_tag.c 8.2 (Berkeley) 12/3/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "excmd.h" +#include "vcmd.h" + +/* + * v_tagpush -- ^[ + * Do a tag search on a the cursor keyword. + */ +int +v_tagpush(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_TAG, 0, OOBLNO, 0, 0, vp->keyword); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} + +/* + * v_tagpop -- ^T + * Pop the tags stack. + */ +/* ARGSUSED */ +int +v_tagpop(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + EXCMDARG cmd; + + SETCMDARG(cmd, C_TAGPOP, 0, OOBLNO, 0, 0, NULL); + return (sp->s_ex_cmd(sp, ep, &cmd, rp)); +} diff --git a/usr.bin/vi/nvi/v_text.c b/usr.bin/vi/nvi/v_text.c new file mode 100644 index 000000000000..083b5b248e22 --- /dev/null +++ b/usr.bin/vi/nvi/v_text.c @@ -0,0 +1,827 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_text.c 8.23 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * !!! + * Repeated input in the historic vi is mostly wrong and this isn't very + * backward compatible. For example, if the user entered "3Aab\ncd" in + * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then + * appended to the result. There was also a hack which I don't remember + * right now, where "3o" would open 3 lines and then let the user fill them + * in, to make screen movements on 300 baud modems more tolerable. I don't + * think it's going to be missed. + */ + +#define SET_TXT_STD(sp, f) { \ + LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \ + TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \ + if (O_ISSET(sp, O_ALTWERASE)) \ + LF_SET(TXT_ALTWERASE); \ + if (O_ISSET(sp, O_AUTOINDENT)) \ + LF_SET(TXT_AUTOINDENT); \ + if (O_ISSET(sp, O_SHOWMATCH)) \ + LF_SET(TXT_SHOWMATCH); \ + if (O_ISSET(sp, O_WRAPMARGIN)) \ + LF_SET(TXT_WRAPMARGIN); \ + if (F_ISSET(sp, S_SCRIPT)) \ + LF_SET(TXT_CR); \ + if (O_ISSET(sp, O_TTYWERASE)) \ + LF_SET(TXT_TTYWERASE); \ +} + +/* + * !!! + * There's a problem with the way that we do logging for change commands with + * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the + * starting cursor position before the change command "moves" the cursor, the + * cursor position to which we return on undo will be where the user entered + * the change command, not the start of the change. Several of the following + * routines re-log the cursor to make this work correctly. Historic vi tried + * to do the same thing, and mostly got it right. (The only spectacular way + * it fails is if the user entered 'o' from anywhere but the last character of + * the line, the undo returned the cursor to the start of the line. If the + * user was on the last character of the line, the cursor returned to that + * position.) + */ + +static int v_CS __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *, u_int)); + +/* + * v_iA -- [count]A + * Append text to the end of the line. + */ +int +v_iA(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* Move the cursor to the end of the line + 1. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + } else { + /* Correct logging for implied cursor motion. */ + sp->cno = len == 0 ? 0 : len - 1; + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + /* Start the change after the line. */ + sp->cno = len; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_ia -- [count]a + * Append text to the cursor position. + */ +int +v_ia(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + u_int flags; + size_t len; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* + * Move the cursor one column to the right and + * repaint the screen. + */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + LF_SET(TXT_APPENDEOL); + } else if (len) { + if (len == sp->cno + 1) { + sp->cno = len; + LF_SET(TXT_APPENDEOL); + } else + ++sp->cno; + } else + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_iI -- [count]I + * Insert text at the first non-blank character in the line. + */ +int +v_iI(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + /* + * Move the cursor to the start of the line and repaint + * the screen. + */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, lno); + return (1); + } + lno = 1; + len = 0; + } else { + sp->cno = 0; + if (nonblank(sp, ep, lno, &sp->cno)) + return (1); + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + } + if (len == 0) + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_ii -- [count]i + * Insert text at the cursor position. + */ +int +v_ii(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (lno = fm->lno, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + lno = 1; + len = 0; + } + /* If len == sp->cno, it's a replay caused by a count. */ + if (len == 0 || len == sp->cno) + LF_SET(TXT_APPENDEOL); + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags)) + return (1); + + /* + * On replay, if the line isn't empty, advance the insert + * by one (make it an append). + */ + SET_TXT_STD(sp, TXT_REPLAY); + sp->lno = lno = rp->lno; + if ((sp->cno = rp->cno) != 0) + ++sp->cno; + } + return (0); +} + +/* + * v_iO -- [count]O + * Insert text above this line. + */ +int +v_iO(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t ai_line, lno; + size_t len; + u_long cnt; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if (sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + goto insert; + p = NULL; + len = 0; + ai_line = OOBLNO; + } else { +insert: p = ""; + sp->cno = 0; + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + if (file_iline(sp, ep, sp->lno, p, 0)) + return (1); + if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) { + GETLINE_ERR(sp, sp->lno); + return (1); + } + ai_line = sp->lno + 1; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, ai_line, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_io -- [count]o + * Insert text after this line. + */ +int +v_io(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t ai_line, lno; + size_t len; + u_long cnt; + u_int flags; + int first; + char *p; + + SET_TXT_STD(sp, TXT_APPENDEOL); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + for (first = 1, + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { + if (sp->lno == 1) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + goto insert; + p = NULL; + len = 0; + ai_line = OOBLNO; + } else { +insert: p = ""; + sp->cno = 0; + /* Correct logging for implied cursor motion. */ + if (first == 1) { + log_cursor(sp, ep); + first = 0; + } + len = 0; + if (file_aline(sp, ep, 1, sp->lno, p, len)) + return (1); + if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) { + GETLINE_ERR(sp, sp->lno); + return (1); + } + ai_line = sp->lno - 1; + } + + if (v_ntext(sp, ep, + &sp->tiq, NULL, p, len, rp, 0, ai_line, flags)) + return (1); + + SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY); + sp->lno = lno = rp->lno; + sp->cno = rp->cno; + } + return (0); +} + +/* + * v_Change -- [buffer][count]C + * Change line command. + */ +int +v_Change(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (v_CS(sp, ep, vp, fm, tm, rp, 0)); +} + +/* + * v_Subst -- [buffer][count]S + * Line substitute command. + */ +int +v_Subst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + u_int flags; + + /* + * The S command is the same as a 'C' command from the beginning + * of the line. This is hard to do in the parser, so do it here. + * + * If autoindent is on, the change is from the first *non-blank* + * character of the line, not the first character. And, to make + * it just a bit more exciting, the initial space is handled as + * auto-indent characters. + */ + LF_INIT(0); + if (O_ISSET(sp, O_AUTOINDENT)) { + fm->cno = 0; + if (nonblank(sp, ep, fm->lno, &fm->cno)) + return (1); + LF_SET(TXT_AICHARS); + } else + fm->cno = 0; + sp->cno = fm->cno; + return (v_CS(sp, ep, vp, fm, tm, rp, flags)); +} + +/* + * v_CS -- + * C and S commands. + */ +static int +v_CS(sp, ep, vp, fm, tm, rp, iflags) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; + u_int iflags; +{ + recno_t lno; + size_t len; + char *p; + u_int flags; + + SET_TXT_STD(sp, iflags); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + /* + * There are two cases -- if a count is supplied, we do a line + * mode change where we delete the lines and then insert text + * into a new line. Otherwise, we replace the current line. + */ + tm->lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0); + if (fm->lno != tm->lno) { + /* Make sure that the to line is real. */ + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + + /* Cut the lines. */ + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + + /* Insert a line while we still can... */ + if (file_iline(sp, ep, fm->lno, "", 0)) + return (1); + ++fm->lno; + ++tm->lno; + + /* Delete the lines. */ + if (delete(sp, ep, fm, tm, 1)) + return (1); + + /* Get the inserted line. */ + if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + tm = NULL; + sp->lno = fm->lno; + sp->cno = 0; + LF_SET(TXT_APPENDEOL); + } else { + /* The line may be empty, but that's okay. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, tm->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + tm->cno = len; + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + } + /* Correct logging for implied cursor motion. */ + log_cursor(sp, ep); + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); +} + +/* + * v_change -- [buffer][count]c[count]motion + * Change command. + */ +int +v_change(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t blen, len; + u_int flags; + int lmode, rval; + char *bp, *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + /* + * Move the cursor to the start of the change. Note, if autoindent + * is turned on, the cc command in line mode changes from the first + * *non-blank* character of the line, not the first character. And, + * to make it just a bit more exciting, the initial space is handled + * as auto-indent characters. + */ + lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0; + if (lmode) { + fm->cno = 0; + if (O_ISSET(sp, O_AUTOINDENT)) { + if (nonblank(sp, ep, fm->lno, &fm->cno)) + return (1); + LF_SET(TXT_AICHARS); + } + } + sp->lno = fm->lno; + sp->cno = fm->cno; + + /* Correct logging for implied cursor motion. */ + log_cursor(sp, ep); + + /* + * If changing within a single line, the line either currently has + * text or it doesn't. If it doesn't, just insert text. Otherwise, + * copy it and overwrite it. + */ + if (fm->lno == tm->lno) { + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (p == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, lmode)) + return (1); + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); + } + + /* + * It's trickier if changing over multiple lines. If we're in + * line mode we delete all of the lines and insert a replacement + * line which the user edits. If there was leading whitespace + * in the first line being changed, we copy it and use it as the + * replacement. If we're not in line mode, we just delete the + * text and start inserting. + * + * Copy the text. + */ + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, lmode)) + return (1); + + /* If replacing entire lines and there's leading text. */ + if (lmode && fm->cno) { + /* Get a copy of the first line changed. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + /* Copy the leading text elsewhere. */ + GET_SPACE_RET(sp, bp, blen, fm->cno); + memmove(bp, p, fm->cno); + } else + bp = NULL; + + /* Delete the text. */ + if (delete(sp, ep, fm, tm, lmode)) + return (1); + + /* If replacing entire lines, insert a replacement line. */ + if (lmode) { + if (file_iline(sp, ep, fm->lno, bp, fm->cno)) + return (1); + sp->lno = fm->lno; + len = sp->cno = fm->cno; + } + + /* Get the line we're editing. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + len = 0; + } + + /* Check to see if we're appending to the line. */ + if (fm->cno >= len) + LF_SET(TXT_APPENDEOL); + + /* No to mark. */ + tm = NULL; + + rval = v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags); + + if (bp != NULL) + FREE_SPACE(sp, bp, blen); + return (rval); +} + +/* + * v_Replace -- [count]R + * Overwrite multiple characters. + */ +int +v_Replace(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + u_long cnt; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, rp->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_OVERWRITE | TXT_REPLACE); + } + tm->lno = rp->lno; + tm->cno = len ? len : 0; + if (v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)) + return (1); + + /* + * Special case. The historic vi handled [count]R badly, in that R + * would replace some number of characters, and then the count would + * append count-1 copies of the replacing chars to the replaced space. + * This seems wrong, so this version counts R commands. There is some + * trickiness in moving back to where the user stopped replacing after + * each R command. Basically, if the user ended with a newline, we + * want to use rp->cno (which will be 0). Otherwise, use the column + * after the returned cursor, unless it would be past the end of the + * line, in which case we append to the line. + */ + while (--cnt) { + if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) + GETLINE_ERR(sp, rp->lno); + SET_TXT_STD(sp, TXT_REPLAY); + + sp->lno = rp->lno; + + if (len == 0 || rp->cno == len - 1) { + sp->cno = len; + LF_SET(TXT_APPENDEOL); + } else { + sp->cno = rp->cno; + if (rp->cno != 0) + ++sp->cno; + LF_SET(TXT_OVERWRITE | TXT_REPLACE); + } + + if (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)) + return (1); + } + return (0); +} + +/* + * v_subst -- [buffer][count]s + * Substitute characters. + */ +int +v_subst(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t len; + u_int flags; + char *p; + + SET_TXT_STD(sp, 0); + if (F_ISSET(vp, VC_ISDOT)) + LF_SET(TXT_REPLAY); + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + len = 0; + LF_SET(TXT_APPENDEOL); + } else { + if (len == 0) + LF_SET(TXT_APPENDEOL); + LF_SET(TXT_EMARK | TXT_OVERWRITE); + } + + tm->lno = fm->lno; + tm->cno = fm->cno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1); + if (tm->cno > len) + tm->cno = len; + + if (p != NULL && cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + + return (v_ntext(sp, ep, + &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags)); +} diff --git a/usr.bin/vi/nvi/v_ulcase.c b/usr.bin/vi/nvi/v_ulcase.c new file mode 100644 index 000000000000..12fd1c6626b9 --- /dev/null +++ b/usr.bin/vi/nvi/v_ulcase.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_ulcase.c 8.3 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_ulcase -- [count]~ + * Toggle upper & lower case letters. + * + * !!! + * In historic vi, the count was ignored. It would have been better + * if there had been an associated motion, but it's too late to change + * it now. + */ +int +v_ulcase(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t lno; + size_t blen, lcnt, len; + u_long cnt; + int ch, change, rval; + char *bp, *p; + + /* Figure out what memory to use. */ + GET_SPACE_RET(sp, bp, blen, 256); + + /* + * !!! + * Historic vi didn't permit ~ to cross newline boundaries. + * I can think of no reason why it shouldn't, which at least + * lets you auto-repeat through a paragraph. + */ + rval = 0; + for (change = -1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt;) { + /* Get the line; EOF is an infinite sink. */ + if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno >= fm->lno) { + GETLINE_ERR(sp, fm->lno); + rval = 1; + break; + } + if (change == -1) { + v_eof(sp, ep, NULL); + return (1); + } + break; + } + + /* Set current line number. */ + lno = fm->lno; + + /* Empty lines just decrement the count. */ + if (len == 0) { + --cnt; + ++fm->lno; + fm->cno = 0; + change = 0; + continue; + } + + /* Get a copy of the line. */ + ADD_SPACE_RET(sp, bp, blen, len); + memmove(bp, p, len); + + /* Set starting pointer. */ + if (change == -1) + p = bp + fm->cno; + else + p = bp; + + /* + * Figure out how many characters get changed in this + * line. Set the final cursor column. + */ + if (fm->cno + cnt >= len) { + lcnt = len - fm->cno; + ++fm->lno; + fm->cno = 0; + } else + fm->cno += lcnt = cnt; + cnt -= lcnt; + + /* Change the line. */ + for (change = 0; lcnt--; ++p) { + ch = *(u_char *)p; + if (islower(ch)) { + *p = toupper(ch); + change = 1; + } else if (isupper(ch)) { + *p = tolower(ch); + change = 1; + } + } + + /* Update the line if necessary. */ + if (change && file_sline(sp, ep, lno, bp, len)) { + rval = 1; + break; + } + } + + /* If changed lines, could be on an illegal line. */ + if (fm->lno != lno && file_gline(sp, ep, fm->lno, &len) == NULL) { + --fm->lno; + fm->cno = len ? len - 1 : 0; + } + *rp = *fm; + + FREE_SPACE(sp, bp, blen); + return (rval); +} diff --git a/usr.bin/vi/nvi/v_undo.c b/usr.bin/vi/nvi/v_undo.c new file mode 100644 index 000000000000..87c749b38179 --- /dev/null +++ b/usr.bin/vi/nvi/v_undo.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_undo.c 8.6 (Berkeley) 1/8/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Undo -- U + * Undo changes to this line. + */ +int +v_Undo(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* + * Historically, U reset the cursor to the first column in the line + * (not the first non-blank). This seems a bit non-intuitive, but, + * considering that we may have undone multiple changes, anything + * else (including the cursor position stored in the logging records) + * is going to appear random. + */ + rp->lno = fm->lno; + rp->cno = 0; + + /* + * !!! + * Set up the flags so that an immediately subsequent 'u' will roll + * forward, instead of backward. In historic vi, a 'u' following a + * 'U' redid all of the changes to the line. Given that the user has + * explicitly discarded those changes by entering 'U', it seems likely + * that the user wants something between the original and end forms of + * the line, so starting to replay the changes seems the best way to + * get to there. + */ + F_SET(ep, F_UNDO); + ep->lundo = BACKWARD; + + return (log_setline(sp, ep)); +} + +/* + * v_undo -- u + * Undo the last change. + */ +int +v_undo(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + /* Set the command count. */ + VIP(sp)->u_ccnt = sp->ccnt; + + /* + * !!! + * In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u' + * undid the last undo. However, if there has been a change since + * the last undo/redo, we always do an undo. To make this work when + * the user can undo multiple operations, we leave the old semantic + * unchanged, but make '.' after a 'u' do another undo/redo operation. + * This has two problems. + * + * The first is that 'u' didn't set '.' in historic vi. So, if a + * user made a change, realized it was in the wrong place, does a + * 'u' to undo it, moves to the right place and then does '.', the + * change was reapplied. To make this work, we only apply the '.' + * to the undo command if it's the command immediately following an + * undo command. See vi/vi.c:getcmd() for the details. + * + * The second is that the traditional way to view the numbered cut + * buffers in vi was to enter the commands "1pu.u.u.u. which will + * no longer work because the '.' immediately follows the 'u' command. + * Since we provide a much better method of viewing buffers, and + * nobody can think of a better way of adding in multiple undo, this + * remains broken. + */ + if (!F_ISSET(ep, F_UNDO)) { + F_SET(ep, F_UNDO); + ep->lundo = BACKWARD; + } else if (!F_ISSET(vp, VC_ISDOT)) + ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD; + + switch (ep->lundo) { + case BACKWARD: + return (log_backward(sp, ep, rp)); + case FORWARD: + return (log_forward(sp, ep, rp)); + default: + abort(); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/nvi/v_util.c b/usr.bin/vi/nvi/v_util.c new file mode 100644 index 000000000000..83c4fb4ebbf6 --- /dev/null +++ b/usr.bin/vi/nvi/v_util.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_util.c 8.5 (Berkeley) 11/15/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_eof -- + * Vi end-of-file error. + */ +void +v_eof(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + u_long lno; + + if (mp == NULL) + msgq(sp, M_BERR, "Already at end-of-file."); + else { + if (file_lline(sp, ep, &lno)) + return; + if (mp->lno >= lno) + msgq(sp, M_BERR, "Already at end-of-file."); + else + msgq(sp, M_BERR, + "Movement past the end-of-file."); + } +} + +/* + * v_eol -- + * Vi end-of-line error. + */ +void +v_eol(sp, ep, mp) + SCR *sp; + EXF *ep; + MARK *mp; +{ + size_t len; + + if (mp == NULL) + msgq(sp, M_BERR, "Already at end-of-line."); + else { + if (file_gline(sp, ep, mp->lno, &len) == NULL) { + GETLINE_ERR(sp, mp->lno); + return; + } + if (mp->cno == len - 1) + msgq(sp, M_BERR, "Already at end-of-line."); + else + msgq(sp, M_BERR, "Movement past the end-of-line."); + } +} + +/* + * v_sof -- + * Vi start-of-file error. + */ +void +v_sof(sp, mp) + SCR *sp; + MARK *mp; +{ + if (mp == NULL || mp->lno == 1) + msgq(sp, M_BERR, "Already at the beginning of the file."); + else + msgq(sp, M_BERR, "Movement past the beginning of the file."); +} + +/* + * v_isempty -- + * Return if the line contains nothing but white-space characters. + */ +int +v_isempty(p, len) + char *p; + size_t len; +{ + for (; len--; ++p) + if (!isblank(*p)) + return (0); + return (1); +} diff --git a/usr.bin/vi/nvi/v_word.c b/usr.bin/vi/nvi/v_word.c new file mode 100644 index 000000000000..8d917d68e088 --- /dev/null +++ b/usr.bin/vi/nvi/v_word.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_word.c 8.10 (Berkeley) 10/26/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * There are two types of "words". Bigwords are easy -- groups of anything + * delimited by whitespace. Normal words are trickier. They are either a + * group of characters, numbers and underscores, or a group of anything but, + * delimited by whitespace. When for a word, if you're in whitespace, it's + * easy, just remove the whitespace and go to the beginning or end of the + * word. Otherwise, figure out if the next character is in a different group. + * If it is, go to the beginning or end of that group, otherwise, go to the + * beginning or end of the current group. The historic version of vi didn't + * get this right, so, for example, there were cases where "4e" was not the + * same as "eeee". To get it right you have to resolve the cursor after each + * search so that the look-ahead to figure out what type of "word" the cursor + * is in will be correct. + * + * Empty lines, and lines that consist of only white-space characters count + * as a single word, and the beginning and end of the file counts as an + * infinite number of words. + * + * Movements associated with commands are different than movement commands. + * For example, in "abc def", with the cursor on the 'a', "cw" is from + * 'a' to 'c', while "w" is from 'a' to 'd'. In general, trailing white + * space is discarded from the change movement. Another example is that, + * in the same string, a "cw" on any white space character replaces that + * single character, and nothing else. Ain't nothin' in here that's easy. + * + * One historic note -- in the original vi, the 'w', 'W' and 'B' commands + * would treat groups of empty lines as individual words, i.e. the command + * would move the cursor to each new empty line. The 'e' and 'E' commands + * would treat groups of empty lines as a single word, i.e. the first use + * would move past the group of lines. The 'b' command would just beep at + * you. If the lines contained only white-space characters, the 'w' and 'W' + * commands will just beep at you, and the 'B', 'b', 'E' and 'e' commands + * will treat the group as a single word, and the 'B' and 'b' commands will + * treat the lines as individual words. This implementation treats both + * cases as a single white-space word. + */ + +#define FW(test) for (; len && (test); --len, ++p) +#define BW(test) for (; len && (test); --len, --p) + +enum which {BIGWORD, LITTLEWORD}; + +static int bword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int)); +static int eword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, int)); +static int fword __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, enum which)); + +/* + * v_wordw -- [count]w + * Move forward a word at a time. + */ +int +v_wordw(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (fword(sp, ep, vp, fm, rp, LITTLEWORD)); +} + +/* + * v_wordW -- [count]W + * Move forward a bigword at a time. + */ +int +v_wordW(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (fword(sp, ep, vp, fm, rp, BIGWORD)); +} + +/* + * fword -- + * Move forward by words. + */ +static int +fword(sp, ep, vp, fm, rp, type) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + enum which type; +{ + enum { INWORD, NOTWORD } state; + VCS cs; + u_long cnt; + + cs.cs_lno = fm->lno; + cs.cs_cno = fm->cno; + if (cs_init(sp, ep, &cs)) + return (1); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * If in white-space: + * If the count is 1, and it's a change command, we're done. + * Else, move to the first non-white-space character, which + * counts as a single word move. If it's a motion command, + * don't move off the end of the line. + */ + if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) { + if (cs.cs_flags != CS_EMP && cnt == 1) { + if (F_ISSET(vp, VC_C)) { + ++cs.cs_cno; + goto ret3; + } + if (F_ISSET(vp, VC_D | VC_Y)) { + if (cs_fspace(sp, ep, &cs)) + return (1); + goto ret1; + } + } + if (cs_fblank(sp, ep, &cs)) + return (1); + --cnt; + } + + /* + * Cyclically move to the next word -- this involves skipping + * over word characters and then any trailing non-word characters. + * Note, for the 'w' command, the definition of a word keeps + * switching. + */ + if (type == BIGWORD) + while (cnt--) { + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + break; + } + /* + * If a motion command and we're at the end of the + * last word, we're done. Delete and yank eat any + * trailing blanks, but we don't move off the end + * of the line regardless. + */ + if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (F_ISSET(vp, VC_D | VC_Y) && + cs_fspace(sp, ep, &cs)) + return (1); + break; + } + + /* Eat whitespace characters. */ + if (cs_fblank(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + } + else + while (cnt--) { + state = cs.cs_flags == 0 && + inword(cs.cs_ch) ? INWORD : NOTWORD; + for (;;) { + if (cs_next(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + break; + if (state == INWORD) { + if (!inword(cs.cs_ch)) + break; + } else + if (inword(cs.cs_ch)) + break; + } + /* See comment above. */ + if (cnt == 0 && F_ISSET(vp, VC_C | VC_D | VC_Y)) { + if (F_ISSET(vp, VC_D | VC_Y) && + cs_fspace(sp, ep, &cs)) + return (1); + break; + } + + /* Eat whitespace characters. */ + if (cs.cs_flags != 0 || isblank(cs.cs_ch)) + if (cs_fblank(sp, ep, &cs)) + return (1); + if (cs.cs_flags == CS_EOF) + goto ret2; + } + + /* + * If a motion command, and eating the trailing non-word would + * move us off this line, don't do it. Move the return cursor + * to one past the EOL instead. + */ +ret1: if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOL) + ++cs.cs_cno; + + /* If we didn't move, we must be at EOF. */ +ret2: if (cs.cs_lno == fm->lno && cs.cs_cno == fm->cno) { + v_eof(sp, ep, fm); + return (1); + } + /* + * If at EOF, and it's a motion command, move the return cursor + * one past the EOF. + */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y) && cs.cs_flags == CS_EOF) + ++cs.cs_cno; +ret3: rp->lno = cs.cs_lno; + rp->cno = cs.cs_cno; + return (0); +} + +/* + * v_wordb -- [count]b + * Move backward a word at a time. + */ +int +v_wordb(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (bword(sp, ep, vp, fm, rp, 0)); +} + +/* + * v_WordB -- [count]B + * Move backward a bigword at a time. + */ +int +v_wordB(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (bword(sp, ep, vp, fm, rp, 1)); +} + +/* + * bword -- + * Move backward by words. + */ +static int +bword(sp, ep, vp, fm, rp, spaceonly) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + int spaceonly; +{ + register char *p; + recno_t lno; + size_t len; + u_long cno, cnt; + char *startp; + + lno = fm->lno; + cno = fm->cno; + + /* Check for start of file. */ + if (lno == 1 && cno == 0) { + v_sof(sp, NULL); + return (1); + } + + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_sof(sp, NULL); + else + GETLINE_ERR(sp, lno); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Reset the length to the number of characters in the line; the + * first character is the current cursor position. + */ + len = cno ? cno + 1 : 0; + if (len == 0) + goto line; + for (startp = p, p += cno; cnt--;) { + if (spaceonly) { + if (!isblank(*p)) { + if (len < 2) + goto line; + --p; + --len; + } + BW(isblank(*p)); + if (len) + BW(!isblank(*p)); + else + goto line; + } else { + if (!isblank(*p)) { + if (len < 2) + goto line; + --p; + --len; + } + BW(isblank(*p)); + if (len) + if (inword(*p)) + BW(inword(*p)); + else + BW(!isblank(*p) && !inword(*p)); + else + goto line; + } + + if (cnt && len == 0) { + /* If we hit SOF, stay there (historic practice). */ +line: if (lno == 1) { + rp->lno = 1; + rp->cno = 0; + return (0); + } + + /* + * Get the line. If the line is empty, decrement + * count and get another one. + */ + if ((p = file_gline(sp, ep, --lno, &len)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + if (len == 0) { + if (cnt == 0 || --cnt == 0) { + rp->lno = lno; + rp->cno = 0; + return (0); + } + goto line; + } + + /* + * Set the cursor to the end of the line. If the word + * at the end of this line has only a single character, + * we've already skipped over it. + */ + startp = p; + if (len) { + p += len - 1; + if (cnt && len > 1 && !isblank(p[0])) + if (inword(p[0])) { + if (!inword(p[-1])) + --cnt; + } else if (!isblank(p[-1]) && + !inword(p[-1])) + --cnt; + } + } else { + ++p; + ++len; + } + } + rp->lno = lno; + rp->cno = p - startp; + return (0); +} + +/* + * v_worde -- [count]e + * Move forward to the end of the word. + */ +int +v_worde(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (eword(sp, ep, vp, fm, rp, 0)); +} + +/* + * v_wordE -- [count]E + * Move forward to the end of the bigword. + */ +int +v_wordE(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + return (eword(sp, ep, vp, fm, rp, 1)); +} + +/* + * eword -- + * Move forward to the end of the word. + */ +static int +eword(sp, ep, vp, fm, rp, spaceonly) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *rp; + int spaceonly; +{ + register char *p; + recno_t lno; + size_t len, llen; + u_long cno, cnt; + int empty; + char *startp; + + lno = fm->lno; + cno = fm->cno; + + if ((p = file_gline(sp, ep, lno, &llen)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eof(sp, ep, NULL); + else + GETLINE_ERR(sp, lno); + return (1); + } + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Reset the length; the first character is the current cursor + * position. If no more characters in this line, may already + * be at EOF. + */ + len = llen - cno; + if (empty = llen == 0 || llen == cno + 1) + goto line; + + for (startp = p += cno; cnt--; empty = 0) { + if (spaceonly) { + if (!isblank(*p)) { + if (len < 2) + goto line; + ++p; + --len; + } + FW(isblank(*p)); + if (len) + FW(!isblank(*p)); + else + ++cnt; + } else { + if (!isblank(*p)) { + if (len < 2) + goto line; + ++p; + --len; + } + FW(isblank(*p)); + if (len) + if (inword(*p)) + FW(inword(*p)); + else + FW(!isblank(*p) && !inword(*p)); + else + ++cnt; + } + + if (cnt && len == 0) { + /* If we hit EOF, stay there (historic practice). */ +line: if ((p = file_gline(sp, ep, ++lno, &llen)) == NULL) { + /* + * If already at eof, complain, unless it's + * a change command or a delete command and + * there's something to delete. + */ + if (empty) { + if (F_ISSET(vp, VC_C) || + F_ISSET(vp, VC_D) && llen != 0) { + rp->lno = lno - 1; + rp->cno = llen ? llen : 1; + return (0); + } + v_eof(sp, ep, NULL); + return (1); + } + if ((p = + file_gline(sp, ep, --lno, &llen)) == NULL) { + GETLINE_ERR(sp, lno); + return (1); + } + rp->lno = lno; + rp->cno = llen ? llen - 1 : 0; + /* The 'c', 'd' and 'y' need one more space. */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); + } + len = llen; + cno = 0; + startp = p; + } else { + --p; + ++len; + } + } + rp->lno = lno; + rp->cno = cno + (p - startp); + + /* The 'c', 'd' and 'y' need one more space. */ + if (F_ISSET(vp, VC_C | VC_D | VC_Y)) + ++rp->cno; + return (0); +} diff --git a/usr.bin/vi/nvi/v_xchar.c b/usr.bin/vi/nvi/v_xchar.c new file mode 100644 index 000000000000..019862240f26 --- /dev/null +++ b/usr.bin/vi/nvi/v_xchar.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_xchar.c 8.4 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +#define NODEL(sp) { \ + msgq(sp, M_BERR, "No characters to delete."); \ + return (1); \ +} + +/* + * v_xchar -- + * Deletes the character(s) on which the cursor sits. + */ +int +v_xchar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + MARK m; + recno_t lno; + u_long cnt; + size_t len; + + if (file_gline(sp, ep, fm->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + NODEL(sp); + GETLINE_ERR(sp, fm->lno); + return (1); + } + + if (len == 0) + NODEL(sp); + + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + + /* + * Deleting from the cursor toward the end of line, w/o moving the + * cursor. Note, "2x" at EOL isn't the same as "xx" because the + * left movement of the cursor as part of the 'x' command isn't + * taken into account. Historically correct. + */ + tm->lno = fm->lno; + if (cnt < len - fm->cno) { + tm->cno = fm->cno + cnt; + m = *fm; + } else { + tm->cno = len; + m.lno = fm->lno; + m.cno = fm->cno ? fm->cno - 1 : 0; + } + + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + *rp = m; + return (0); +} + +/* + * v_Xchar -- + * Deletes the character(s) immediately before the current cursor + * position. + */ +int +v_Xchar(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + u_long cnt; + + if (fm->cno == 0) { + msgq(sp, M_BERR, "Already at the left-hand margin."); + return (1); + } + + *tm = *fm; + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + fm->cno = cnt >= tm->cno ? 0 : tm->cno - cnt; + + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + if (delete(sp, ep, fm, tm, 0)) + return (1); + + *rp = *fm; + return (0); +} diff --git a/usr.bin/vi/nvi/v_yank.c b/usr.bin/vi/nvi/v_yank.c new file mode 100644 index 000000000000..7b2718e647a4 --- /dev/null +++ b/usr.bin/vi/nvi/v_yank.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_yank.c 8.11 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_Yank -- [buffer][count]Y + * Yank lines of text into a cut buffer. + */ +int +v_Yank(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + if (cut(sp, ep, NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + + sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1; + return (0); +} + +/* + * v_yank -- [buffer][count]y[count][motion] + * Yank text (or lines of text) into a cut buffer. + */ +int +v_yank(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + if (F_ISSET(vp, VC_LMODE)) { + if (file_gline(sp, ep, tm->lno, NULL) == NULL) { + v_eof(sp, ep, fm); + return (1); + } + if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + fm, tm, CUT_LINEMODE)) + return (1); + } else if (cut(sp, ep, + NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0)) + return (1); + + /* + * !!! + * Historic vi moved the cursor to the from MARK if it was before the + * current cursor. This makes no sense. For example, "yj" moves the + * cursor but "yk" does not. Unfortunately, it's too late to change + * this now. Matching the historic semantics isn't easy. The line + * number was always changed and column movement was usually relative. + * However, "y'a" moved the cursor to the first non-blank of the line + * marked by a, while "y`a" moved the cursor to the line and column + * marked by a. + */ + if (F_ISSET(vp, VC_REVMOVE)) { + rp->lno = fm->lno; + if (vp->mkp == &vikeys['\'']) { + rp->cno = 0; + (void)nonblank(sp, ep, rp->lno, &rp->cno); + } else if (vp->mkp == &vikeys['`']) + rp->cno = fm->cno; + else + rp->cno = sp->s_relative(sp, ep, rp->lno); + } + + sp->rptlines[L_YANKED] += (tm->lno - fm->lno) + 1; + return (0); +} diff --git a/usr.bin/vi/nvi/v_z.c b/usr.bin/vi/nvi/v_z.c new file mode 100644 index 000000000000..31937ffc951c --- /dev/null +++ b/usr.bin/vi/nvi/v_z.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)v_z.c 8.8 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * v_z -- [count]z[count][-.+^<CR>] + * Move the screen. + */ +int +v_z(sp, ep, vp, fm, tm, rp) + SCR *sp; + EXF *ep; + VICMDARG *vp; + MARK *fm, *tm, *rp; +{ + recno_t last, lno; + u_int value; + + /* + * The first count is the line to use. If the value doesn't + * exist, use the last line. + */ + if (F_ISSET(vp, VC_C1SET)) { + lno = vp->count; + if (file_lline(sp, ep, &last)) + return (1); + if (lno > last) + lno = last; + } else + lno = fm->lno; + + /* Set return cursor values. */ + rp->lno = lno; + rp->cno = fm->cno; + + /* + * The second count is the displayed window size, i.e. the 'z' + * command is another way to get artificially small windows. + * + * !!! + * A window size of 0 was historically allowed, and simply ignored. + * Also, this could be much more simply done by modifying the value + * of the O_WINDOW option, but that's not how it worked historically. + */ + if (F_ISSET(vp, VC_C2SET) && + vp->count2 != 0 && sp->s_rrel(sp, vp->count2)) + return (1); + + switch (vp->character) { + case '-': /* Put the line at the bottom. */ + if (sp->s_fill(sp, ep, lno, P_BOTTOM)) + return (1); + break; + case '.': /* Put the line in the middle. */ + if (sp->s_fill(sp, ep, lno, P_MIDDLE)) + return (1); + break; + default: /* Put the line at the top for <cr>. */ + value = term_key_val(sp, vp->character); + if (value != K_CR && value != K_NL) { + msgq(sp, M_ERR, "usage: %s.", vp->kp->usage); + return (1); + } + /* FALLTHROUGH */ + case '+': /* Put the line at the top. */ + if (sp->s_fill(sp, ep, lno, P_TOP)) + return (1); + break; + case '^': /* Print the screen before the z- screen. */ + /* + * !!! + * Historic practice isn't real clear on this one. It seems + * that the command "70z^" is the same as ":70<cr>z-z^" with + * an off-by-one difference. So, until I find documentation + * to the contrary, the z^ command in this implementation + * displays the screen immediately before the current one. + * Fill the screen with the selected line at the bottom, then, + * scroll the screen down a page, and move to the middle line + * of the screen. Historic vi moved the cursor to some random + * place in the screen, as far as I can tell. + */ + if (sp->s_fill(sp, ep, lno, P_BOTTOM)) + return (1); + if (sp->s_down(sp, ep, rp, sp->t_maxrows - 1, 1)) + return (1); + if (sp->s_position(sp, ep, rp, 0, P_MIDDLE)) + return (1); + break; + } + + /* If the map changes, have to redraw the entire screen. */ + F_SET(sp, S_REDRAW); + + return (0); +} diff --git a/usr.bin/vi/nvi/vcmd.c b/usr.bin/vi/nvi/vcmd.c new file mode 100644 index 000000000000..d7f1caf81d0c --- /dev/null +++ b/usr.bin/vi/nvi/vcmd.c @@ -0,0 +1,522 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)vcmd.c 8.22 (Berkeley) 1/8/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "vcmd.h" + +/* + * This array maps keystrokes to vi command functions. It is known + * in ex/ex_usage.c that it takes four columns to name a vi character. + */ +VIKEYS const vikeys [MAXVIKEY + 1] = { +/* 000 NUL -- The code in vi.c expects key 0 to be undefined. */ + {NULL}, +/* 001 ^A */ + {v_searchw, V_ABS|V_CNT|V_MOVE|V_KEYW|V_RCM_SET, + "[count]^A", + "^A search forward for cursor word"}, +/* 002 ^B */ + {v_pageup, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^B", + "^B page up by screens"}, +/* 003 ^C */ + {NULL, 0, + "^C", + "^C interrupt a search or global command"}, +/* 004 ^D */ + {v_hpagedown, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^D", + "^D page down by half screens (setting count)"}, +/* 005 ^E */ + {v_linedown, V_CNT, + "[count]^E", + "^E page down by lines"}, +/* 006 ^F */ + {v_pagedown, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^F", + "^F page down by screens"}, +/* 007 ^G */ + {v_status, 0, + "^G", + "^G file status"}, +/* 010 ^H */ + {v_left, V_CNT|V_MOVE|V_RCM_SET, + "[count]^H", + "^H move left by columns"}, +/* 011 ^I */ + {NULL}, +/* 012 ^J */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^J", + "^J move down by lines"}, +/* 013 ^K */ + {NULL}, +/* 014 ^L */ + {v_redraw, 0, + "^L", + "^L redraw screen"}, +/* 015 ^M */ + {v_cr, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]^M", + "^M move down by lines (to first non-blank)"}, +/* 016 ^N */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^N", + "^N move down by lines"}, +/* 017 ^O */ + {NULL}, +/* 020 ^P */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]^P", + "^P move up by lines"}, +/* 021 ^Q -- not available, used for hardware flow control. */ + {NULL}, +/* 022 ^R */ + {v_redraw, 0, + "^R", + "^R redraw screen"}, +/* 023 ^S -- not available, used for hardware flow control. */ + {NULL}, +/* 024 ^T */ + {v_tagpop, V_RCM_SET, + "^T", + "^T tag pop"}, +/* 025 ^U */ + {v_hpageup, V_ABS|V_CNT|V_RCM_SETLFNB, + "[count]^U", + "^U half page up (set count)"}, +/* 026 ^V */ + {NULL, 0, + "^V", + "^V input a literal character"}, +/* 027 ^W */ + {v_screen, 0, + "^W", + "^W move to next screen"}, +/* 030 ^X */ + {NULL}, +/* 031 ^Y */ + {v_lineup, V_CNT, + "[count]^Y", + "^Y page up by lines"}, +/* 032 ^Z */ + {v_stop, 0, + "^Z", + "^Z suspend editor"}, +/* 033 ^[ */ + {NULL, 0, + "^[ <escape>", + "^[ <escape> leave input mode, return to command mode"}, +/* 034 ^\ */ + {NULL}, +/* 035 ^] */ + {v_tagpush, V_KEYW|V_RCM_SET, + "^]", + "^] tag push cursor word"}, +/* 036 ^^ */ + {v_switch, 0, + "^^", + "^^ switch to previous file"}, +/* 037 ^_ */ + {NULL}, +/* 040 ' ' */ + {v_right, V_CNT|V_MOVE|V_RCM_SET, + "[count]' '", + " <space> move right by columns"}, +/* 041 ! */ + {v_filter, V_CNT|V_DOT|V_MOTION|V_RCM_SET, + "[count]![count]motion command(s)", + " ! filter through command(s) to motion"}, +/* 042 " */ + {NULL}, +/* 043 # */ + {v_increment, V_CHAR|V_CNT|V_DOT|V_KEYNUM|V_RCM_SET, + "[count]#[#+-]", + " # number increment/decrement"}, +/* 044 $ */ + {v_dollar, V_CNT|V_MOVE|V_RCM_SETLAST, + " [count]$", + " $ move to last column"}, +/* 045 % */ + {v_match, V_ABS|V_MOVE|V_RCM_SET, + "%", + " % move to match"}, +/* 046 & */ + {v_again, 0, + "&", + " & repeat substitution"}, +/* 047 ' */ + {v_gomark, V_ABS|V_CHAR|V_LMODE|V_MOVE|V_RCM_SETFNB, + "'['a-z]", + " ' move to mark (to first non-blank)"}, +/* 050 ( */ + {v_sentenceb, V_CNT|V_MOVE|V_RCM_SET, + "[count](", + " ( move back sentence"}, +/* 051 ) */ + {v_sentencef, V_ABS|V_CNT|V_MOVE|V_RCM_SET, + "[count])", + " ) move forward sentence"}, +/* 052 * */ + {NULL}, +/* 053 + */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]+", + " + move down by lines (to first non-blank)"}, +/* 054 , */ + {v_chrrepeat, V_CNT|V_MOVE|V_RCM_SET, + "[count],", + " , reverse last F, f, T or t search"}, +/* 055 - */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]-", + " - move up by lines (to first non-blank)"}, +/* 056 . */ + {NULL, 0, + ".", + " . repeat the last command"}, +/* 057 / */ + {v_searchf, V_ABS|V_MOVE|V_RCM_SET, + "/RE[/ offset]", + " / search forward"}, +/* 060 0 */ + {v_zero, V_MOVE|V_RCM_SET, + "0", + " 0 move to first character"}, +/* 061 1 */ + {NULL}, +/* 062 2 */ + {NULL}, +/* 063 3 */ + {NULL}, +/* 064 4 */ + {NULL}, +/* 065 5 */ + {NULL}, +/* 066 6 */ + {NULL}, +/* 067 7 */ + {NULL}, +/* 070 8 */ + {NULL}, +/* 071 9 */ + {NULL}, +/* 072 : */ + {v_ex, 0, + ":command [| command] ...", + " : ex command"}, +/* 073 ; */ + {v_chrepeat, V_CNT|V_MOVE|V_RCM_SET, + "[count];", + " ; repeat last F, f, T or t search"}, +/* 074 < */ + {v_shiftl, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH, + "[count]<[count]motion", + " < shift lines left to motion"}, +/* 075 = */ + {NULL}, +/* 076 > */ + {v_shiftr, V_CNT|V_DOT|V_MOTION|V_RCM_SET|VC_SH, + "[count]>[count]motion", + " > shift lines right to motion"}, +/* 077 ? */ + {v_searchb, V_ABS|V_MOVE|V_RCM_SET, + "?RE[? offset]", + " ? search backward"}, +/* 100 @ */ + {v_at, V_RBUF|V_RCM_SET, + "@buffer", + " @ execute buffer"}, +/* 101 A */ + {v_iA, V_CNT|V_DOT|V_RCM_SET, + "[count]A", + " A append to the line"}, +/* 102 B */ + {v_wordB, V_CNT|V_MOVE|V_RCM_SET, + "[count]B", + " B move back bigword"}, +/* 103 C */ + {v_Change, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]C", + " C change to end-of-line"}, +/* 104 D */ + {v_Delete, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]D", + " D delete to end-of-line"}, +/* 105 E */ + {v_wordE, V_CNT|V_MOVE|V_RCM_SET, + "[count]E", + " E move to end of bigword"}, +/* 106 F */ + {v_chF, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]F character", + " F character in line backward search"}, +/* 107 G */ + {v_lgoto, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SETFNB, + "[count]G", + " G move to line"}, +/* 110 H */ + {v_home, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "[count]H", + " H move to count lines from screen top"}, +/* 111 I */ + {v_iI, V_CNT|V_DOT|V_RCM_SET, + "[count]I", + " I insert at line beginning"}, +/* 112 J */ + {v_join, V_CNT|V_DOT|V_RCM_SET, + "[count]J", + " J join lines"}, +/* 113 K */ + {NULL}, +/* 114 L */ + {v_bottom, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "[count]L", + " L move to screen bottom"}, +/* 115 M */ + {v_middle, V_CNT|V_LMODE|V_MOVE|V_RCM_SETNNB, + "M", + " M move to screen middle"}, +/* 116 N */ + {v_searchN, V_ABS|V_MOVE|V_RCM_SET, + "n", + " N reverse last search"}, +/* 117 O */ + {v_iO, V_CNT|V_DOT|V_RCM_SET, + "[count]O", + " O insert above line"}, +/* 120 P */ + {v_Put, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer]P", + " P insert before cursor from buffer"}, +/* 121 Q */ + {v_exmode, 0, + "Q", + " Q switch to ex mode"}, +/* 122 R */ + {v_Replace, V_CNT|V_DOT|V_RCM_SET, + "[count]R", + " R replace characters"}, +/* 123 S */ + {v_Subst, V_CNT|V_DOT|V_LMODE|V_OBUF|V_RCM_SET, + "[buffer][count]S", + " S substitute for the line(s)"}, +/* 124 T */ + {v_chT, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]T character", + " T before character in line backward search"}, +/* 125 U */ + {v_Undo, V_RCM_SET, + "U", + " U Restore the current line"}, +/* 126 V */ + {NULL}, +/* 127 W */ + {v_wordW, V_CNT|V_MOVE|V_RCM_SET, + "[count]W", + " W move to next bigword"}, +/* 130 X */ + {v_Xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]X", + " X delete character before cursor"}, +/* 131 Y */ + {v_Yank, V_CNT|V_LMODE|V_OBUF, + "[buffer][count]Y", + " Y copy line"}, +/* 132 Z */ + {v_exit, 0, + "ZZ", + "ZZ save file and exit"}, +/* 133 [ */ + {v_sectionb, V_ABS|V_LMODE|V_MOVE|V_RCM_SET, + "[[", + "[[ move back section"}, +/* 134 \ */ + {NULL}, +/* 135 ] */ + {v_sectionf, V_ABS|V_LMODE|V_MOVE|V_RCM_SET, + "]]", + "]] move forward section"}, +/* 136 ^ */ + /* + * DON'T set the V_RCM_SETFNB flag, the function has to do + * the work anyway, in case it's a motion component. DO set + * V_RCM_SET, so that any motion that's part of a command is + * preserved. + */ + {v_first, V_CNT|V_MOVE|V_RCM_SET, + "^", + " ^ move to first non-blank"}, +/* 137 _ */ + /* + * DON'T set the V_RCM_SETFNB flag, the function has to do + * the work anyway, in case it's a motion component. DO set + * V_RCM_SET, so that any motion that's part of a command is + * preserved. + */ + {v_cfirst, V_CNT|V_MOVE|V_RCM_SET, + "_", + " _ move to first non-blank"}, +/* 140 ` */ + {v_gomark, V_ABS|V_CHAR|V_MOVE|V_RCM_SET, + "`[`a-z]", + " ` move to mark"}, +/* 141 a */ + {v_ia, V_CNT|V_DOT|V_RCM_SET, + "[count]a", + " a append after cursor"}, +/* 142 b */ + {v_wordb, V_CNT|V_MOVE|V_RCM_SET, + "[count]b", + " b move back word"}, +/* 143 c */ + {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_C, + "[buffer][count]c[count]motion", + " c change to motion"}, +/* 144 d */ + {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|V_RCM_SET|VC_D, + "[buffer][count]d[count]motion", + " d delete to motion"}, +/* 145 e */ + {v_worde, V_CNT|V_MOVE|V_RCM_SET, + "[count]e", + " e move to end of word"}, +/* 146 f */ + {v_chf, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]f character", + " f character in line forward search"}, +/* 147 g */ + {NULL}, +/* 150 h */ + {v_left, V_CNT|V_MOVE|V_RCM_SET, + "[count]h", + " h move left by columns"}, +/* 151 i */ + {v_ii, V_CNT|V_DOT|V_RCM_SET, + "[count]i", + " i insert before cursor"}, +/* 152 j */ + {v_down, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]j", + " j move down by lines"}, +/* 153 k */ + {v_up, V_CNT|V_LMODE|V_MOVE|V_RCM, + "[count]k", + " k move up by lines"}, +/* 154 l */ + {v_right, V_CNT|V_MOVE|V_RCM_SET, + "[count]l", + " l move right by columns"}, +/* 155 m */ + {v_mark, V_CHAR, + "m[a-z]", + " m set mark"}, +/* 156 n */ + {v_searchn, V_ABS|V_MOVE|V_RCM_SET, + "n", + " n repeat last search"}, +/* 157 o */ + {v_io, V_CNT|V_DOT|V_RCM_SET, + "[count]o", + " o append after line"}, +/* 160 p */ + {v_put, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer]p", + " p insert after cursor from buffer"}, +/* 161 q */ + {NULL}, +/* 162 r */ + {v_replace, V_CNT|V_DOT|V_RCM_SET, + "[count]r character", + " r replace character"}, +/* 163 s */ + {v_subst, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]s", + " s substitute character"}, +/* 164 t */ + {v_cht, V_CHAR|V_CNT|V_MOVE|V_RCM_SET, + "[count]t character", + " t before character in line forward search"}, +/* 165 u */ + /* + * DON'T set the V_DOT flag, it' more complicated than that. + * See vi/vi.c for details. + */ + {v_undo, V_RCM_SET, + "u", + " u undo last change"}, +/* 166 v */ + {NULL}, +/* 167 w */ + {v_wordw, V_CNT|V_MOVE|V_RCM_SET, + "[count]w", + " w move to next word"}, +/* 170 x */ + {v_xchar, V_CNT|V_DOT|V_OBUF|V_RCM_SET, + "[buffer][count]x", + " x delete character"}, +/* 171 y */ + {v_yank, V_CNT|V_MOTION|V_OBUF|V_RCM_SET|VC_Y, + "[buffer][count]y[count]motion", + " y copy text to motion into a cut buffer"}, +/* 172 z */ + /* + * DON'T set the V_CHAR flag, the char isn't required, + * so it's handled specially in getcmd(). + */ + {v_z, V_CNT|V_RCM_SETFNB, + "[line]z[window_size][-|.|+|^|<CR>]", + " z redraw window"}, +/* 173 { */ + {v_paragraphb, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET, + "[count]{", + " { move back paragraph"}, +/* 174 | */ + {v_ncol, V_ABS|V_CNT|V_MOVE|V_RCM_SET, + "[count]|", + " | move to column"}, +/* 175 } */ + {v_paragraphf, V_ABS|V_CNT|V_LMODE|V_MOVE|V_RCM_SET, + "[count]}", + " } move forward paragraph"}, +/* 176 ~ */ + {v_ulcase, V_CNT|V_DOT|V_RCM_SET, + "[count]~", + " ~ reverse case"}, +}; diff --git a/usr.bin/vi/nvi/vcmd.h b/usr.bin/vi/nvi/vcmd.h new file mode 100644 index 000000000000..d48fa26acf5e --- /dev/null +++ b/usr.bin/vi/nvi/vcmd.h @@ -0,0 +1,274 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vcmd.h 8.23 (Berkeley) 1/8/94 + */ + +typedef struct _vikeys VIKEYS; + +/* Structure passed around to functions implementing vi commands. */ +typedef struct _vicmdarg { +#define vp_startzero buffer /* START ZERO OUT. */ + CHAR_T buffer; /* Buffer. */ + CHAR_T character; /* Character. */ + u_long count; /* Count. */ + u_long count2; /* Second count (only used by z). */ + int key; /* Command key. */ + VIKEYS const *kp; /* VIKEYS key. */ + VIKEYS const *mkp; /* VIKEYS motion key. */ + size_t klen; /* Keyword length. */ + +/* + * Historic vi allowed "dl" when the cursor was on the last column, deleting + * the last character, and similarly allowed "dw" when the cursor was on the + * last column of the file. It didn't allow "dh" when the cursor was on + * column 1, although these cases are not strictly analogous. The point is + * that some movements would succeed if they were associated with a motion + * command, and fail otherwise. This is part of the off-by-1 schizophrenia + * that plagued vi. Other examples are that "dfb" deleted everything up to + * and including the next 'b' character, but "d/b" only deleted everything + * up to the next 'b' character. While this implementation regularizes the + * interface to the extent possible, there are many special cases that can't + * be fixed. This is implemented by setting special flags per command so that + * the motion routines know what's really going on. + * + * Note, the VC_COMMASK flags are set in the vikeys array, and therefore + * must have values not used in the set of flags declared in the VIKEYS + * structure below. + */ +#define VC_C 0x0001 /* The 'c' command. */ +#define VC_D 0x0002 /* The 'd' command. */ +#define VC_SH 0x0004 /* The '>' command. */ +#define VC_Y 0x0008 /* The 'y' command. */ +#define VC_COMMASK 0x000f /* Mask for special flags. */ + +#define VC_BUFFER 0x0010 /* Buffer set. */ +#define VC_C1SET 0x0020 /* Count 1 set. */ +#define VC_C1RESET 0x0040 /* Reset the C1SET flag for dot commands. */ +#define VC_C2SET 0x0080 /* Count 2 set. */ +#define VC_LMODE 0x0100 /* Motion is line oriented. */ +#define VC_ISDOT 0x0200 /* Command was the dot command. */ +#define VC_REVMOVE 0x0400 /* Movement was before the cursor. */ + + u_int flags; + +#define vp_endzero keyword /* END ZERO OUT. */ + char *keyword; /* Keyword. */ + size_t kbuflen; /* Keyword buffer length. */ +} VICMDARG; + +/* Vi command structure. */ +struct _vikeys { /* Underlying function. */ + int (*func) __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *)); + +#define V_DONTUSE1 0x000001 /* VC_C */ +#define V_DONTUSE2 0x000002 /* VC_D */ +#define V_DONTUSE3 0x000004 /* VC_SH */ +#define V_DONTUSE4 0x000008 /* VC_Y */ +#define V_ABS 0x000010 /* Absolute movement, set '' mark. */ +#define V_CHAR 0x000020 /* Character (required, trailing). */ +#define V_CNT 0x000040 /* Count (optional, leading). */ +#define V_DOT 0x000080 /* On success, sets dot command. */ +#define V_KEYNUM 0x000100 /* Cursor referenced number. */ +#define V_KEYW 0x000200 /* Cursor referenced word. */ +#define V_LMODE 0x000400 /* Motion is line oriented. */ +#define V_MOTION 0x000800 /* Motion (required, trailing). */ +#define V_MOVE 0x001000 /* Command defines movement. */ +#define V_OBUF 0x002000 /* Buffer (optional, leading). */ +#define V_RBUF 0x004000 /* Buffer (required, trailing). */ +#define V_RCM 0x008000 /* Use relative cursor movment (RCM). */ +#define V_RCM_SET 0x010000 /* RCM: set to current position. */ +#define V_RCM_SETFNB 0x020000 /* RCM: set to first non-blank (FNB). */ +#define V_RCM_SETLAST 0x040000 /* RCM: set to last character. */ +#define V_RCM_SETLFNB 0x080000 /* RCM: set to FNB if line moved. */ +#define V_RCM_SETNNB 0x100000 /* RCM: set to next non-blank. */ + u_long flags; + char *usage; /* Usage line. */ + char *help; /* Help line. */ +}; +#define MAXVIKEY 126 /* List of vi commands. */ +extern VIKEYS const vikeys[MAXVIKEY + 1]; + +/* Definition of a "word". */ +#define inword(ch) (isalnum(ch) || (ch) == '_') + +/* Character stream structure, prototypes. */ +typedef struct _vcs { + recno_t cs_lno; /* Line. */ + size_t cs_cno; /* Column. */ + char *cs_bp; /* Buffer. */ + size_t cs_len; /* Length. */ + int cs_ch; /* Character. */ +#define CS_EMP 1 /* Empty line. */ +#define CS_EOF 2 /* End-of-file. */ +#define CS_EOL 3 /* End-of-line. */ +#define CS_SOF 4 /* Start-of-file. */ + int cs_flags; /* Return flags. */ +} VCS; + +int cs_bblank __P((SCR *, EXF *, VCS *)); +int cs_fblank __P((SCR *, EXF *, VCS *)); +int cs_fspace __P((SCR *, EXF *, VCS *)); +int cs_init __P((SCR *, EXF *, VCS *)); +int cs_next __P((SCR *, EXF *, VCS *)); +int cs_prev __P((SCR *, EXF *, VCS *)); + +/* Vi private, per-screen memory. */ +typedef struct _vi_private { + VICMDARG sdot; /* Saved dot, motion command. */ + VICMDARG sdotmotion; + + CHAR_T rlast; /* Last 'r' command character. */ + + char *rep; /* Input replay buffer. */ + size_t rep_len; /* Input replay buffer length. */ + size_t rep_cnt; /* Input replay buffer characters. */ + + CHAR_T inc_lastch; /* Last increment character. */ + long inc_lastval; /* Last increment value. */ + + char *paragraph; /* Paragraph search list. */ + size_t paragraph_len; /* Paragraph search list length. */ + + u_long u_ccnt; /* Undo command count. */ +} VI_PRIVATE; + +#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private)) + +/* Vi function prototypes. */ +int txt_auto __P((SCR *, EXF *, recno_t, TEXT *, size_t, TEXT *)); +int v_buildparagraph __P((SCR *)); +int v_end __P((SCR *)); +void v_eof __P((SCR *, EXF *, MARK *)); +void v_eol __P((SCR *, EXF *, MARK *)); +int v_exwrite __P((void *, const char *, int)); +int v_init __P((SCR *, EXF *)); +int v_isempty __P((char *, size_t)); +int v_msgflush __P((SCR *)); +int v_ntext __P((SCR *, EXF *, TEXTH *, MARK *, + const char *, const size_t, MARK *, int, recno_t, u_int)); +int v_optchange __P((SCR *, int)); +int v_screen_copy __P((SCR *, SCR *)); +int v_screen_end __P((SCR *)); +void v_sof __P((SCR *, MARK *)); +int vi __P((SCR *, EXF *)); + +#define VIPROTO(type, name) \ + type name __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *)) + +VIPROTO(int, v_again); +VIPROTO(int, v_at); +VIPROTO(int, v_bottom); +VIPROTO(int, v_cfirst); +VIPROTO(int, v_Change); +VIPROTO(int, v_change); +VIPROTO(int, v_chF); +VIPROTO(int, v_chf); +VIPROTO(int, v_chrepeat); +VIPROTO(int, v_chrrepeat); +VIPROTO(int, v_chT); +VIPROTO(int, v_cht); +VIPROTO(int, v_cr); +VIPROTO(int, v_Delete); +VIPROTO(int, v_delete); +VIPROTO(int, v_dollar); +VIPROTO(int, v_down); +VIPROTO(int, v_ex); +VIPROTO(int, v_exit); +VIPROTO(int, v_exmode); +VIPROTO(int, v_filter); +VIPROTO(int, v_first); +VIPROTO(int, v_gomark); +VIPROTO(int, v_home); +VIPROTO(int, v_hpagedown); +VIPROTO(int, v_hpageup); +VIPROTO(int, v_iA); +VIPROTO(int, v_ia); +VIPROTO(int, v_iI); +VIPROTO(int, v_ii); +VIPROTO(int, v_increment); +VIPROTO(int, v_iO); +VIPROTO(int, v_io); +VIPROTO(int, v_join); +VIPROTO(int, v_left); +VIPROTO(int, v_lgoto); +VIPROTO(int, v_linedown); +VIPROTO(int, v_lineup); +VIPROTO(int, v_mark); +VIPROTO(int, v_match); +VIPROTO(int, v_middle); +VIPROTO(int, v_ncol); +VIPROTO(int, v_pagedown); +VIPROTO(int, v_pageup); +VIPROTO(int, v_paragraphb); +VIPROTO(int, v_paragraphf); +VIPROTO(int, v_Put); +VIPROTO(int, v_put); +VIPROTO(int, v_redraw); +VIPROTO(int, v_Replace); +VIPROTO(int, v_replace); +VIPROTO(int, v_right); +VIPROTO(int, v_screen); +VIPROTO(int, v_searchb); +VIPROTO(int, v_searchf); +VIPROTO(int, v_searchN); +VIPROTO(int, v_searchn); +VIPROTO(int, v_searchw); +VIPROTO(int, v_sectionb); +VIPROTO(int, v_sectionf); +VIPROTO(int, v_sentenceb); +VIPROTO(int, v_sentencef); +VIPROTO(int, v_shiftl); +VIPROTO(int, v_shiftr); +VIPROTO(int, v_status); +VIPROTO(int, v_stop); +VIPROTO(int, v_Subst); +VIPROTO(int, v_subst); +VIPROTO(int, v_switch); +VIPROTO(int, v_tagpop); +VIPROTO(int, v_tagpush); +VIPROTO(int, v_ulcase); +VIPROTO(int, v_Undo); +VIPROTO(int, v_undo); +VIPROTO(int, v_up); +VIPROTO(int, v_wordB); +VIPROTO(int, v_wordb); +VIPROTO(int, v_wordE); +VIPROTO(int, v_worde); +VIPROTO(int, v_wordW); +VIPROTO(int, v_wordw); +VIPROTO(int, v_Xchar); +VIPROTO(int, v_xchar); +VIPROTO(int, v_Yank); +VIPROTO(int, v_yank); +VIPROTO(int, v_z); +VIPROTO(int, v_zero); diff --git a/usr.bin/vi/nvi/vi.c b/usr.bin/vi/nvi/vi.c new file mode 100644 index 000000000000..f969d78cfabf --- /dev/null +++ b/usr.bin/vi/nvi/vi.c @@ -0,0 +1,780 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)vi.c 8.45 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" + +static int getcmd __P((SCR *, EXF *, + VICMDARG *, VICMDARG *, VICMDARG *, int *)); +static inline int + getcount __P((SCR *, ARG_CHAR_T, u_long *)); +static inline int + getkey __P((SCR *, CH *, u_int)); +static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int)); +static int getmotion __P((SCR *, EXF *, + VICMDARG *, VICMDARG *, MARK *, MARK *)); + +/* + * Side-effect: + * The dot structure can be set by the underlying vi functions, + * see v_Put() and v_put(). + */ +#define DOT (&VIP(sp)->sdot) +#define DOTMOTION (&VIP(sp)->sdotmotion) + +/* + * vi -- + * Main vi command loop. + */ +int +vi(sp, ep) + SCR *sp; + EXF *ep; +{ + MARK abs, fm, tm, m; + VICMDARG cmd, *vp; + u_int flags, saved_mode; + int comcount, eval; + + /* Start vi. */ + if (v_init(sp, ep)) + return (1); + + /* Paint the screen. */ + if (sp->s_refresh(sp, ep)) { + (void)v_end(sp); + return (1); + } + + /* Command initialization. */ + memset(&cmd, 0, sizeof(VICMDARG)); + + for (eval = 0, vp = &cmd;;) { + if (!MAPPED_KEYS_WAITING(sp) && log_cursor(sp, ep)) + goto err; + + /* + * We get a command, which may or may not have an associated + * motion. If it does, we get it too, calling its underlying + * function to get the resulting mark. We then call the + * command setting the cursor to the resulting mark. + */ + if (getcmd(sp, ep, DOT, vp, NULL, &comcount)) + goto err; + + /* + * Historical practice: if a dot command gets a new count, + * any motion component goes away, i.e. "d3w2." deletes a + * total of 5 words. + */ + if (F_ISSET(vp, VC_ISDOT) && comcount) + DOTMOTION->count = 1; + + /* Get any associated keyword. */ + flags = vp->kp->flags; + if (LF_ISSET(V_KEYNUM | V_KEYW) && + getkeyword(sp, ep, vp, flags)) + goto err; + + /* If a non-relative movement, copy the future absolute mark. */ + if (LF_ISSET(V_ABS)) { + abs.lno = sp->lno; + abs.cno = sp->cno; + } + + /* + * Do any required motion; getmotion sets the from MARK + * and the line mode flag. + */ + if (LF_ISSET(V_MOTION)) { + if (getmotion(sp, ep, DOTMOTION, vp, &fm, &tm)) + goto err; + } else { + /* + * Set everything to the current cursor position. + * Line commands (ex: Y) default to the current line. + */ + tm.lno = fm.lno = sp->lno; + tm.cno = fm.cno = sp->cno; + + /* + * Set line mode flag, for example, "yy". + * + * If a count is set, we set the to MARK here relative + * to the cursor/from MARK. This is done for commands + * that take both counts and motions, i.e. "4yy" and + * "y%" -- there's no way the command can known which + * the user did, so we have to do it here. There are + * other commands that are line mode commands and take + * counts ("#G", "#H") and for which this calculation + * is either meaningless or wrong. Each command must + * do its own validity checking of the value. + */ + if (F_ISSET(vp->kp, V_LMODE)) { + F_SET(vp, VC_LMODE); + if (F_ISSET(vp, VC_C1SET)) { + tm.lno = sp->lno + vp->count - 1; + tm.cno = sp->cno; + } + } + } + + /* Increment the command count. */ + ++sp->ccnt; + + /* + * Call the function. Set the return cursor to the current + * cursor position first -- the underlying routines don't + * bother to do the work if it doesn't move. + */ + m.lno = sp->lno; + m.cno = sp->cno; + saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE); + if ((vp->kp->func)(sp, ep, vp, &fm, &tm, &m)) + goto err; +#ifdef DEBUG + /* Make sure no function left the temporary space locked. */ + if (F_ISSET(sp->gp, G_TMP_INUSE)) { + msgq(sp, M_ERR, + "Error: vi: temporary buffer not released."); + return (1); + } +#endif + /* + * If that command took us out of vi or changed the screen, + * then exit the loop without further action. + */ + if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) + break; + + /* Set the absolute mark. */ + if (LF_ISSET(V_ABS) && mark_set(sp, ep, ABSMARK1, &abs, 1)) + goto err; + + /* Set the dot command structure. */ + if (LF_ISSET(V_DOT)) { + *DOT = cmd; + F_SET(DOT, VC_ISDOT); + /* + * If a count was supplied for both the command and + * its motion, the count was used only for the motion. + * Turn the count back on for the dot structure. + */ + if (F_ISSET(vp, VC_C1RESET)) + F_SET(DOT, VC_C1SET); + } + + /* + * Some vi row movements are "attracted" to the last position + * set, i.e. the V_RCM commands are moths to the V_RCM_SET + * commands' candle. It's broken into two parts. Here we deal + * with the command flags. In sp->relative(), we deal with the + * screen flags. If the movement is to the EOL the vi command + * handles it. If it's to the beginning, we handle it here. + * + * Note, some commands (e.g. _, ^) don't set the V_RCM_SETFNB + * flag, but do the work themselves. The reason is that they + * have to modify the column in case they're being used as a + * motion component. Other similar commands (e.g. +, -) don't + * have to modify the column because they are always line mode + * operations when used as motions, so the column number isn't + * of any interest. + * + * Does this totally violate the screen and editor layering? + * You betcha. As they say, if you think you understand it, + * you don't. + */ + switch (LF_ISSET(V_RCM | V_RCM_SETFNB | + V_RCM_SETLAST | V_RCM_SETLFNB | V_RCM_SETNNB)) { + case 0: + break; + case V_RCM: + m.cno = sp->s_relative(sp, ep, m.lno); + break; + case V_RCM_SETLAST: + sp->rcmflags = RCM_LAST; + break; + case V_RCM_SETLFNB: + if (fm.lno != m.lno) { + if (nonblank(sp, ep, m.lno, &m.cno)) + goto err; + sp->rcmflags = RCM_FNB; + } + break; + case V_RCM_SETFNB: + m.cno = 0; + /* FALLTHROUGH */ + case V_RCM_SETNNB: + if (nonblank(sp, ep, m.lno, &m.cno)) + goto err; + sp->rcmflags = RCM_FNB; + break; + default: + abort(); + } + + /* Update the cursor. */ + sp->lno = m.lno; + sp->cno = m.cno; + + if (!MAPPED_KEYS_WAITING(sp)) { + (void)msg_rpt(sp, 1); + + if (0) +err: term_map_flush(sp, "Vi error"); + } + + /* Refresh the screen. */ + if (sp->s_refresh(sp, ep)) { + eval = 1; + break; + } + + /* Set the new favorite position. */ + if (LF_ISSET(V_RCM_SET)) { + sp->rcmflags = 0; + (void)sp->s_column(sp, ep, &sp->rcm); + } + } + + return (v_end(sp) || eval); +} + +#define KEY(key, map) { \ + if (getkey(sp, &ikey, map)) \ + return (1); \ + key = ikey.ch; \ +} + +/* + * getcmd -- + * + * The command structure for vi is less complex than ex (and don't think + * I'm not grateful!) The command syntax is: + * + * [count] [buffer] [count] key [[motion] | [buffer] [character]] + * + * and there are several special cases. The motion value is itself a vi + * command, with the syntax: + * + * [count] key [character] + */ +static int +getcmd(sp, ep, dp, vp, ismotion, comcountp) + SCR *sp; + EXF *ep; + VICMDARG *dp, *vp; + VICMDARG *ismotion; /* Previous key if getting motion component. */ + int *comcountp; +{ + VIKEYS const *kp; + u_int flags; + CH ikey; + CHAR_T key; + + /* Refresh the command structure. */ + memset(&vp->vp_startzero, 0, + (char *)&vp->vp_endzero - (char *)&vp->vp_startzero); + + /* An escape bells the user if in command mode. */ + if (getkey(sp, &ikey, TXT_MAPCOMMAND)) { + if (ikey.value == K_ESCAPE && ismotion == NULL) + msgq(sp, M_BERR, "Already in command mode"); + return (1); + } + + key = ikey.ch; + if (key > MAXVIKEY) { + msgq(sp, M_BERR, "%s isn't a vi command", charname(sp, key)); + return (1); + } + + /* Pick up optional buffer. */ + if (key == '"') { + KEY(vp->buffer, 0); + F_SET(vp, VC_BUFFER); + KEY(key, TXT_MAPCOMMAND); + } + + /* + * Pick up optional count, where a leading 0 is not a count, + * it's a command. + */ + if (isdigit(key) && key != '0') { + if (getcount(sp, key, &vp->count)) + return (1); + F_SET(vp, VC_C1SET); + *comcountp = 1; + KEY(key, TXT_MAPCOMMAND); + } else + *comcountp = 0; + + /* Pick up optional buffer. */ + if (key == '"') { + if (F_ISSET(vp, VC_BUFFER)) { + msgq(sp, M_ERR, "Only one buffer can be specified."); + return (1); + } + KEY(vp->buffer, 0); + F_SET(vp, VC_BUFFER); + KEY(key, TXT_MAPCOMMAND); + } + + /* + * Find the command. The only legal command with no underlying + * function is dot. + */ + kp = vp->kp = &vikeys[vp->key = key]; + if (kp->func == NULL) { + if (key != '.') { + msgq(sp, M_ERR, + "%s isn't a command", charname(sp, key)); + return (1); + } + + /* If called for a motion command, stop now. */ + if (dp == NULL) + goto usage; + + /* A repeatable command must have been executed. */ + if (!F_ISSET(dp, VC_ISDOT)) { + msgq(sp, M_ERR, "No command to repeat."); + return (1); + } + + /* + * !!! + * If a '.' is immediately entered after an undo command, we + * replay the log instead of redoing the last command. This + * is necessary because 'u' can't set the dot command -- see + * vi/v_undo.c:v_undo for details. + */ + if (VIP(sp)->u_ccnt == sp->ccnt) { + vp->kp = &vikeys['u']; + F_SET(vp, VC_ISDOT); + return (0); + } + + /* Set new count/buffer, if any, and return. */ + if (F_ISSET(vp, VC_C1SET)) { + F_SET(dp, VC_C1SET); + dp->count = vp->count; + } + if (F_ISSET(vp, VC_BUFFER)) + dp->buffer = vp->buffer; + *vp = *dp; + return (0); + } + + flags = kp->flags; + + /* Check for illegal count. */ + if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT)) + goto usage; + + /* Illegal motion command. */ + if (ismotion == NULL) { + /* Illegal buffer. */ + if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER)) + goto usage; + + /* Required buffer. */ + if (LF_ISSET(V_RBUF)) + KEY(vp->buffer, 0); + + /* + * Special case: '[', ']' and 'Z' commands. Doesn't the + * fact that the *single* characters don't mean anything + * but the *doubled* characters do just frost your shorts? + */ + if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') { + KEY(key, TXT_MAPCOMMAND); + if (vp->key != key) + goto usage; + } + /* Special case: 'z' command. */ + if (vp->key == 'z') { + KEY(vp->character, 0); + if (isdigit(vp->character)) { + if (getcount(sp, vp->character, &vp->count2)) + return (1); + F_SET(vp, VC_C2SET); + KEY(vp->character, 0); + } + } + } + + /* + * Commands that have motion components can be doubled to + * imply the current line. + */ + else if (ismotion->key != key && !LF_ISSET(V_MOVE)) { +usage: msgq(sp, M_ERR, "Usage: %s", ismotion != NULL ? + vikeys[ismotion->key].usage : kp->usage); + return (1); + } + + /* Required character. */ + if (LF_ISSET(V_CHAR)) + KEY(vp->character, 0); + + return (0); +} + +/* + * getmotion -- + * + * Get resulting motion mark. + */ +static int +getmotion(sp, ep, dm, vp, fm, tm) + SCR *sp; + EXF *ep; + VICMDARG *dm, *vp; + MARK *fm, *tm; +{ + MARK m; + VICMDARG motion; + u_long cnt; + int notused; + + /* If '.' command, use the dot motion, else get the motion command. */ + if (F_ISSET(vp, VC_ISDOT)) { + motion = *dm; + F_SET(&motion, VC_ISDOT); + } else if (getcmd(sp, ep, NULL, &motion, vp, ¬used)) + return (1); + + /* + * A count may be provided both to the command and to the motion, in + * which case the count is multiplicative. For example, "3y4y" is the + * same as "12yy". This count is provided to the motion command and + * not to the regular function. + */ + cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1; + if (F_ISSET(vp, VC_C1SET)) { + motion.count *= vp->count; + F_SET(&motion, VC_C1SET); + + /* + * Set flags to restore the original values of the command + * structure so dot commands can change the count values, + * e.g. "2dw" "3." deletes a total of five words. + */ + F_CLR(vp, VC_C1SET); + F_SET(vp, VC_C1RESET); + } + + /* + * Some commands can be repeated to indicate the current line. In + * this case, or if the command is a "line command", set the flags + * appropriately. If not a doubled command, run the function to get + * the resulting mark. + */ + if (vp->key == motion.key) { + F_SET(vp, VC_LMODE); + + /* + * Set the end of the command; the column is after the line. + * + * If the current line is missing, i.e. the file is empty, + * historic vi permitted a "cc" or "!!" command to insert + * text. + */ + tm->lno = sp->lno + motion.count - 1; + if (file_gline(sp, ep, tm->lno, &tm->cno) == NULL) { + if (tm->lno != 1 || vp->key != 'c' && vp->key != '!') { + m.lno = sp->lno; + m.cno = sp->cno; + v_eof(sp, ep, &m); + return (1); + } + tm->cno = 0; + } + + /* Set the origin of the command. */ + fm->lno = sp->lno; + fm->cno = 0; + } else { + /* + * Motion commands change the underlying movement (*snarl*). + * For example, "l" is illegal at the end of a line, but "dl" + * is not. Set flags so the function knows the situation. + */ + F_SET(&motion, vp->kp->flags & VC_COMMASK); + + /* + * Everything starts at the current position. This permits + * commands like 'j' and 'k', that are line oriented motions + * and have special cursor suck semantics when they are used + * as standalone commands, to ignore column positioning. + */ + fm->lno = tm->lno = sp->lno; + fm->cno = tm->cno = sp->cno; + if ((motion.kp->func)(sp, ep, &motion, fm, NULL, tm)) + return (1); + + /* + * If the underlying motion was a line motion, set the flag + * in the command structure. Underlying commands can also + * flag the movement as a line motion (see v_sentence). + */ + if (F_ISSET(motion.kp, V_LMODE) || F_ISSET(&motion, VC_LMODE)) + F_SET(vp, VC_LMODE); + + /* + * If the motion is in the reverse direction, switch the from + * and to MARK's so that it's always in a forward direction. + * Because the motion is always from the from MARK to, but not + * including, the to MARK, the function may have modified the + * from MARK, so that it gets the one-past-the-place semantics + * we use; see v_match() for an example. Also set a flag so + * that the underlying function knows that we did this; v_yank, + * for example, has to know so it gets the return cursor right. + */ + if (tm->lno < fm->lno || + tm->lno == fm->lno && tm->cno < fm->cno) { + m = *fm; + *fm = *tm; + *tm = m; + F_SET(vp, VC_REVMOVE); + } + } + + /* + * If the command sets dot, save the motion structure. The + * motion count was changed above and needs to be reset, that's + * why this is done here, and not in the calling routine. + */ + if (F_ISSET(vp->kp, V_DOT)) { + *dm = motion; + dm->count = cnt; + } + + /* Let the underlying function know what motion command was used. */ + vp->mkp = motion.kp; + return (0); +} + +#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c)) + +/* + * getkeyword -- + * Get the "word" the cursor is on. + */ +static int +getkeyword(sp, ep, kp, flags) + SCR *sp; + EXF *ep; + VICMDARG *kp; + u_int flags; +{ + recno_t lno; + size_t beg, end, len; + char *p; + + if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + v_eof(sp, ep, NULL); + else + GETLINE_ERR(sp, sp->lno); + return (1); + } + beg = sp->cno; + + /* May not be a keyword at all. */ + if (p == NULL || len == 0 || + LF_ISSET(V_KEYW) && !inword(p[beg]) || + LF_ISSET(V_KEYNUM) && !innum(p[beg]) && + p[beg] != '-' && p[beg] != '+') { +noword: msgq(sp, M_BERR, "Cursor not in a %s", + LF_ISSET(V_KEYW) ? "word" : "number"); + return (1); + } + + /* + * !!! + * Find the beginning/end of the keyword. Keywords (V_KEYW) are + * used for cursor-word searching and for tags. Historical vi + * only used the word in a tag search from the cursor to the end + * of the word, i.e. if the cursor was on the 'b' in " abc ", the + * tag was "bc". For no particular reason, we make cursor word + * searches follow the same rule. + */ + if (beg != 0) + if (LF_ISSET(V_KEYW)) { +#ifdef MOVE_TO_KEYWORD_BEGINNING + for (;;) { + --beg; + if (!inword(p[beg])) { + ++beg; + break; + } + if (beg == 0) + break; + } +#endif + } else { + for (;;) { + --beg; + if (!innum(p[beg])) { + if (beg > 0 && p[beg - 1] == '0' && + (p[beg] == 'X' || p[beg] == 'x')) + --beg; + else + ++beg; + break; + } + if (beg == 0) + break; + } + + /* Skip possible leading sign. */ + if (beg != 0 && p[beg] != '0' && + (p[beg - 1] == '+' || p[beg - 1] == '-')) + --beg; + } + + if (LF_ISSET(V_KEYW)) { + for (end = sp->cno; ++end < len && inword(p[end]);); + --end; + } else { + for (end = sp->cno; ++end < len;) { + if (p[end] == 'X' || p[end] == 'x') { + if (end != beg + 1 || p[beg] != '0') + break; + continue; + } + if (!innum(p[end])) + break; + } + + /* Just a sign isn't a number. */ + if (end == beg && (p[beg] == '+' || p[beg] == '-')) + goto noword; + --end; + } + + /* + * Getting a keyword implies moving the cursor to its beginning. + * Refresh now. + */ + if (beg != sp->cno) { + sp->cno = beg; + sp->s_refresh(sp, ep); + } + + /* + * XXX + * 8-bit clean problem. Numeric keywords are handled using strtol(3) + * and friends. This would have to be fixed in v_increment and here + * to not depend on a trailing NULL. + */ + len = (end - beg) + 2; /* XXX */ + kp->klen = (end - beg) + 1; + BINC_RET(sp, kp->keyword, kp->kbuflen, len); + memmove(kp->keyword, p + beg, kp->klen); + kp->keyword[kp->klen] = '\0'; /* XXX */ + return (0); +} + +/* + * getcount -- + * Return the next count. + */ +static inline int +getcount(sp, fkey, countp) + SCR *sp; + ARG_CHAR_T fkey; + u_long *countp; +{ + u_long count, tc; + CH ikey; + + ikey.ch = fkey; + count = tc = 0; + do { + /* Assume that overflow results in a smaller number. */ + tc = count * 10 + ikey.ch - '0'; + if (count > tc) { + /* Toss to the next non-digit. */ + do { + if (getkey(sp, &ikey, + TXT_MAPCOMMAND | TXT_MAPNODIGIT)) + return (1); + } while (isdigit(ikey.ch)); + msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX); + return (1); + } + count = tc; + if (getkey(sp, &ikey, TXT_MAPCOMMAND | TXT_MAPNODIGIT)) + return (1); + } while (isdigit(ikey.ch)); + *countp = count; + return (0); +} + +/* + * getkey -- + * Return the next key. + */ +static inline int +getkey(sp, ikeyp, map) + SCR *sp; + CH *ikeyp; + u_int map; +{ + switch (term_key(sp, ikeyp, map)) { + case INP_OK: + break; + case INP_EOF: + F_SET(sp, S_EXIT_FORCE); + /* FALLTHROUGH */ + case INP_ERR: + return (1); + } + return (ikeyp->value == K_ESCAPE); +} diff --git a/usr.bin/vi/options.c b/usr.bin/vi/options.c new file mode 100644 index 000000000000..9c74160c5c01 --- /dev/null +++ b/usr.bin/vi/options.c @@ -0,0 +1,822 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options.c 8.36 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "pathnames.h" + +static int opts_abbcmp __P((const void *, const void *)); +static int opts_cmp __P((const void *, const void *)); +static OPTLIST const *opts_prefix __P((char *)); +static int opts_print __P((SCR *, OPTLIST const *, OPTION *)); + +/* + * O'Reilly noted options and abbreviations are from "Learning the VI Editor", + * Fifth Edition, May 1992. There's no way of knowing what systems they are + * actually from. + * + * HPUX noted options and abbreviations are from "The Ultimate Guide to the + * VI and EX Text Editors", 1990. + */ +static OPTLIST const optlist[] = { +/* O_ALTWERASE 4.4BSD */ + {"altwerase", f_altwerase, OPT_0BOOL, 0}, +/* O_AUTOINDENT 4BSD */ + {"autoindent", NULL, OPT_0BOOL, 0}, +/* O_AUTOPRINT 4BSD */ + {"autoprint", NULL, OPT_1BOOL, 0}, +/* O_AUTOWRITE 4BSD */ + {"autowrite", NULL, OPT_0BOOL, 0}, +/* O_BEAUTIFY 4BSD */ + {"beautify", NULL, OPT_0BOOL, 0}, +/* O_COLUMNS 4.4BSD */ + {"columns", f_columns, OPT_NUM, OPT_NOSAVE}, +/* O_COMMENT 4.4BSD */ + {"comment", NULL, OPT_0BOOL, 0}, +/* O_DIGRAPH XXX: Elvis */ + {"digraph", NULL, OPT_0BOOL, 0}, +/* O_DIRECTORY 4BSD */ + {"directory", NULL, OPT_STR, 0}, +/* O_EDCOMPATIBLE 4BSD */ + {"edcompatible",NULL, OPT_0BOOL, 0}, +/* O_ERRORBELLS 4BSD */ + {"errorbells", NULL, OPT_0BOOL, 0}, +/* O_EXRC System V (undocumented) */ + {"exrc", NULL, OPT_0BOOL, 0}, +/* O_EXTENDED 4.4BSD */ + {"extended", NULL, OPT_0BOOL, 0}, +/* O_FLASH HPUX */ + {"flash", NULL, OPT_1BOOL, 0}, +/* O_HARDTABS 4BSD */ + {"hardtabs", NULL, OPT_NUM, 0}, +/* O_IGNORECASE 4BSD */ + {"ignorecase", NULL, OPT_0BOOL, 0}, +/* O_KEYTIME 4.4BSD */ + {"keytime", f_keytime, OPT_NUM, 0}, +/* O_LEFTRIGHT 4.4BSD */ + {"leftright", f_leftright, OPT_0BOOL, 0}, +/* O_LINES 4.4BSD */ + {"lines", f_lines, OPT_NUM, OPT_NOSAVE}, +/* O_LISP 4BSD */ + {"lisp", f_lisp, OPT_0BOOL, 0}, +/* O_LIST 4BSD */ + {"list", f_list, OPT_0BOOL, 0}, +/* O_MAGIC 4BSD */ + {"magic", NULL, OPT_1BOOL, 0}, +/* O_MATCHTIME 4.4BSD */ + {"matchtime", f_matchtime, OPT_NUM, 0}, +/* O_MESG 4BSD */ + {"mesg", f_mesg, OPT_1BOOL, 0}, +/* O_MODELINE 4BSD */ + {"modeline", f_modeline, OPT_0BOOL, 0}, +/* O_NUMBER 4BSD */ + {"number", f_number, OPT_0BOOL, 0}, +/* O_OPEN 4BSD */ + {"open", NULL, OPT_1BOOL, 0}, +/* O_OPTIMIZE 4BSD */ + {"optimize", f_optimize, OPT_1BOOL, 0}, +/* O_PARAGRAPHS 4BSD */ + {"paragraphs", f_paragraph, OPT_STR, 0}, +/* O_PROMPT 4BSD */ + {"prompt", NULL, OPT_1BOOL, 0}, +/* O_READONLY 4BSD (undocumented) */ + {"readonly", f_readonly, OPT_0BOOL, 0}, +/* O_RECDIR 4.4BSD */ + {"recdir", NULL, OPT_STR, 0}, +/* O_REDRAW 4BSD */ + {"redraw", NULL, OPT_0BOOL, 0}, +/* O_REMAP 4BSD */ + {"remap", NULL, OPT_1BOOL, 0}, +/* O_REPORT 4BSD */ + {"report", NULL, OPT_NUM, OPT_NOSTR}, +/* O_RULER 4.4BSD */ + {"ruler", f_ruler, OPT_0BOOL, 0}, +/* O_SCROLL 4BSD */ + {"scroll", NULL, OPT_NUM, 0}, +/* O_SECTIONS 4BSD */ + {"sections", f_section, OPT_STR, 0}, +/* O_SHELL 4BSD */ + {"shell", NULL, OPT_STR, 0}, +/* O_SHIFTWIDTH 4BSD */ + {"shiftwidth", f_shiftwidth, OPT_NUM, 0}, +/* O_SHOWDIRTY 4.4BSD */ + {"showdirty", NULL, OPT_0BOOL, 0}, +/* O_SHOWMATCH 4BSD */ + {"showmatch", NULL, OPT_0BOOL, 0}, +/* O_SHOWMODE 4.4BSD */ + {"showmode", NULL, OPT_0BOOL, 0}, +/* O_SIDESCROLL 4.4BSD */ + {"sidescroll", f_sidescroll, OPT_NUM, 0}, +/* O_SLOWOPEN 4BSD */ + {"slowopen", NULL, OPT_0BOOL, 0}, +/* O_SOURCEANY 4BSD (undocumented) */ + {"sourceany", f_sourceany, OPT_0BOOL, 0}, +/* O_TABSTOP 4BSD */ + {"tabstop", f_tabstop, OPT_NUM, 0}, +/* O_TAGLENGTH 4BSD */ + {"taglength", NULL, OPT_NUM, OPT_NOSTR}, +/* O_TAGS 4BSD */ + {"tags", f_tags, OPT_STR, 0}, +/* O_TERM 4BSD */ + {"term", f_term, OPT_STR, OPT_NOSAVE}, +/* O_TERSE 4BSD */ + {"terse", NULL, OPT_0BOOL, 0}, +/* O_TIMEOUT 4BSD (undocumented) */ + {"timeout", NULL, OPT_1BOOL, 0}, +/* O_TTYWERASE 4.4BSD */ + {"ttywerase", f_ttywerase, OPT_0BOOL, 0}, +/* O_VERBOSE 4.4BSD */ + {"verbose", NULL, OPT_0BOOL, 0}, +/* O_W1200 4BSD */ + {"w1200", f_w1200, OPT_NUM, OPT_NEVER}, +/* O_W300 4BSD */ + {"w300", f_w300, OPT_NUM, OPT_NEVER}, +/* O_W9600 4BSD */ + {"w9600", f_w9600, OPT_NUM, OPT_NEVER}, +/* O_WARN 4BSD */ + {"warn", NULL, OPT_1BOOL, 0}, +/* O_WINDOW 4BSD */ + {"window", f_window, OPT_NUM, 0}, +/* O_WRAPMARGIN 4BSD */ + {"wrapmargin", f_wrapmargin, OPT_NUM, OPT_NOSTR}, +/* O_WRAPSCAN 4BSD */ + {"wrapscan", NULL, OPT_1BOOL, 0}, +/* O_WRITEANY 4BSD */ + {"writeany", NULL, OPT_0BOOL, 0}, + {NULL}, +}; + +typedef struct abbrev { + char *name; + int offset; +} OABBREV; + +static OABBREV const abbrev[] = { + {"ai", O_AUTOINDENT}, /* 4BSD */ + {"ap", O_AUTOPRINT}, /* 4BSD */ + {"aw", O_AUTOWRITE}, /* 4BSD */ + {"bf", O_BEAUTIFY}, /* 4BSD */ + {"co", O_COLUMNS}, /* 4.4BSD */ + {"dir", O_DIRECTORY}, /* 4BSD */ + {"eb", O_ERRORBELLS}, /* 4BSD */ + {"ed", O_EDCOMPATIBLE}, /* 4BSD (undocumented) */ + {"ex", O_EXRC}, /* System V (undocumented) */ + {"ht", O_HARDTABS}, /* 4BSD */ + {"ic", O_IGNORECASE}, /* 4BSD */ + {"li", O_LINES}, /* 4.4BSD */ + {"modelines", O_MODELINE}, /* HPUX */ + {"nu", O_NUMBER}, /* 4BSD */ + {"opt", O_OPTIMIZE}, /* 4BSD */ + {"para", O_PARAGRAPHS}, /* 4BSD */ + {"re", O_REDRAW}, /* O'Reilly */ + {"ro", O_READONLY}, /* 4BSD (undocumented) */ + {"scr", O_SCROLL}, /* 4BSD (undocumented) */ + {"sect", O_SECTIONS}, /* O'Reilly */ + {"sh", O_SHELL}, /* 4BSD */ + {"slow", O_SLOWOPEN}, /* 4BSD */ + {"sm", O_SHOWMATCH}, /* 4BSD */ + {"sw", O_SHIFTWIDTH}, /* 4BSD */ + {"tag", O_TAGS}, /* 4BSD (undocumented) */ + {"tl", O_TAGLENGTH}, /* 4BSD */ + {"to", O_TIMEOUT}, /* 4BSD (undocumented) */ + {"ts", O_TABSTOP}, /* 4BSD */ + {"tty", O_TERM}, /* 4BSD (undocumented) */ + {"ttytype", O_TERM}, /* 4BSD (undocumented) */ + {"w", O_WINDOW}, /* O'Reilly */ + {"wa", O_WRITEANY}, /* 4BSD */ + {"wi", O_WINDOW}, /* 4BSD (undocumented) */ + {"wm", O_WRAPMARGIN}, /* 4BSD */ + {"ws", O_WRAPSCAN}, /* 4BSD */ + {NULL}, +}; + +/* + * opts_init -- + * Initialize some of the options. Since the user isn't really + * "setting" these variables, don't set their OPT_SET bits. + */ +int +opts_init(sp) + SCR *sp; +{ + ARGS *argv[2], a, b; + OPTLIST const *op; + u_long v; + int cnt; + char *s, b1[1024]; + + a.bp = b1; + a.len = 0; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b; + +#define SET_DEF(opt, str) { \ + if (str != b1) /* GCC puts strings in text-space. */ \ + (void)strcpy(b1, str); \ + a.len = strlen(b1); \ + if (opts_set(sp, argv)) { \ + msgq(sp, M_ERR, \ + "Unable to set default %s option", optlist[opt]); \ + return (1); \ + } \ + F_CLR(&sp->opts[opt], OPT_SET); \ +} + /* Set default values. */ + for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt) + if (op->type == OPT_0BOOL) + O_CLR(sp, cnt); + else if (op->type == OPT_1BOOL) + O_SET(sp, cnt); + + /* + * !!! + * Vi historically stored temporary files in /var/tmp. We store them + * in /tmp by default, hoping it's a memory based file system. There + * are two ways to change this -- the user can set either the directory + * option or the TMPDIR environmental variable. + */ + (void)snprintf(b1, sizeof(b1), "directory=%s", + (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s); + SET_DEF(O_DIRECTORY, b1); + SET_DEF(O_KEYTIME, "keytime=6"); + SET_DEF(O_MATCHTIME, "matchtime=7"); + SET_DEF(O_REPORT, "report=5"); + SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp"); + (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE); + SET_DEF(O_RECDIR, b1); + (void)snprintf(b1, sizeof(b1), "scroll=%ld", O_VAL(sp, O_LINES) / 2); + SET_DEF(O_SCROLL, b1); + SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh"); + (void)snprintf(b1, sizeof(b1), + "shell=%s", (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s); + SET_DEF(O_SHELL, b1); + SET_DEF(O_SHIFTWIDTH, "shiftwidth=8"); + SET_DEF(O_SIDESCROLL, "sidescroll=16"); + SET_DEF(O_TABSTOP, "tabstop=8"); + (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS); + SET_DEF(O_TAGS, b1); + (void)snprintf(b1, sizeof(b1), + "term=%s", (s = getenv("TERM")) == NULL ? "unknown" : s); + SET_DEF(O_TERM, b1); + + /* + * The default window option values are: + * 8 if baud rate <= 600 + * 16 if baud rate <= 1200 + * LINES - 1 if baud rate > 1200 + */ + v = baud_from_bval(sp); + if (v <= 600) + v = 8; + else if (v <= 1200) + v = 16; + else + v = O_VAL(sp, O_LINES) - 1; + (void)snprintf(b1, sizeof(b1), "window=%lu", v); + SET_DEF(O_WINDOW, b1); + + SET_DEF(O_WRAPMARGIN, "wrapmargin=0"); + + /* + * By default, the historic vi always displayed information + * about two options, redraw and term. Term seems sufficient. + */ + F_SET(&sp->opts[O_TERM], OPT_SET); + return (0); +} + +/* + * opts_set -- + * Change the values of one or more options. + */ +int +opts_set(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + enum optdisp disp; + OABBREV atmp, *ap; + OPTLIST const *op; + OPTLIST otmp; + OPTION *spo; + u_long value, turnoff; + int ch, offset, rval; + char *endp, *equals, *name, *p; + + disp = NO_DISPLAY; + for (rval = 0; (*argv)->len != 0; ++argv) { + /* + * The historic vi dumped the options for each occurrence of + * "all" in the set list. Puhleeze. + */ + if (!strcmp(argv[0]->bp, "all")) { + disp = ALL_DISPLAY; + continue; + } + + /* Find equals sign or end of set, skipping backquoted chars. */ + for (p = name = argv[0]->bp, equals = NULL; ch = *p; ++p) + switch(ch) { + case '=': + equals = p; + break; + case '\\': + /* Historic vi just used the backslash. */ + if (p[1] == '\0') + break; + ++p; + break; + } + + turnoff = 0; + op = NULL; + if (equals) + *equals++ = '\0'; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Try the name without any leading "no". */ + if (name[0] == 'n' && name[1] == 'o') { + turnoff = 1; + name += 2; + } else + goto prefix; + + /* Check list of abbreviations. */ + atmp.name = name; + if ((ap = bsearch(&atmp, abbrev, + sizeof(abbrev) / sizeof(OABBREV) - 1, + sizeof(OABBREV), opts_abbcmp)) != NULL) { + op = optlist + ap->offset; + goto found; + } + + /* Check list of options. */ + otmp.name = name; + if ((op = bsearch(&otmp, optlist, + sizeof(optlist) / sizeof(OPTLIST) - 1, + sizeof(OPTLIST), opts_cmp)) != NULL) + goto found; + + /* Check for prefix match. */ +prefix: op = opts_prefix(name); + +found: if (op == NULL) { + msgq(sp, M_ERR, + "no %s option: 'set all' gives all option values", + name); + continue; + } + + /* Find current option values. */ + offset = op - optlist; + spo = sp->opts + offset; + + /* Set name, value. */ + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (equals) { + msgq(sp, M_ERR, + "set: [no]%s option doesn't take a value", + name); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, NULL, turnoff)) { + rval = 1; + break; + } + } else if (turnoff) + O_CLR(sp, offset); + else + O_SET(sp, offset); + goto change; + case OPT_NUM: + /* + * !!! + * Extension to historic vi. If the OPT_NOSTR flag is + * set, a numeric option may be turned off by using a + * "no" prefix, e.g. "nowrapmargin". (We assume that + * setting the value to 0 turns a numeric option off.) + */ + if (turnoff) { + if (F_ISSET(op, OPT_NOSTR)) { + value = 0; + goto nostr; + } + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + value = strtol(equals, &endp, 10); + if (*endp && !isblank(*endp)) { + msgq(sp, M_ERR, + "set %s: illegal number %s", name, equals); + break; + } +nostr: if (op->func != NULL) { + if (op->func(sp, spo, equals, value)) { + rval = 1; + break; + } + } else + O_VAL(sp, offset) = value; + goto change; + case OPT_STR: + if (turnoff) { + msgq(sp, M_ERR, + "set: %s option isn't a boolean", name); + break; + } + if (!equals) { + if (!disp) + disp = SELECT_DISPLAY; + F_SET(spo, OPT_SELECTED); + break; + } + if (op->func != NULL) { + if (op->func(sp, spo, equals, (u_long)0)) { + rval = 1; + break; + } + } else { + if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED)) + free(O_STR(sp, offset)); + if ((O_STR(sp, offset) = + strdup(equals)) == NULL) { + msgq(sp, M_SYSERR, NULL); + rval = 1; + break; + } else + F_SET(&sp->opts[offset], OPT_ALLOCATED); + } +change: if (sp->s_optchange != NULL) + (void)sp->s_optchange(sp, offset); + F_SET(&sp->opts[offset], OPT_SET); + break; + default: + abort(); + } + } + if (disp) + opts_dump(sp, disp); + return (rval); +} + +/* + * opts_dump -- + * List the current values of selected options. + */ +void +opts_dump(sp, type) + SCR *sp; + enum optdisp type; +{ + OPTLIST const *op; + int base, b_num, cnt, col, colwidth, curlen, s_num; + int numcols, numrows, row; + int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT]; + char nbuf[20]; + + /* + * Options are output in two groups -- those that fit in a column and + * those that don't. Output is done on 6 character "tab" boundaries + * for no particular reason. (Since we don't output tab characters, + * we can ignore the terminal's tab settings.) Ignore the user's tab + * setting because we have no idea how reasonable it is. + */ +#define BOUND 6 + + /* Find a column width we can live with. */ + for (cnt = 6; cnt > 1; --cnt) { + colwidth = (sp->cols - 1) / cnt & ~(BOUND - 1); + if (colwidth >= 10) { + colwidth = (colwidth + BOUND) & ~(BOUND - 1); + break; + } + colwidth = 0; + } + + /* + * Two passes. First, get the set of options to list, entering them + * into the column list or the overflow list. No error checking, + * since we know that at least one option (O_TERM) has the OPT_SET bit + * set. + */ + for (b_num = s_num = 0, op = optlist; op->name; ++op) { + cnt = op - optlist; + + /* If OPT_NEVER set, it's never displayed. */ + if (F_ISSET(op, OPT_NEVER)) + continue; + + switch (type) { + case ALL_DISPLAY: /* Display all. */ + break; + case CHANGED_DISPLAY: /* Display changed. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SET)) + continue; + break; + case SELECT_DISPLAY: /* Display selected. */ + if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED)) + continue; + break; + default: + case NO_DISPLAY: + abort(); + /* NOTREACHED */ + } + F_CLR(&sp->opts[cnt], OPT_SELECTED); + + curlen = strlen(op->name); + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (!O_ISSET(sp, cnt)) + curlen += 2; + break; + case OPT_NUM: + (void)snprintf(nbuf, + sizeof(nbuf), "%ld", O_VAL(sp, cnt)); + curlen += strlen(nbuf); + break; + case OPT_STR: + curlen += strlen(O_STR(sp, cnt)) + 3; + break; + } + /* Offset by two so there's a gap. */ + if (curlen < colwidth - 2) + s_op[s_num++] = cnt; + else + b_op[b_num++] = cnt; + } + + numcols = (sp->cols - 1) / colwidth; + if (s_num > numcols) { + numrows = s_num / numcols; + if (s_num % numcols) + ++numrows; + } else + numrows = 1; + + for (row = 0; row < numrows;) { + for (base = row, col = 0; col < numcols; ++col) { + cnt = opts_print(sp, + &optlist[s_op[base]], &sp->opts[s_op[base]]); + if ((base += numrows) >= s_num) + break; + (void)ex_printf(EXCOOKIE, + "%*s", (int)(colwidth - cnt), ""); + } + if (++row < numrows || b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + + for (row = 0; row < b_num;) { + (void)opts_print(sp, &optlist[b_op[row]], &sp->opts[b_op[row]]); + if (++row < b_num) + (void)ex_printf(EXCOOKIE, "\n"); + } + (void)ex_printf(EXCOOKIE, "\n"); +} + +/* + * opts_print -- + * Print out an option. + */ +static int +opts_print(sp, op, spo) + SCR *sp; + OPTLIST const *op; + OPTION *spo; +{ + int curlen, offset; + + curlen = 0; + offset = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + curlen += ex_printf(EXCOOKIE, + "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name); + break; + case OPT_NUM: + curlen += ex_printf(EXCOOKIE, + "%s=%ld", op->name, O_VAL(sp, offset)); + break; + case OPT_STR: + curlen += ex_printf(EXCOOKIE, + "%s=\"%s\"", op->name, O_STR(sp, offset)); + break; + } + return (curlen); +} + +/* + * opts_save -- + * Write the current configuration to a file. + */ +int +opts_save(sp, fp) + SCR *sp; + FILE *fp; +{ + OPTION *spo; + OPTLIST const *op; + int ch, cnt; + char *p; + + for (spo = sp->opts, op = optlist; op->name; ++op) { + if (F_ISSET(op, OPT_NOSAVE)) + continue; + cnt = op - optlist; + switch (op->type) { + case OPT_0BOOL: + case OPT_1BOOL: + if (O_ISSET(sp, cnt)) + (void)fprintf(fp, "set %s\n", op->name); + else + (void)fprintf(fp, "set no%s\n", op->name); + break; + case OPT_NUM: + (void)fprintf(fp, + "set %s=%-3d\n", op->name, O_VAL(sp, cnt)); + break; + case OPT_STR: + (void)fprintf(fp, "set "); + for (p = op->name; (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('=', fp); + for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) { + if (isblank(ch)) + (void)putc('\\', fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + break; + } + if (ferror(fp)) { + msgq(sp, M_ERR, "I/O error: %s", strerror(errno)); + return (1); + } + } + return (0); +} + +/* + * opts_prefix -- + * Check to see if the name is the prefix of one (and only one) + * option. If so, return the option. + */ +static OPTLIST const * +opts_prefix(name) + char *name; +{ + OPTLIST const *op, *save_op; + size_t len; + + save_op = NULL; + len = strlen(name); + for (op = optlist; op->name != NULL; ++op) { + if (op->name[0] < name[0]) + continue; + if (op->name[0] > name[0]) + break; + if (!memcmp(op->name, name, len)) { + if (save_op != NULL) + return (NULL); + save_op = op; + } + } + return (save_op); +} + +static int +opts_abbcmp(a, b) + const void *a, *b; +{ + return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name)); +} + +static int +opts_cmp(a, b) + const void *a, *b; +{ + return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name)); +} + +/* + * opts_free -- + * Free all option strings + */ +void +opts_free(sp) + SCR *sp; +{ + int cnt; + char *p; + + for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) { + p = O_STR(sp, cnt); + FREE(p, strlen(p) + 1); + } +} + +/* + * opts_copy -- + * Copy a screen's OPTION array. + */ +int +opts_copy(orig, sp) + SCR *orig, *sp; +{ + OPTION *op; + int cnt; + + /* Copy most everything without change. */ + memmove(sp->opts, orig->opts, sizeof(orig->opts)); + + /* + * Allocate copies of the strings -- keep trying to reallocate + * after ENOMEM failure, otherwise end up with more than one + * screen referencing the original memory. + */ + for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op) + if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) && + (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) { + msgq(orig, M_SYSERR, NULL); + return (1); + } + return (0); +} diff --git a/usr.bin/vi/options.h.stub b/usr.bin/vi/options.h.stub new file mode 100644 index 000000000000..f5f52b52b3d3 --- /dev/null +++ b/usr.bin/vi/options.h.stub @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h.stub 8.14 (Berkeley) 12/20/93 + */ + +struct _option { + union { + u_long val; /* Value or boolean. */ + char *str; /* String. */ + } o_u; + size_t len; /* String length. */ + +#define OPT_ALLOCATED 0x01 /* Allocated space. */ +#define OPT_SELECTED 0x02 /* Selected for display. */ +#define OPT_SET 0x04 /* Set (display for the user). */ + u_char flags; +}; + +struct _optlist { + char *name; /* Name. */ + /* Change function. */ + int (*func) __P((SCR *, OPTION *, char *, u_long)); + /* Type of object. */ + enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type; + +#define OPT_NEVER 0x01 /* Never display the option. */ +#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */ +#define OPT_NOSTR 0x04 /* String that takes a "no". */ + u_int flags; +}; + +/* Clear, set, test boolean options. */ +#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1 +#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0 +#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val) + +/* Get option values. */ +#define O_LEN(sp, o) (sp)->opts[(o)].len +#define O_STR(sp, o) (sp)->opts[(o)].o_u.str +#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val + +/* Option routines. */ +enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY }; + +int opts_copy __P((SCR *, SCR *)); +void opts_dump __P((SCR *, enum optdisp)); +void opts_free __P((SCR *)); +int opts_init __P((SCR *)); +int opts_save __P((SCR *, FILE *)); +int opts_set __P((SCR *, ARGS *[])); + +/* Per-option change routines. */ +int f_altwerase __P((SCR *, OPTION *, char *, u_long)); +int f_columns __P((SCR *, OPTION *, char *, u_long)); +int f_keytime __P((SCR *, OPTION *, char *, u_long)); +int f_leftright __P((SCR *, OPTION *, char *, u_long)); +int f_lines __P((SCR *, OPTION *, char *, u_long)); +int f_lisp __P((SCR *, OPTION *, char *, u_long)); +int f_list __P((SCR *, OPTION *, char *, u_long)); +int f_matchtime __P((SCR *, OPTION *, char *, u_long)); +int f_mesg __P((SCR *, OPTION *, char *, u_long)); +int f_modeline __P((SCR *, OPTION *, char *, u_long)); +int f_number __P((SCR *, OPTION *, char *, u_long)); +int f_optimize __P((SCR *, OPTION *, char *, u_long)); +int f_paragraph __P((SCR *, OPTION *, char *, u_long)); +int f_readonly __P((SCR *, OPTION *, char *, u_long)); +int f_ruler __P((SCR *, OPTION *, char *, u_long)); +int f_section __P((SCR *, OPTION *, char *, u_long)); +int f_shiftwidth __P((SCR *, OPTION *, char *, u_long)); +int f_sidescroll __P((SCR *, OPTION *, char *, u_long)); +int f_sourceany __P((SCR *, OPTION *, char *, u_long)); +int f_tabstop __P((SCR *, OPTION *, char *, u_long)); +int f_tags __P((SCR *, OPTION *, char *, u_long)); +int f_term __P((SCR *, OPTION *, char *, u_long)); +int f_ttywerase __P((SCR *, OPTION *, char *, u_long)); +int f_w1200 __P((SCR *, OPTION *, char *, u_long)); +int f_w300 __P((SCR *, OPTION *, char *, u_long)); +int f_w9600 __P((SCR *, OPTION *, char *, u_long)); +int f_window __P((SCR *, OPTION *, char *, u_long)); +int f_wrapmargin __P((SCR *, OPTION *, char *, u_long)); diff --git a/usr.bin/vi/options_f.c b/usr.bin/vi/options_f.c new file mode 100644 index 000000000000..efd596f9585a --- /dev/null +++ b/usr.bin/vi/options_f.c @@ -0,0 +1,547 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options_f.c 8.25 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "tag.h" + +static int opt_putenv __P((char *)); + +#define DECL(f) \ + int \ + f(sp, op, str, val) \ + SCR *sp; \ + OPTION *op; \ + char *str; \ + u_long val; +#define CALL(f) \ + f(sp, op, str, val) + +#define turnoff val + +DECL(f_altwerase) +{ + if (turnoff) + O_CLR(sp, O_ALTWERASE); + else { + O_SET(sp, O_ALTWERASE); + O_CLR(sp, O_TTYWERASE); + } + return (0); +} + +DECL(f_ttywerase) +{ + if (turnoff) + O_CLR(sp, O_TTYWERASE); + else { + O_SET(sp, O_TTYWERASE); + O_CLR(sp, O_ALTWERASE); + } + return (0); +} + +DECL(f_columns) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_COLS) { + msgq(sp, M_ERR, "Screen columns too small, less than %d.", + MINIMUM_SCREEN_COLS); + return (1); + } + if (val < O_VAL(sp, O_SHIFTWIDTH)) { + msgq(sp, M_ERR, + "Screen columns too small, less than shiftwidth."); + return (1); + } + if (val < O_VAL(sp, O_SIDESCROLL)) { + msgq(sp, M_ERR, + "Screen columns too small, less than sidescroll."); + return (1); + } + if (val < O_VAL(sp, O_TABSTOP)) { + msgq(sp, M_ERR, + "Screen columns too small, less than tabstop."); + return (1); + } + if (val < O_VAL(sp, O_WRAPMARGIN)) { + msgq(sp, M_ERR, + "Screen columns too small, less than wrapmargin."); + return (1); + } +#ifdef XXX_NOT_RIGHT + /* + * This has to be checked by reaching down into the screen code. + */ + if (val < O_NUMBER_LENGTH) { + msgq(sp, M_ERR, + "Screen columns too small, less than number option."); + return (1); + } +#endif + /* Set the columns value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_COLUMNS) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_COLUMNS) = val; + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_keytime) +{ + O_VAL(sp, O_KEYTIME) = val; + return (0); +} + +DECL(f_leftright) +{ + if (turnoff) + O_CLR(sp, O_LEFTRIGHT); + else + O_SET(sp, O_LEFTRIGHT); + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_lines) +{ + char buf[25]; + + /* Validate the number. */ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen lines too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Set the rows value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val); + if (opt_putenv(buf)) + return (1); + + /* This is expensive, don't do it unless it's necessary. */ + if (O_VAL(sp, O_LINES) == val) + return (0); + + /* Set the value. */ + O_VAL(sp, O_LINES) = val; + + /* + * If no window value set, set a new default window and, + * optionally, a new scroll value. + */ + if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) { + O_VAL(sp, O_WINDOW) = val - 1; + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = val / 2; + } + + F_SET(sp, S_RESIZE); + return (0); +} + +DECL(f_lisp) +{ + msgq(sp, M_ERR, "The lisp option is not implemented."); + return (0); +} + +DECL(f_list) +{ + if (turnoff) + O_CLR(sp, O_LIST); + else + O_SET(sp, O_LIST); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_matchtime) +{ + O_VAL(sp, O_MATCHTIME) = val; + return (0); +} + +DECL(f_mesg) +{ + struct stat sb; + char *tty; + + /* Find the tty. */ + if ((tty = ttyname(STDERR_FILENO)) == NULL) { + msgq(sp, M_ERR, "ttyname: %s.", strerror(errno)); + return (1); + } + + /* Save the tty mode for later; only save it once. */ + if (!F_ISSET(sp->gp, G_SETMODE)) { + F_SET(sp->gp, G_SETMODE); + if (stat(tty, &sb) < 0) { + msgq(sp, M_ERR, "%s: %s.", tty, strerror(errno)); + return (1); + } + sp->gp->origmode = sb.st_mode; + } + + if (turnoff) { + if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned off: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_CLR(sp, O_MESG); + } else { + if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) { + msgq(sp, M_ERR, "messages not turned on: %s: %s.", + tty, strerror(errno)); + return (1); + } + O_SET(sp, O_MESG); + } + return (0); +} + +/* + * f_modeline -- + * This has been documented in historical systems as both "modeline" + * and as "modelines". Regardless of the name, this option represents + * a security problem of mammoth proportions, not to mention a stunning + * example of what your intro CS professor referred to as the perils of + * mixing code and data. Don't add it, or I will kill you. + */ +DECL(f_modeline) +{ + if (!turnoff) + msgq(sp, M_ERR, "The modeline(s) option may never be set."); + return (0); +} + +DECL(f_number) +{ + if (turnoff) + O_CLR(sp, O_NUMBER); + else + O_SET(sp, O_NUMBER); + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_optimize) +{ + msgq(sp, M_ERR, "The optimize option is not implemented."); + return (0); +} + +DECL(f_paragraph) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Paragraph options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED)) + free(O_STR(sp, O_PARAGRAPHS)); + if ((O_STR(sp, O_PARAGRAPHS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_PARAGRAPHS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_readonly) +{ + if (turnoff) { + O_CLR(sp, O_READONLY); + if (sp->frp != NULL) + F_CLR(sp->frp, FR_RDONLY); + } else { + O_SET(sp, O_READONLY); + if (sp->frp != NULL) + F_SET(sp->frp, FR_RDONLY); + } + return (0); +} + +DECL(f_ruler) +{ + if (turnoff) + O_CLR(sp, O_RULER); + else + O_SET(sp, O_RULER); + return (0); +} + +DECL(f_section) +{ + if (strlen(str) & 1) { + msgq(sp, M_ERR, + "Section options must be in sets of two characters."); + return (1); + } + + if (F_ISSET(&sp->opts[O_SECTIONS], OPT_ALLOCATED)) + free(O_STR(sp, O_SECTIONS)); + if ((O_STR(sp, O_SECTIONS) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_SECTIONS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_shiftwidth) +{ + if (val == 0) { + msgq(sp, M_ERR, "The shiftwidth can't be set to 0."); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Shiftwidth can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SHIFTWIDTH) = val; + return (0); +} + +DECL(f_sidescroll) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Sidescroll can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_SIDESCROLL) = val; + return (0); +} + +/* + * f_sourceany -- + * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they + * were owned by the user. The sourceany option was an undocumented + * feature of historic vi which permitted the startup source'ing of + * .exrc files the user didn't own. This is an obvious security problem, + * and we ignore the option. + */ +DECL(f_sourceany) +{ + if (!turnoff) + msgq(sp, M_ERR, "The sourceany option may never be set."); + return (0); +} + +DECL(f_tabstop) +{ + if (val == 0) { + msgq(sp, M_ERR, "Tab stops can't be set to 0."); + return (1); + } +#define MAXTABSTOP 20 + if (val > MAXTABSTOP) { + msgq(sp, M_ERR, + "Tab stops can't be larger than %d.", MAXTABSTOP); + return (1); + } + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Tab stops can't be larger than screen size.", + MAXTABSTOP); + return (1); + } + O_VAL(sp, O_TABSTOP) = val; + + F_SET(sp, S_REFORMAT | S_REDRAW); + return (0); +} + +DECL(f_tags) +{ + char *p; + + /* Copy for user display. */ + p = O_STR(sp, O_TAGS); + if ((O_STR(sp, O_TAGS) = strdup(str)) == NULL) { + O_STR(sp, O_TAGS) = p; + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (F_ISSET(&sp->opts[O_TAGS], OPT_ALLOCATED)) + FREE(p, strlen(p) + 1); + F_SET(&sp->opts[O_TAGS], OPT_ALLOCATED | OPT_SET); + return (0); +} + +DECL(f_term) +{ + char buf[256]; + + if (F_ISSET(&sp->opts[O_TERM], OPT_ALLOCATED)) + free(O_STR(sp, O_TERM)); + if ((O_STR(sp, O_TERM) = strdup(str)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + F_SET(&sp->opts[O_TERM], OPT_ALLOCATED | OPT_SET); + + /* Set the terminal value in the environment for curses. */ + (void)snprintf(buf, sizeof(buf), "TERM=%s", str); + if (opt_putenv(buf)) + return (1); + + if (set_window_size(sp, 0, 0)) + return (1); + return (0); +} + +DECL(f_w300) +{ + /* Historical behavior for w300 was < 1200. */ + if (baud_from_bval(sp) >= 1200) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W300) = val; + return (0); +} + +DECL(f_w1200) +{ + u_long v; + + /* Historical behavior for w1200 was == 1200. */ + v = baud_from_bval(sp); + if (v < 1200 || v > 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W1200) = val; + return (0); +} + +DECL(f_w9600) +{ + speed_t v; + + /* Historical behavior for w9600 was > 1200. */ + v = baud_from_bval(sp); + if (v <= 4800) + return (0); + + if (CALL(f_window)) + return (1); + + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_W9600) = val; + return (0); +} + +DECL(f_window) +{ + if (val < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Window too small, less than %d.", + MINIMUM_SCREEN_ROWS); + return (1); + } + if (val > O_VAL(sp, O_LINES) - 1) + val = O_VAL(sp, O_LINES) - 1; + O_VAL(sp, O_WINDOW) = val; + O_VAL(sp, O_SCROLL) = val / 2; + + return (0); +} + +DECL(f_wrapmargin) +{ + if (val > O_VAL(sp, O_COLUMNS)) { + msgq(sp, M_ERR, + "Wrapmargin value can't be larger than screen size."); + return (1); + } + O_VAL(sp, O_WRAPMARGIN) = val; + return (0); +} + +/* + * opt_putenv -- + * Put a value into the environment. We use putenv(3) because it's + * more portable. The following hack is because some moron decided + * to keep a reference to the memory passed to putenv(3), instead of + * having it allocate its own. Someone clearly needs to get promoted + * into management. + */ +static int +opt_putenv(s) + char *s; +{ + char *t; + + /* Memory leak. */ + if ((t = strdup(s)) == NULL) + return (1); + return (putenv(t)); +} diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h new file mode 100644 index 000000000000..be6352b9dfc1 --- /dev/null +++ b/usr.bin/vi/pathnames.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.5 (Berkeley) 12/21/93 + */ + +#define _PATH_BSHELL "/bin/sh" +#define _PATH_DEVNULL "/dev/null" +#define _PATH_EXRC ".exrc" +#define _PATH_NEXRC ".nexrc" +#define _PATH_PRESERVE "/var/tmp/vi.recover" +#define _PATH_SENDMAIL "/usr/sbin/sendmail" +#define _PATH_SYSEXRC "/etc/vi.exrc" +#define _PATH_TAGS "tags /var/db/libc.tags /sys/kern/tags" +#define _PATH_TMP "/tmp" +#define _PATH_TTY "/dev/tty" diff --git a/usr.bin/vi/recover.c b/usr.bin/vi/recover.c new file mode 100644 index 000000000000..f9a483236abe --- /dev/null +++ b/usr.bin/vi/recover.c @@ -0,0 +1,622 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)recover.c 8.40 (Berkeley) 12/21/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> + +/* + * We include <sys/file.h>, because the flock(2) #defines were + * found there on historical systems. We also include <fcntl.h> + * because the open(2) #defines are found there on newer systems. + */ +#include <sys/file.h> + +#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "pathnames.h" + +/* + * Recovery code. + * + * The basic scheme is there's a btree file, whose name we specify. The first + * time a file is modified, and then at RCV_PERIOD intervals after that, the + * btree file is synced to disk. Each time a keystroke is requested for a file + * the terminal routines check to see if the file needs to be synced. This, of + * course means that the data structures had better be consistent each time the + * key routines are called. + * + * We don't use timers other than to flag that the file should be synced. This + * would require that the SCR and EXF data structures be locked, the dbopen(3) + * routines lock out the timers for each update, etc. It's just not worth it. + * The only way we can lose in the current scheme is if the file is saved, then + * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more. + * Not likely. + * + * When a file is first modified, a file which can be handed off to the mailer + * is created. The file contains normal headers, with two additions, which + * occur in THIS order, as the FIRST TWO headers: + * + * Vi-recover-file: file_name + * Vi-recover-path: recover_path + * + * Since newlines delimit the headers, this means that file names cannot + * have newlines in them, but that's probably okay. + * + * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX". + */ + +#define VI_FHEADER "Vi-recover-file: " +#define VI_PHEADER "Vi-recover-path: " + +static void rcv_alrm __P((int)); +static int rcv_mailfile __P((SCR *, EXF *)); +static void rcv_syncit __P((SCR *, int)); + +/* + * rcv_tmp -- + * Build a file name that will be used as the recovery file. + */ +int +rcv_tmp(sp, ep, name) + SCR *sp; + EXF *ep; + char *name; +{ + struct stat sb; + int fd; + char *dp, *p, path[MAXPATHLEN]; + + /* + * If the recovery directory doesn't exist, try and create it. As + * the recovery files are themselves protected from reading/writing + * by other than the owner, the worst that can happen is that a user + * would have permission to remove other users recovery files. If + * the sticky bit has the BSD semantics, that too will be impossible. + */ + dp = O_STR(sp, O_RECDIR); + if (stat(dp, &sb)) { + if (errno != ENOENT || mkdir(dp, 0)) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); + } + + /* Newlines delimit the mail messages. */ + for (p = name; *p; ++p) + if (*p == '\n') { + msgq(sp, M_ERR, + "Files with newlines in the name are unrecoverable."); + return (1); + } + + (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp); + + /* + * !!! + * We depend on mkstemp(3) setting the permissions correctly. + * GP's, we do it ourselves, to keep the window as small as + * possible. + */ + if ((fd = mkstemp(path)) == -1) { + msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); + return (1); + } + (void)chmod(path, S_IRUSR | S_IWUSR); + (void)close(fd); + + if ((ep->rcv_path = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)unlink(path); + return (1); + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_init -- + * Force the file to be snapshotted for recovery. + */ +int +rcv_init(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + struct sigaction act; + recno_t lno; + + F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON); + + /* Build file to mail to the user. */ + if (rcv_mailfile(sp, ep)) + goto err; + + /* Force read of entire file. */ + if (file_lline(sp, ep, &lno)) + goto err; + + /* Turn on a busy message, and sync it to backing store. */ + busy_on(sp, 1, "Copying file for recovery..."); + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Preservation failed: %s: %s", + ep->rcv_path, strerror(errno)); + busy_off(sp); + goto err; + } + busy_off(sp); + + if (!F_ISSET(sp->gp, G_RECOVER_SET)) { + /* Install the recovery timer handler. */ + act.sa_handler = rcv_alrm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, NULL); + + /* Start the recovery timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, NULL)) { + msgq(sp, M_ERR, + "Error: setitimer: %s", strerror(errno)); +err: msgq(sp, M_ERR, + "Recovery after system crash not possible."); + return (1); + } + } + + /* We believe the file is recoverable. */ + F_SET(ep, F_RCV_ON); + return (0); +} + +/* + * rcv_alrm -- + * Recovery timer interrupt handler. + */ +static void +rcv_alrm(signo) + int signo; +{ + F_SET(__global_list, G_SIGALRM); +} + +/* + * rcv_mailfile -- + * Build the file to mail to the user. + */ +static int +rcv_mailfile(sp, ep) + SCR *sp; + EXF *ep; +{ + struct passwd *pw; + uid_t uid; + FILE *fp; + time_t now; + int fd; + char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN]; + + if ((pw = getpwuid(uid = getuid())) == NULL) { + msgq(sp, M_ERR, "Information on user id %u not found.", uid); + return (1); + } + + (void)snprintf(path, sizeof(path), + "%s/recover.XXXXXX", O_STR(sp, O_RECDIR)); + if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) { + msgq(sp, M_ERR, + "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + if (fd != -1) + (void)close(fd); + return (1); + } + + /* + * We keep an open lock on the file so that the recover option can + * distinguish between files that are live and those that need to + * be recovered. There's an obvious window between the mkstemp call + * and the lock, but it's pretty small. + */ + if ((ep->rcv_fd = dup(fd)) != -1) + (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB); + + if ((ep->rcv_mpath = strdup(path)) == NULL) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + + t = FILENAME(sp->frp); + if ((p = strrchr(t, '/')) == NULL) + p = t; + else + ++p; + (void)time(&now); + (void)gethostname(host, sizeof(host)); + (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n", + VI_FHEADER, p, /* Non-standard. */ + VI_PHEADER, ep->rcv_path, /* Non-standard. */ + "Reply-To: root", + "From: root (Nvi recovery program)", + "To: ", pw->pw_name, + "Subject: Nvi saved the file ", p, + "Precedence: bulk"); /* For vacation(1). */ + (void)fprintf(fp, "%s%.24s%s%s\n%s%s", + "On ", ctime(&now), + ", the user ", pw->pw_name, + "was editing a file named ", p); + if (p != t) + (void)fprintf(fp, " (%s)", t); + (void)fprintf(fp, "\n%s%s%s\n", + "on the machine ", host, ", when it was saved for\nrecovery."); + (void)fprintf(fp, "\n%s\n%s\n%s\n\n", + "You can recover most, if not all, of the changes", + "to this file using the -l and -r options to nvi(1)", + "or nex(1)."); + + if (fflush(fp) || ferror(fp)) { + msgq(sp, M_SYSERR, NULL); + (void)fclose(fp); + return (1); + } + return (0); +} + +/* + * rcv_sync -- + * Sync the backing file. + */ +int +rcv_sync(sp, ep) + SCR *sp; + EXF *ep; +{ + struct itimerval value; + + if (ep->db->sync(ep->db, R_RECNOSYNC)) { + msgq(sp, M_ERR, "Automatic file backup failed: %s: %s", + ep->rcv_path, strerror(errno)); + value.it_interval.tv_sec = value.it_interval.tv_usec = 0; + value.it_value.tv_sec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + F_CLR(ep, F_RCV_ON); + return (1); + } + return (0); +} + +/* + * rcv_hup -- + * Recovery SIGHUP interrupt handler. (Modem line dropped, or + * xterm window closed.) + */ +void +rcv_hup() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. Send email to the user for each file saved. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 1); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGHUP, SIG_DFL); + (void)kill(0, SIGHUP); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_term -- + * Recovery SIGTERM interrupt handler. (Reboot or halt is running.) + */ +void +rcv_term() +{ + SCR *sp; + + /* + * Walk the lists of screens, sync'ing the files; only sync + * each file once. + */ + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + for (sp = __global_list->hq.cqh_first; + sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) + rcv_syncit(sp, 0); + + /* + * Die with the proper exit status. Don't bother using + * sigaction(2) 'cause we want the default behavior. + */ + (void)signal(SIGTERM, SIG_DFL); + (void)kill(0, SIGTERM); + + /* NOTREACHED */ + exit (1); +} + +/* + * rcv_syncit -- + * Sync the file, optionally send mail. + */ +static void +rcv_syncit(sp, email) + SCR *sp; + int email; +{ + EXF *ep; + char comm[1024]; + + if ((ep = sp->ep) == NULL || + !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON)) + return; + + (void)ep->db->sync(ep->db, R_RECNOSYNC); + F_SET(ep, F_RCV_NORM); + + /* + * !!! + * If you need to port this to a system that doesn't have sendmail, + * the -t flag being used causes sendmail to read the message for + * the recipients instead of us specifying them some other way. + */ + if (email) { + (void)snprintf(comm, sizeof(comm), + "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath); + (void)system(comm); + } + (void)file_end(sp, ep, 1); +} + +/* + * people making love + * never exactly the same + * just like a snowflake + * + * rcv_list -- + * List the files that can be recovered by this user. + */ +int +rcv_list(sp) + SCR *sp; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FILE *fp; + int found; + char *p, file[1024]; + + if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) { + (void)fprintf(stderr, + "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + for (found = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + if ((fp = fopen(dp->d_name, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the header, get the file name. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL) { + (void)fprintf(stderr, + "vi: %s: malformed recovery file.\n", dp->d_name); + goto next; + } + *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + (void)fprintf(stderr, + "vi: %s: %s\n", dp->d_name, strerror(errno)); + goto next; + } + + /* Display. */ + (void)printf("%s: %s", + file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime)); + found = 1; + +next: (void)fclose(fp); + } + if (found == 0) + (void)printf("vi: no files to recover.\n"); + (void)closedir(dirp); + return (0); +} + +/* + * rcv_read -- + * Start a recovered file as the file to edit. + */ +int +rcv_read(sp, name) + SCR *sp; + char *name; +{ + struct dirent *dp; + struct stat sb; + DIR *dirp; + FREF *frp; + FILE *fp; + time_t rec_mtime; + int found, requested; + char *p, *t, *recp, *pathp; + char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN]; + + if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) { + msgq(sp, M_ERR, + "%s: %s", O_STR(sp, O_RECDIR), strerror(errno)); + return (1); + } + + recp = pathp = NULL; + for (found = requested = 0; (dp = readdir(dirp)) != NULL;) { + if (strncmp(dp->d_name, "recover.", 8)) + continue; + + /* If it's readable, it's recoverable. */ + (void)snprintf(recpath, sizeof(recpath), + "%s/%s", O_STR(sp, O_RECDIR), dp->d_name); + if ((fp = fopen(recpath, "r")) == NULL) + continue; + + /* If it's locked, it's live. */ + if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { + (void)fclose(fp); + continue; + } + + /* Check the headers. */ + if (fgets(file, sizeof(file), fp) == NULL || + strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || + (p = strchr(file, '\n')) == NULL || + fgets(path, sizeof(path), fp) == NULL || + strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) || + (t = strchr(path, '\n')) == NULL) { + msgq(sp, M_ERR, + "%s: malformed recovery file.", recpath); + goto next; + } + ++found; + *t = *p = '\0'; + + /* Get the last modification time. */ + if (fstat(fileno(fp), &sb)) { + msgq(sp, M_ERR, + "vi: %s: %s", dp->d_name, strerror(errno)); + goto next; + } + + /* Check the file name. */ + if (strcmp(file + sizeof(VI_FHEADER) - 1, name)) + goto next; + + ++requested; + + /* If we've found more than one, take the most recent. */ + if (recp == NULL || rec_mtime < sb.st_mtime) { + p = recp; + t = pathp; + if ((recp = strdup(recpath)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + recp = p; + goto next; + } + if ((pathp = strdup(path)) == NULL) { + msgq(sp, M_ERR, + "vi: Error: %s.\n", strerror(errno)); + FREE(recp, strlen(recp) + 1); + recp = p; + pathp = t; + goto next; + } + if (p != NULL) { + FREE(p, strlen(p) + 1); + FREE(t, strlen(t) + 1); + } + rec_mtime = sb.st_mtime; + } + +next: (void)fclose(fp); + } + (void)closedir(dirp); + + if (recp == NULL) { + msgq(sp, M_INFO, + "No files named %s, owned by you, to edit.", name); + return (1); + } + if (found) { + if (requested > 1) + msgq(sp, M_INFO, + "There are older versions of this file for you to recover."); + if (found > requested) + msgq(sp, M_INFO, + "There are other files that you can recover."); + } + + /* Create the FREF structure, start the btree file. */ + if ((frp = file_add(sp, NULL, name, 0)) == NULL || + file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) { + FREE(recp, strlen(recp) + 1); + FREE(pathp, strlen(pathp) + 1); + return (1); + } + sp->ep->rcv_mpath = recp; + return (0); +} diff --git a/usr.bin/vi/screen.c b/usr.bin/vi/screen.c new file mode 100644 index 000000000000..e18bd02183dd --- /dev/null +++ b/usr.bin/vi/screen.c @@ -0,0 +1,296 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)screen.c 8.51 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "tag.h" + +/* + * screen_init -- + * Do the default initialization of an SCR structure. + */ +int +screen_init(orig, spp, flags) + SCR *orig, **spp; + u_int flags; +{ + SCR *sp; + size_t len; + + *spp = NULL; + CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR)); + *spp = sp; + +/* INITIALIZED AT SCREEN CREATE. */ + sp->gp = __global_list; /* All ref the GS structure. */ + + LIST_INIT(&sp->msgq); + CIRCLEQ_INIT(&sp->frefq); + + sp->ccnt = 2; /* Anything > 1 */ + + FD_ZERO(&sp->rdfd); + + CIRCLEQ_INIT(&sp->tiq); + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + sp->searchdir = NOTSET; + sp->csearchdir = CNOTSET; + + switch (flags & S_SCREENS) { + case S_EX: + if (sex_screen_init(sp)) + return (1); + break; + case S_VI_CURSES: + if (svi_screen_init(sp)) + return (1); + break; + case S_VI_XAW: + if (xaw_screen_init(sp)) + return (1); + break; + default: + abort(); + } + + sp->flags = flags; + } else { + if (orig->alt_name != NULL && + (sp->alt_name = strdup(orig->alt_name)) == NULL) + goto mem; + + /* Retain all searching/substitution information. */ + if (F_ISSET(orig, S_SRE_SET)) { + F_SET(sp, S_SRE_SET); + sp->sre = orig->sre; + } + if (F_ISSET(orig, S_SUBRE_SET)) { + F_SET(sp, S_SUBRE_SET); + sp->subre = orig->subre; + } + sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD; + sp->csearchdir = CNOTSET; + sp->lastckey = orig->lastckey; + + if (orig->matchsize) { + len = orig->matchsize * sizeof(regmatch_t); + MALLOC(sp, sp->match, regmatch_t *, len); + if (sp->match == NULL) + goto mem; + sp->matchsize = orig->matchsize; + memmove(sp->match, orig->match, len); + } + if (orig->repl_len) { + MALLOC(sp, sp->repl, char *, orig->repl_len); + if (sp->repl == NULL) + goto mem; + sp->repl_len = orig->repl_len; + memmove(sp->repl, orig->repl, orig->repl_len); + } + if (orig->newl_len) { + len = orig->newl_len * sizeof(size_t); + MALLOC(sp, sp->newl, size_t *, len); + if (sp->newl == NULL) + goto mem; + sp->newl_len = orig->newl_len; + sp->newl_cnt = orig->newl_cnt; + memmove(sp->newl, orig->newl, len); + } + + sp->saved_vi_mode = orig->saved_vi_mode; + + if (opts_copy(orig, sp)) { +mem: msgq(orig, M_SYSERR, "new screen attributes"); + (void)screen_end(sp); + return (1); + } + + sp->s_bell = orig->s_bell; + sp->s_bg = orig->s_bg; + sp->s_busy = orig->s_busy; + sp->s_change = orig->s_change; + sp->s_chposition = orig->s_chposition; + sp->s_clear = orig->s_clear; + sp->s_column = orig->s_column; + sp->s_confirm = orig->s_confirm; + sp->s_down = orig->s_down; + sp->s_edit = orig->s_edit; + sp->s_end = orig->s_end; + sp->s_ex_cmd = orig->s_ex_cmd; + sp->s_ex_run = orig->s_ex_run; + sp->s_ex_write = orig->s_ex_write; + sp->s_fg = orig->s_fg; + sp->s_fill = orig->s_fill; + sp->s_get = orig->s_get; + sp->s_key_read = orig->s_key_read; + sp->s_optchange = orig->s_optchange; + sp->s_position = orig->s_position; + sp->s_rabs = orig->s_rabs; + sp->s_refresh = orig->s_refresh; + sp->s_relative = orig->s_relative; + sp->s_rrel = orig->s_rrel; + sp->s_split = orig->s_split; + sp->s_suspend = orig->s_suspend; + sp->s_up = orig->s_up; + + F_SET(sp, F_ISSET(orig, S_SCREENS)); + } + + if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */ + return (1); + if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */ + return (1); + if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */ + return (1); + if (v_screen_copy(orig, sp)) /* Init vi. */ + return (1); + if (ex_screen_copy(orig, sp)) /* Init ex. */ + return (1); + + *spp = sp; + return (0); +} + +/* + * screen_end -- + * Release a screen. + */ +int +screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */ + rval = 1; + if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */ + rval = 1; + if (sex_screen_end(sp)) /* End S_EX screen. */ + rval = 1; + if (v_screen_end(sp)) /* End vi. */ + rval = 1; + if (ex_screen_end(sp)) /* End ex. */ + rval = 1; + + /* Free FREF's. */ + { FREF *frp; + while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) { + CIRCLEQ_REMOVE(&sp->frefq, frp, q); + if (frp->cname != NULL) + free(frp->cname); + if (frp->name != NULL) + free(frp->name); + if (frp->tname != NULL) + free(frp->tname); + FREE(frp, sizeof(FREF)); + } + } + + /* Free any text input. */ + text_lfree(&sp->tiq); + + /* Free any script information. */ + if (F_ISSET(sp, S_SCRIPT)) + sscr_end(sp); + + /* Free alternate file name. */ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + + /* Free up search information. */ + if (sp->match != NULL) + FREE(sp->match, sizeof(regmatch_t)); + if (sp->repl != NULL) + FREE(sp->repl, sp->repl_len); + if (sp->newl != NULL) + FREE(sp->newl, sp->newl_len); + + /* Free all the options */ + opts_free(sp); + + /* + * Free the message chain last, so previous failures have a place + * to put messages. Copy messages to (in order) a related screen, + * any screen, the global area. + */ + { SCR *c_sp; MSG *mp, *next; + if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else if ((c_sp = + sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) { + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(c_sp, S_BELLSCHED); + } else { + c_sp = NULL; + if (F_ISSET(sp, S_BELLSCHED)) + F_SET(sp->gp, G_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, c_sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + FREE(mp->mbuf, mp->blen); + FREE(mp, sizeof(MSG)); + } + } + + /* Remove the screen from the displayed queue. */ + CIRCLEQ_REMOVE(&sp->gp->dq, sp, q); + + /* Free the screen itself. */ + FREE(sp, sizeof(SCR)); + + return (rval); +} diff --git a/usr.bin/vi/screen.h b/usr.bin/vi/screen.h new file mode 100644 index 000000000000..d95179352b48 --- /dev/null +++ b/usr.bin/vi/screen.h @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)screen.h 8.81 (Berkeley) 12/29/93 + */ + +/* + * There are minimum values that vi has to have to display a screen. The + * row minimum is fixed at 1 line for the text, and 1 line for any error + * messages. The column calculation is a lot trickier. For example, you + * have to have enough columns to display the line number, not to mention + * guaranteeing that tabstop and shiftwidth values are smaller than the + * current column value. It's a lot simpler to have a fixed value and not + * worry about it. + * + * XXX + * MINIMUM_SCREEN_COLS is probably wrong. + */ +#define MINIMUM_SCREEN_ROWS 2 +#define MINIMUM_SCREEN_COLS 20 + /* Line operations. */ +enum operation { LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET }; + /* Position values. */ +enum position { P_BOTTOM, P_FILL, P_MIDDLE, P_TOP }; + +/* + * Structure for holding file references. Each SCR structure contains a + * linked list of these (the user's argument list) as well as pointers to + * the current and previous files. The structure contains the name of the + * file, along with the information that follows the name. A file has up + * to three "names". The tname field is the path of the temporary backing + * file, if any. The name field is the name the user originally used to + * specify the file to be edited. The cname field is the changed name if + * the user changed the name. + * + * Note that the read-only bit follows the file name, not the file itself. + * + * XXX + * The mtime field should be a struct timespec, but time_t is more portable. + */ +struct _fref { + CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */ + char *cname; /* Changed file name. */ + char *name; /* File name. */ + char *tname; /* Temporary file name. */ + + recno_t lno; /* 1-N: file cursor line. */ + size_t cno; /* 0-N: file cursor column. */ + time_t mtime; /* Last modification time. */ + +#define FR_CHANGEWRITE 0x001 /* Name changed and then written. */ +#define FR_CURSORSET 0x002 /* If lno/cno valid. */ +#define FR_EDITED 0x004 /* If the file was ever edited. */ +#define FR_IGNORE 0x008 /* File isn't part of argument list. */ +#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */ +#define FR_RDONLY 0x020 /* File is read-only. */ + u_int flags; +}; + +/* + * There's a file name hierarchy -- if the user has changed the name, we + * use it, otherwise, we use the original name, if there was one, othewise + * use the temporary name. + */ +#define FILENAME(frp) \ + ((frp)->cname != NULL) ? (frp)->cname : \ + ((frp)->name != NULL) ? (frp)->name : (frp)->tname + +/* + * SCR -- + * The screen structure. To the extent possible, all screen information + * is stored in the various private areas. The only information here + * is used by global routines or is shared by too many screens. + */ +struct _scr { +/* INITIALIZED AT SCREEN CREATE. */ + CIRCLEQ_ENTRY(_scr) q; /* Screens. */ + + GS *gp; /* Pointer to global area. */ + + SCR *nextdisp; /* Next display screen. */ + + EXF *ep; /* Screen's current EXF structure. */ + + MSGH msgq; /* Message list. */ + /* FREF list. */ + CIRCLEQ_HEAD(_frefh, _fref) frefq; + FREF *frp; /* FREF being edited. */ + FREF *a_frp; /* Last argument list FREF edited. */ + FREF *p_frp; /* Last FREF edited. */ + + u_long ccnt; /* Command count. */ + u_long q_ccnt; /* Quit or ZZ command count. */ + + /* Screen's: */ + size_t rows; /* 1-N: number of rows. */ + size_t cols; /* 1-N: number of columns. */ + size_t woff; /* 0-N: row offset in screen. */ + size_t t_rows; /* 1-N: cur number of text rows. */ + size_t t_maxrows; /* 1-N: max number of text rows. */ + size_t t_minrows; /* 1-N: min number of text rows. */ + + /* Cursor's: */ + recno_t lno; /* 1-N: file line. */ + size_t cno; /* 0-N: file character in line. */ + + size_t rcm; /* Vi: 0-N: Column suck. */ +#define RCM_FNB 0x01 /* Column suck: first non-blank. */ +#define RCM_LAST 0x02 /* Column suck: last. */ + u_int rcmflags; + +#define L_ADDED 0 /* Added lines. */ +#define L_CHANGED 1 /* Changed lines. */ +#define L_COPIED 2 /* Copied lines. */ +#define L_DELETED 3 /* Deleted lines. */ +#define L_JOINED 4 /* Joined lines. */ +#define L_MOVED 5 /* Moved lines. */ +#define L_PUT 6 /* Put lines. */ +#define L_LSHIFT 7 /* Left shift lines. */ +#define L_RSHIFT 8 /* Right shift lines. */ +#define L_YANKED 9 /* Yanked lines. */ + recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */ + + FILE *stdfp; /* Ex output file pointer. */ + + char *if_name; /* Ex input file name, for messages. */ + recno_t if_lno; /* Ex input file line, for messages. */ + + fd_set rdfd; /* Ex/vi: read fd select mask. */ + + TEXTH tiq; /* Ex/vi: text input queue. */ + + SCRIPT *script; /* Vi: script mode information .*/ + + char const *time_msg; /* ITIMER_REAL message. */ + struct itimerval time_value; /* ITIMER_REAL saved value. */ + struct sigaction time_handler; /* ITIMER_REAL saved handler. */ + + void *vi_private; /* Vi private area. */ + void *ex_private; /* Ex private area. */ + void *svi_private; /* Vi curses screen private area. */ + void *xaw_private; /* Vi XAW screen private area. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + char *alt_name; /* Ex/vi: alternate file name. */ + + /* Ex/vi: search/substitute info. */ + regex_t sre; /* Last search RE. */ + regex_t subre; /* Last substitute RE. */ + enum direction searchdir; /* File search direction. */ + enum cdirection csearchdir; /* Character search direction. */ + CHAR_T lastckey; /* Last search character. */ + regmatch_t *match; /* Substitute match array. */ + size_t matchsize; /* Substitute match array size. */ + char *repl; /* Substitute replacement. */ + size_t repl_len; /* Substitute replacement length.*/ + size_t *newl; /* Newline offset array. */ + size_t newl_len; /* Newline array size. */ + size_t newl_cnt; /* Newlines in replacement. */ + + u_int saved_vi_mode; /* Saved vi display type. */ + + OPTION opts[O_OPTIONCOUNT]; /* Options. */ + +/* + * SCREEN SUPPORT ROUTINES. + * + * A SCR * MUST be the first argument to these routines. + */ + /* Ring the screen bell. */ + void (*s_bell) __P((SCR *)); + /* Background the screen. */ + int (*s_bg) __P((SCR *)); + /* Put up a busy message. */ + int (*s_busy) __P((SCR *, char const *)); + /* Change a screen line. */ + int (*s_change) __P((SCR *, EXF *, recno_t, enum operation)); + /* Return column close to specified. */ + size_t (*s_chposition) __P((SCR *, EXF *, recno_t, size_t)); + /* Clear the screen. */ + int (*s_clear) __P((SCR *)); + /* Return the logical cursor column. */ + int (*s_column) __P((SCR *, EXF *, size_t *)); + enum confirm /* Confirm an action with the user. */ + (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *)); + /* Move down the screen. */ + int (*s_down) __P((SCR *, EXF *, MARK *, recno_t, int)); + /* Edit a file. */ + int (*s_edit) __P((SCR *, EXF *)); + /* End a screen. */ + int (*s_end) __P((SCR *)); + /* Run a single ex command. */ + int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *)); + /* Run user's ex commands. */ + int (*s_ex_run) __P((SCR *, EXF *, MARK *)); + /* Screen's ex write function. */ + int (*s_ex_write) __P((void *, const char *, int)); + /* Foreground the screen. */ + int (*s_fg) __P((SCR *, CHAR_T *)); + /* Fill the screen's map. */ + int (*s_fill) __P((SCR *, EXF *, recno_t, enum position)); + enum input /* Get a line from the user. */ + (*s_get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + enum input /* Get a key from the user. */ + (*s_key_read) __P((SCR *, int *, struct timeval *)); + /* Tell the screen an option changed. */ + int (*s_optchange) __P((SCR *, int)); + /* Return column at screen position. */ + int (*s_position) __P((SCR *, EXF *, + MARK *, u_long, enum position)); + /* Change the absolute screen size. */ + int (*s_rabs) __P((SCR *, long)); + /* Refresh the screen. */ + int (*s_refresh) __P((SCR *, EXF *)); + /* Return column close to last char. */ + size_t (*s_relative) __P((SCR *, EXF *, recno_t)); + /* Change the relative screen size. */ + int (*s_rrel) __P((SCR *, long)); + /* Split the screen. */ + int (*s_split) __P((SCR *, ARGS *[])); + /* Suspend the screen. */ + int (*s_suspend) __P((SCR *)); + /* Move up the screen. */ + int (*s_up) __P((SCR *, EXF *, MARK *, recno_t, int)); + +/* Editor screens. */ +#define S_EX 0x0000001 /* Ex screen. */ +#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */ +#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */ + +#define IN_EX_MODE(sp) /* If in ex mode. */ \ + (F_ISSET(sp, S_EX)) +#define IN_VI_MODE(sp) /* If in vi mode. */ \ + (F_ISSET(sp, S_VI_CURSES | S_VI_XAW)) +#define S_SCREENS /* Screens. */ \ + (S_EX | S_VI_CURSES | S_VI_XAW) + +/* Major screen/file changes. */ +#define S_EXIT 0x0000008 /* Exiting (not forced). */ +#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */ +#define S_FSWITCH 0x0000020 /* Switch files. */ +#define S_SSWITCH 0x0000040 /* Switch screens. */ +#define S_MAJOR_CHANGE /* Screen or file changes. */ \ + (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH) + +#define S_BELLSCHED 0x0000080 /* Bell scheduled. */ +#define S_CONTINUE 0x0000100 /* Need to ask the user to continue. */ +#define S_EXSILENT 0x0000200 /* Ex batch script. */ +#define S_GLOBAL 0x0000400 /* Doing a global command. */ +#define S_INPUT 0x0000800 /* Doing text input. */ +#define S_INTERRUPTED 0x0001000 /* If have been interrupted. */ +#define S_INTERRUPTIBLE 0x0002000 /* If can be interrupted. */ +#define S_REDRAW 0x0004000 /* Redraw the screen. */ +#define S_REFORMAT 0x0008000 /* Reformat the screen. */ +#define S_REFRESH 0x0010000 /* Refresh the screen. */ +#define S_RENUMBER 0x0020000 /* Renumber the screen. */ +#define S_RESIZE 0x0040000 /* Resize the screen. */ +#define S_SCRIPT 0x0080000 /* Window is a shell script. */ +#define S_SRE_SET 0x0100000 /* The search RE has been set. */ +#define S_SUBRE_SET 0x0200000 /* The substitute RE has been set. */ +#define S_TIMER_SET 0x0400000 /* If a busy timer is running. */ +#define S_UPDATE_MODE 0x0800000 /* Don't repaint modeline. */ + u_int flags; +}; + +/* Generic routines to start/stop a screen. */ +int screen_end __P((SCR *)); +int screen_init __P((SCR *, SCR **, u_int)); + +/* Public interfaces to the underlying screens. */ +int ex_screen_copy __P((SCR *, SCR *)); +int ex_screen_end __P((SCR *)); +int ex_screen_init __P((SCR *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_end __P((SCR *)); +int sex_screen_init __P((SCR *)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_end __P((SCR *)); +int svi_screen_init __P((SCR *)); +int v_screen_copy __P((SCR *, SCR *)); +int v_screen_end __P((SCR *)); +int v_screen_init __P((SCR *)); +int xaw_screen_copy __P((SCR *, SCR *)); +int xaw_screen_end __P((SCR *)); +int xaw_screen_init __P((SCR *)); diff --git a/usr.bin/vi/search.c b/usr.bin/vi/search.c new file mode 100644 index 000000000000..22aadef48751 --- /dev/null +++ b/usr.bin/vi/search.c @@ -0,0 +1,860 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)search.c 8.32 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "interrupt.h" + +static int check_delta __P((SCR *, EXF *, long, recno_t)); +static int ctag_conv __P((SCR *, char **, int *)); +static int get_delta __P((SCR *, char **, long *, u_int *)); +static int resetup __P((SCR *, regex_t **, enum direction, + char *, char **, long *, u_int *)); +static void search_intr __P((int)); + +/* + * resetup -- + * Set up a search for a regular expression. + */ +static int +resetup(sp, rep, dir, ptrn, epp, deltap, flagp) + SCR *sp; + regex_t **rep; + enum direction dir; + char *ptrn, **epp; + long *deltap; + u_int *flagp; +{ + u_int flags; + int delim, eval, re_flags, replaced; + char *p, *t; + + /* Set return information the default. */ + *deltap = 0; + + /* + * Use saved pattern if no pattern supplied, or if only a delimiter + * character is supplied. Only the pattern was saved, historic vi + * did not reuse any delta supplied. + */ + flags = *flagp; + if (ptrn == NULL) + goto prev; + if (ptrn[1] == '\0') { + if (epp != NULL) + *epp = ptrn + 1; + goto prev; + } + if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') { + if (epp != NULL) + *epp = ptrn + 2; +prev: if (!F_ISSET(sp, S_SRE_SET)) { + msgq(sp, M_ERR, "No previous search pattern."); + return (1); + } + *rep = &sp->sre; + + /* Empty patterns set the direction. */ + if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + return (0); + } + + re_flags = 0; /* Set RE flags. */ + if (O_ISSET(sp, O_EXTENDED)) + re_flags |= REG_EXTENDED; + if (O_ISSET(sp, O_IGNORECASE)) + re_flags |= REG_ICASE; + + if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */ + /* Set delimiter. */ + delim = *ptrn++; + + /* Find terminating delimiter, handling escaped delimiters. */ + for (p = t = ptrn;;) { + if (p[0] == '\0' || p[0] == delim) { + if (p[0] == delim) + ++p; + *t = '\0'; + break; + } + if (p[1] == delim && p[0] == '\\') + ++p; + *t++ = *p++; + } + + /* + * If characters after the terminating delimiter, it may + * be an error, or may be an offset. In either case, we + * return the end of the string, whatever it may be. + */ + if (*p) { + if (get_delta(sp, &p, deltap, flagp)) + return (1); + if (*p && LF_ISSET(SEARCH_TERM)) { + msgq(sp, M_ERR, + "Characters after search string and/or delta."); + return (1); + } + } + if (epp != NULL) + *epp = p; + + /* Check for "/ " or other such silliness. */ + if (*ptrn == '\0') + goto prev; + + if (re_conv(sp, &ptrn, &replaced)) + return (1); + } else if (LF_ISSET(SEARCH_TAG)) { + if (ctag_conv(sp, &ptrn, &replaced)) + return (1); + re_flags &= ~(REG_EXTENDED | REG_ICASE); + } + + /* Compile the RE. */ + if (eval = regcomp(*rep, ptrn, re_flags)) + re_error(sp, eval, *rep); + else if (LF_ISSET(SEARCH_SET)) { + F_SET(sp, S_SRE_SET); + sp->searchdir = dir; + sp->sre = **rep; + } + + /* Free up any extra memory. */ + if (replaced) + FREE_SPACE(sp, ptrn, 0); + return (eval); +} + +/* + * ctag_conv -- + * Convert a tags search path into something that the POSIX + * 1003.2 RE functions can handle. + */ +static int +ctag_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, len; + int lastdollar; + char *bp, *p, *t; + + *replacedp = 0; + + len = strlen(p = *ptrnp); + + /* Max memory usage is 2 times the length of the string. */ + GET_SPACE_RET(sp, bp, blen, len * 2); + + t = bp; + + /* The last charcter is a '/' or '?', we just strip it. */ + if (p[len - 1] == '/' || p[len - 1] == '?') + p[len - 1] = '\0'; + + /* The next-to-last character is a '$', and it's magic. */ + if (p[len - 2] == '$') { + lastdollar = 1; + p[len - 2] = '\0'; + } else + lastdollar = 0; + + /* The first character is a '/' or '?', we just strip it. */ + if (p[0] == '/' || p[0] == '?') + ++p; + + /* The second character is a '^', and it's magic. */ + if (p[0] == '^') + *t++ = *p++; + + /* + * Escape every other magic character we can find, stripping the + * backslashes ctags inserts to escape the search delimiter + * characters. + */ + while (p[0]) { + /* Ctags escapes the search delimiter characters. */ + if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) + ++p; + else if (strchr("^.[]$*", p[0])) + *t++ = '\\'; + *t++ = *p++; + } + if (lastdollar) + *t++ = '$'; + *t++ = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * search_intr -- + * Set the interrupt bit in any screen that is interruptible. + * + * XXX + * In the future this may be a problem. The user should be able to move to + * another screen and keep typing while this runs. If so, and the user has + * more than one search/global (see ex/ex_global.c) running, it will be hard + * to decide which one to stop. + */ +static void +search_intr(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_INTERRUPTIBLE)) + F_SET(sp, S_INTERRUPTED); +} + +#define EMPTYMSG "File empty; nothing to search." +#define EOFMSG "Reached end-of-file without finding the pattern." +#define NOTFOUND "Pattern not found." +#define SOFMSG "Reached top-of-file without finding the pattern." +#define WRAPMSG "Search wrapped." + +int +f_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* + * Start searching immediately after the cursor. If at the end of the + * line, start searching on the next line. This is incompatible (read + * bug fix) with the historic vi -- searches for the '$' pattern never + * moved forward, and "-t foo" didn't work if "foo" was the first thing + * in the file. + */ + if (LF_ISSET(SEARCH_FILE)) { + lno = 1; + coff = 0; + } else { + if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) { + GETLINE_ERR(sp, fm->lno); + return (1); + } + if (fm->cno + 1 >= len) { + if (fm->lno == lno) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + return (1); + } + lno = 1; + } else + lno = fm->lno + 1; + coff = 0; + } else { + lno = fm->lno; + coff = fm->cno + 1; + } + } + + /* + * Set up busy message, interrupts. + * + * F_search is called from the ex_tagfirst() routine, which runs + * before the screen really exists. Make sure we don't step on + * anything. + */ + if (sp->s_position != NULL) + busy_on(sp, 1, "Searching..."); + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0;; ++lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno > fm->lno || + (l = file_gline(sp, ep, lno, &len)) == NULL) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EOFMSG); + break; + } + lno = 0; + wrapped = 1; + continue; + } + + /* If already at EOL, just keep going. */ + if (len && coff == len) + continue; + + /* Set the termination. */ + match[0].rm_so = coff; + match[0].rm_eo = len; + +#if defined(DEBUG) && 0 + TRACE(sp, "F search: %lu from %u to %u\n", + lno, coff, len ? len - 1 : len); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + /* + * If an offset, see if it's legal. It's possible to match + * past the end of the line with $, so check for that case. + */ + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + rm->lno = lno; + rm->cno = match[0].rm_so; + + /* + * If a change command, it's possible to move beyond + * the end of a line. Historic vi generally got this + * wrong (try "c?$<cr>"). Not all that sure this gets + * it right, there are lots of strange cases. + */ + if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len) + rm->cno = len ? len - 1 : 0; + } + rval = 0; + break; + } + +interrupt_err: + + /* Turn off busy message, interrupts. */ + if (sp->s_position != NULL) + busy_off(sp); + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +int +b_search(sp, ep, fm, rm, ptrn, eptrn, flagp) + SCR *sp; + EXF *ep; + MARK *fm, *rm; + char *ptrn, **eptrn; + u_int *flagp; +{ + DECLARE_INTERRUPTS; + regmatch_t match[1]; + regex_t *re, lre; + recno_t lno; + size_t coff, len, last; + long delta; + u_int flags; + int eval, rval, wrapped; + char *l; + + if (file_lline(sp, ep, &lno)) + return (1); + flags = *flagp; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + return (1); + } + + re = &lre; + if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp)) + return (1); + + /* If in the first column, start searching on the previous line. */ + if (fm->cno == 0) { + if (fm->lno == 1) { + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + return (1); + } + } else + lno = fm->lno - 1; + } else + lno = fm->lno; + + /* Turn on busy message, interrupts. */ + busy_on(sp, 1, "Searching..."); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SET_UP_INTERRUPTS(search_intr); + + for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) { + if (F_ISSET(sp, S_INTERRUPTED)) { + msgq(sp, M_INFO, "Interrupted."); + break; + } + if (wrapped && lno < fm->lno || lno == 0) { + if (wrapped) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, NOTFOUND); + break; + } + if (!O_ISSET(sp, O_WRAPSCAN)) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, SOFMSG); + break; + } + if (file_lline(sp, ep, &lno)) + goto err; + if (lno == 0) { + if (LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, EMPTYMSG); + break; + } + ++lno; + wrapped = 1; + continue; + } + + if ((l = file_gline(sp, ep, lno, &len)) == NULL) + goto err; + + /* Set the termination. */ + match[0].rm_so = 0; + match[0].rm_eo = coff ? coff : len; + +#if defined(DEBUG) && 0 + TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo); +#endif + /* Search the line. */ + eval = regexec(re, l, 1, match, + (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND); + if (eval == REG_NOMATCH) + continue; + if (eval != 0) { + re_error(sp, eval, re); + break; + } + + /* Warn if wrapped. */ + if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG)) + msgq(sp, M_INFO, WRAPMSG); + + if (delta) { + if (check_delta(sp, ep, delta, lno)) + break; + rm->lno = delta + lno; + rm->cno = 0; + } else { +#if defined(DEBUG) && 0 + TRACE(sp, "found: %qu to %qu\n", + match[0].rm_so, match[0].rm_eo); +#endif + /* + * Find the last acceptable one in this line. This + * is really painful, we need a cleaner interface to + * regexec to make this possible. + */ + for (;;) { + last = match[0].rm_so; + match[0].rm_so = match[0].rm_eo + 1; + if (match[0].rm_so >= len || + coff && match[0].rm_so >= coff) + break; + match[0].rm_eo = coff ? coff : len; + eval = regexec(re, l, 1, match, + (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | + REG_STARTEND); + if (eval == REG_NOMATCH) + break; + if (eval != 0) { + re_error(sp, eval, re); + goto err; + } + } + rm->lno = lno; + + /* See comment in f_search(). */ + if (!LF_ISSET(SEARCH_EOL) && last >= len) + rm->cno = len ? len - 1 : 0; + else + rm->cno = last; + } + rval = 0; + break; + } + + /* Turn off busy message, interrupts. */ +interrupt_err: +err: busy_off(sp); + + if (F_ISSET(sp->gp, G_ISFROMTTY)) + TEAR_DOWN_INTERRUPTS; + + return (rval); +} + +/* + * re_conv -- + * Convert vi's regular expressions into something that the + * the POSIX 1003.2 RE functions can handle. + * + * There are three conversions we make to make vi's RE's (specifically + * the global, search, and substitute patterns) work with POSIX RE's. + * + * 1: If O_MAGIC is not set, strip backslashes from the magic character + * set (.[]*~) that have them, and add them to the ones that don't. + * 2: If O_MAGIC is not set, the string "\~" is replaced with the text + * from the last substitute command's replacement string. If O_MAGIC + * is set, it's the string "~". + * 3: The pattern \<ptrn\> does "word" searches, convert it to use the + * new RE escapes. + */ +int +re_conv(sp, ptrnp, replacedp) + SCR *sp; + char **ptrnp; + int *replacedp; +{ + size_t blen, needlen; + int magic; + char *bp, *p, *t; + + /* + * First pass through, we figure out how much space we'll need. + * We do it in two passes, on the grounds that most of the time + * the user is doing a search and won't have magic characters. + * That way we can skip the malloc and memmove's. + */ + for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + magic = 1; + needlen += sizeof(RE_WSTART); + break; + case '>': + magic = 1; + needlen += sizeof(RE_WSTOP); + break; + case '~': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 1; + } + break; + default: + needlen += 2; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) { + magic = 1; + needlen += 2; + } + break; + default: + needlen += 1; + break; + } + + if (!magic) { + *replacedp = 0; + return (0); + } + + /* + * Get enough memory to hold the final pattern. + * + * XXX + * It's nul-terminated, for now. + */ + GET_SPACE_RET(sp, bp, blen, needlen + 1); + + for (p = *ptrnp, t = bp; *p != '\0'; ++p) + switch (*p) { + case '\\': + switch (*++p) { + case '<': + memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1); + t += sizeof(RE_WSTART) - 1; + break; + case '>': + memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1); + t += sizeof(RE_WSTOP) - 1; + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '~'; + else { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } + break; + case '.': + case '[': + case ']': + case '*': + if (O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = '\\'; + *t++ = *p; + } + break; + case '~': + if (O_ISSET(sp, O_MAGIC)) { + memmove(t, sp->repl, sp->repl_len); + t += sp->repl_len; + } else + *t++ = '~'; + break; + case '.': + case '[': + case ']': + case '*': + if (!O_ISSET(sp, O_MAGIC)) + *t++ = '\\'; + *t++ = *p; + break; + default: + *t++ = *p; + break; + } + *t = '\0'; + + *ptrnp = bp; + *replacedp = 1; + return (0); +} + +/* + * get_delta -- + * Get a line delta. The trickiness is that the delta can be pretty + * complicated, i.e. "+3-2+3++- ++" is allowed. + * + * !!! + * In historic vi, if you had a delta on a search pattern which was used as + * a motion command, the command became a line mode command regardless of the + * cursor positions. A fairly common trick is to use a delta of "+0" to make + * the command a line mode command. This is the only place that knows about + * delta's, so we set the return flag information here. + */ +static int +get_delta(sp, dp, valp, flagp) + SCR *sp; + char **dp; + long *valp; + u_int *flagp; +{ + char *p; + long val, tval; + + for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) { + if (isblank(*p)) { + ++p; + continue; + } + if (*p == '+' || *p == '-') { + if (!isdigit(*(p + 1))) { + if (*p == '+') { + if (tval == LONG_MAX) + goto overflow; + ++tval; + } else { + if (tval == LONG_MIN) + goto underflow; + --tval; + } + ++p; + continue; + } + } else + if (!isdigit(*p)) + break; + + errno = 0; + val = strtol(p, &p, 10); + if (errno == ERANGE) { + if (val == LONG_MAX) +overflow: msgq(sp, M_ERR, "Delta value overflow."); + else if (val == LONG_MIN) +underflow: msgq(sp, M_ERR, "Delta value underflow."); + else + msgq(sp, M_SYSERR, NULL); + return (1); + } + if (val >= 0) { + if (LONG_MAX - val < tval) + goto overflow; + } else + if (-(LONG_MIN - tval) > val) + goto underflow; + tval += val; + } + *dp = p; + *valp = tval; + return (0); +} + +/* + * check_delta -- + * Check a line delta to see if it's legal. + */ +static int +check_delta(sp, ep, delta, lno) + SCR *sp; + EXF *ep; + long delta; + recno_t lno; +{ + /* A delta can overflow a record number. */ + if (delta < 0) { + if (lno < LONG_MAX && delta >= (long)lno) { + msgq(sp, M_ERR, "Search offset before line 1."); + return (1); + } + } else { + if (ULONG_MAX - lno < delta) { + msgq(sp, M_ERR, "Delta value overflow."); + return (1); + } + if (file_gline(sp, ep, lno + delta, NULL) == NULL) { + msgq(sp, M_ERR, "Search offset past end-of-file."); + return (1); + } + } + return (0); +} + +/* + * re_error -- + * Report a regular expression error. + */ +void +re_error(sp, errcode, preg) + SCR *sp; + int errcode; + regex_t *preg; +{ + size_t s; + char *oe; + + s = regerror(errcode, preg, "", 0); + if ((oe = malloc(s)) == NULL) + msgq(sp, M_SYSERR, NULL); + else { + (void)regerror(errcode, preg, oe, s); + msgq(sp, M_ERR, "RE error: %s", oe); + } + free(oe); +} diff --git a/usr.bin/vi/search.h b/usr.bin/vi/search.h new file mode 100644 index 000000000000..00feff6027c8 --- /dev/null +++ b/usr.bin/vi/search.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)search.h 8.7 (Berkeley) 11/18/93 + */ + +#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */ +#define RE_WSTOP "[[:>:]]" + +#define SEARCH_DELTA 0x001 /* A delta part of the search.*/ +#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */ +#define SEARCH_FILE 0x004 /* Search the entire file. */ +#define SEARCH_MSG 0x008 /* Display search warning messages. */ +#define SEARCH_PARSE 0x010 /* Parse the search pattern. */ +#define SEARCH_SET 0x020 /* Set search direction. */ +#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */ +#define SEARCH_TERM 0x080 /* Search pattern should terminate. */ + +enum direction { NOTSET, FORWARD, BACKWARD }; +enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH }; + +/* Search functions. */ +int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *)); +int re_conv __P((SCR *, char **, int *)); +void re_error __P((SCR *, int, regex_t *)); diff --git a/usr.bin/vi/seq.c b/usr.bin/vi/seq.c new file mode 100644 index 000000000000..4d6d44956b0e --- /dev/null +++ b/usr.bin/vi/seq.c @@ -0,0 +1,299 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)seq.c 8.21 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "seq.h" +#include "excmd.h" + +/* + * seq_set -- + * Internal version to enter a sequence. + */ +int +seq_set(sp, name, nlen, input, ilen, output, olen, stype, userdef) + SCR *sp; + char *name, *input, *output; + size_t nlen, ilen, olen; + enum seqtype stype; + int userdef; +{ + SEQ *lastqp, *qp; + CHAR_T *p; + +#if defined(DEBUG) && 0 + TRACE(sp, "seq_set: name {%s} input {%s} output {%s}\n", + name ? name : "", input, output); +#endif + /* Just replace the output field in any previous occurrence. */ + if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) { + if ((p = v_strdup(sp, output, olen)) == NULL) + goto mem1; + FREE(qp->output, qp->olen); + qp->olen = olen; + qp->output = p; + return (0); + } + + /* Allocate and initialize space. */ + CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ)); + if (qp == NULL) + goto mem1; + if (name == NULL) + qp->name = NULL; + else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) + goto mem2; + if ((qp->input = v_strdup(sp, input, ilen)) == NULL) + goto mem3; + if ((qp->output = v_strdup(sp, output, olen)) == NULL) { + FREE(qp->input, ilen); +mem3: if (qp->name != NULL) + FREE(qp->name, nlen); +mem2: FREE(qp, sizeof(SEQ)); +mem1: msgq(sp, M_SYSERR, NULL); + return (1); + } + + qp->stype = stype; + qp->nlen = nlen; + qp->ilen = ilen; + qp->olen = olen; + qp->flags = userdef ? S_USERDEF : 0; + + /* Link into the chain. */ + if (lastqp == NULL) { + LIST_INSERT_HEAD(&sp->gp->seqq, qp, q); + } else { + LIST_INSERT_AFTER(lastqp, qp, q); + } + + /* Set the fast lookup bit. */ + bit_set(sp->gp->seqb, qp->input[0]); + + return (0); +} + +/* + * seq_delete -- + * Delete a sequence. + */ +int +seq_delete(sp, input, ilen, stype) + SCR *sp; + char *input; + size_t ilen; + enum seqtype stype; +{ + SEQ *qp; + + if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL) + return (1); + + LIST_REMOVE(qp, q); + if (qp->name != NULL) + FREE(qp->name, qp->nlen); + FREE(qp->input, qp->ilen); + FREE(qp->output, qp->olen); + FREE(qp, sizeof(SEQ)); + return (0); +} + +/* + * seq_find -- + * Search the sequence list for a match to a buffer, if ispartial + * isn't NULL, partial matches count. + */ +SEQ * +seq_find(sp, lastqp, input, ilen, stype, ispartialp) + SCR *sp; + SEQ **lastqp; + char *input; + size_t ilen; + enum seqtype stype; + int *ispartialp; +{ + SEQ *lqp, *qp; + int diff; + + /* + * Ispartialp is a location where we return if there was a + * partial match, i.e. if the string were extended it might + * match something. + * + * XXX + * Overload the meaning of ispartialp; only the terminal key + * search doesn't want the search limited to complete matches, + * i.e. ilen may be longer than the match. + */ + if (ispartialp != NULL) + *ispartialp = 0; + for (lqp = NULL, qp = sp->gp->seqq.lh_first; + qp != NULL; lqp = qp, qp = qp->q.le_next) { + /* Fast checks on the first character and type. */ + if (qp->input[0] > input[0]) + break; + if (qp->input[0] < input[0] || qp->stype != stype) + continue; + + /* Check on the real comparison. */ + diff = memcmp(qp->input, input, MIN(qp->ilen, ilen)); + if (diff > 0) + break; + if (diff < 0) + continue; + /* + * If the entry is the same length as the string, return a + * match. If the entry is shorter than the string, return a + * match if called from the terminal key routine. Otherwise, + * keep searching for a complete match. + */ + if (qp->ilen <= ilen) { + if (qp->ilen == ilen || ispartialp != NULL) { + if (lastqp != NULL) + *lastqp = lqp; + return (qp); + } + continue; + } + /* + * If the entry longer than the string, return partial match + * if called from the terminal key routine. Otherwise, no + * match. + */ + if (ispartialp != NULL) + *ispartialp = 1; + break; + } + if (lastqp != NULL) + *lastqp = lqp; + return (NULL); +} + +/* + * seq_dump -- + * Display the sequence entries of a specified type. + */ +int +seq_dump(sp, stype, isname) + SCR *sp; + enum seqtype stype; + int isname; +{ + CHNAME const *cname; + SEQ *qp; + int cnt, len, olen, tablen; + char *p; + + cnt = 0; + cname = sp->gp->cname; + tablen = O_VAL(sp, O_TABSTOP); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (stype != qp->stype) + continue; + ++cnt; + for (p = qp->input, + olen = qp->ilen, len = 0; olen > 0; --olen, ++len) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + + for (p = qp->output, olen = qp->olen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, "%s", cname[*p++].name); + + if (isname && qp->name != NULL) { + for (len = tablen - len % tablen; len; --len) + (void)ex_printf(EXCOOKIE, " "); + for (p = qp->name, olen = qp->nlen; olen > 0; --olen) + (void)ex_printf(EXCOOKIE, + "%s", cname[*p++].name); + } + (void)ex_printf(EXCOOKIE, "\n"); + } + return (cnt); +} + +/* + * seq_save -- + * Save the sequence entries to a file. + */ +int +seq_save(sp, fp, prefix, stype) + SCR *sp; + FILE *fp; + char *prefix; + enum seqtype stype; +{ + CHAR_T esc; + SEQ *qp; + size_t olen; + int ch; + char *p; + + /* Write a sequence command for all keys the user defined. */ + (void)term_key_ch(sp, K_VLNEXT, &esc); + for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) { + if (!F_ISSET(qp, S_USERDEF)) + continue; + if (stype != qp->stype) + continue; + if (prefix) + (void)fprintf(fp, "%s", prefix); + for (p = qp->input, olen = qp->ilen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + isblank(ch) || term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc(' ', fp); + for (p = qp->output, olen = qp->olen; olen > 0; --olen) { + ch = *p++; + if (ch == esc || ch == '|' || + term_key_val(sp, ch) == K_NL) + (void)putc(esc, fp); + (void)putc(ch, fp); + } + (void)putc('\n', fp); + } + return (0); +} diff --git a/usr.bin/vi/seq.h b/usr.bin/vi/seq.h new file mode 100644 index 000000000000..b9d9ac6cfd66 --- /dev/null +++ b/usr.bin/vi/seq.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)seq.h 8.7 (Berkeley) 12/2/93 + */ + +/* + * Map and abbreviation structures. + * + * The map structure is doubly linked list, sorted by input string and by + * input length within the string. (The latter is necessary so that short + * matches will happen before long matches when the list is searched.) + * Additionally, there is a bitmap which has bits set if there are entries + * starting with the corresponding character. This keeps us from walking + * the list unless it's necessary. + * + * XXX + * The fast-lookup bits are never turned off -- users don't usually unmap + * things, though, so it's probably not a big deal. + */ + /* Sequence type. */ +enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT }; + +struct _seq { + LIST_ENTRY(_seq) q; /* Linked list of all sequences. */ + enum seqtype stype; /* Sequence type. */ + char *name; /* Sequence name (if any). */ + size_t nlen; /* Name length. */ + char *input; /* Sequence input keys. */ + size_t ilen; /* Input keys length. */ + char *output; /* Sequence output keys. */ + size_t olen; /* Output keys length. */ + +#define S_USERDEF 0x01 /* If sequence user defined. */ + u_char flags; +}; + +int abbr_save __P((SCR *, FILE *)); +int map_save __P((SCR *, FILE *)); +int seq_delete __P((SCR *, char *, size_t, enum seqtype)); +int seq_dump __P((SCR *, enum seqtype, int)); +SEQ *seq_find __P((SCR *, SEQ **, char *, size_t, enum seqtype, int *)); +void seq_init __P((SCR *)); +int seq_save __P((SCR *, FILE *, char *, enum seqtype)); +int seq_set __P((SCR *, char *, size_t, + char *, size_t, char *, size_t, enum seqtype, int)); diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c new file mode 100644 index 000000000000..8b55fadedaae --- /dev/null +++ b/usr.bin/vi/sex/sex_confirm.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +enum confirm +sex_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + recno_t cnt; + + if (ex_print(sp, ep, fp, tp, 0)) + return (CONF_QUIT); + + for (cnt = fp->cno; cnt; --cnt) + (void)putc(' ', stdout); + for (cnt = tp->cno; cnt; --cnt) + (void)putc('^', stdout); + (void)fprintf(stdout, "[ynq]"); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c new file mode 100644 index 000000000000..54b7343608d1 --- /dev/null +++ b/usr.bin/vi/sex/sex_get.c @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_get.c 8.12 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <ctype.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" + +static void repaint __P((SCR *, int, char *, size_t)); + +#define DISPLAY(wval, ch, col) { \ + size_t __len; \ + int __ch; \ + if ((__ch = (ch)) == '\t') { \ + __len = O_VAL(sp, O_TABSTOP) - \ + ((col) % O_VAL(sp, O_TABSTOP)); \ + (col) += (wval) = __len; \ + while (__len--) \ + putc(' ', stdout); \ + } else { \ + (col) += (wval) = cname[(__ch)].len; \ + (void)fprintf(stdout, \ + "%.*s", cname[(__ch)].len, cname[(__ch)].name); \ + } \ +} + +#define ERASECH { \ + for (cnt = tp->wd[tp->len]; cnt > 0; --cnt, --col) \ + (void)fprintf(stdout, "%s", "\b \b"); \ +} + +/* + * sex_get -- + * Fill a buffer from the terminal for ex. + */ +enum input +sex_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + size_t cnt; + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + if (LF_ISSET(TXT_PROMPT) && O_ISSET(sp, O_PROMPT)) { + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + (void)fflush(stdout); + + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) { + ERASECH; + goto ins_ch; + } + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + repaint(sp, prompt, tp->lb, tp->len); + break; + case K_CR: + case K_NL: + if (LF_ISSET(TXT_NLECHO)) { + (void)putc('\r', stdout); + (void)putc('\n', stdout); + (void)fflush(stdout); + } + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) { + --tp->len; + ERASECH; + } + break; + case K_VKILL: + for (; tp->len; --tp->len) + ERASECH; + break; + case K_VLNEXT: + (void)fprintf(stdout, "%s%c", cname['^'].name, '\b'); + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } else + ERASECH; + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + DISPLAY(tp->wd[tp->len], ikey.ch, col); + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * sex_get_notty -- + * Fill a buffer from the terminal for ex, but don't echo + * input. + */ +enum input +sex_get_notty(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + enum { Q_NOTSET, Q_THISCHAR } quoted; + CHNAME const *cname; /* Character map. */ + TEXT *tp; /* Input text structures. */ + CH ikey; /* Input character. */ + size_t col; /* 0-N: screen column. */ + u_int iflags; /* Input flags. */ + int rval; + +#ifdef DEBUG + if (LF_ISSET(~TXT_VALID_EX) || !LF_ISSET(TXT_CR)) + abort(); +#endif + /* + * Get one TEXT structure with some initial buffer space, reusing + * the last one if it's big enough. (All TEXT bookkeeping fields + * default to 0 -- text_init() handles this.) + */ + if (tiqh->cqh_first != (void *)tiqh) { + tp = tiqh->cqh_first; + if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { + text_lfree(tiqh); + goto newtp; + } + tp->len = 0; + } else { +newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) + return (INP_ERR); + CIRCLEQ_INSERT_HEAD(tiqh, tp, q); + } + + cname = sp->gp->cname; + col = 0; + + iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT); + for (quoted = Q_NOTSET;;) { + if (rval = term_key(sp, &ikey, iflags)) + return (rval); + + BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); + BINC_RET(sp, tp->wd, tp->wd_len, tp->len + 1); + + if (quoted == Q_THISCHAR) + goto ins_ch; + + switch (ikey.value) { + case K_CNTRLZ: + sex_suspend(sp); + /* FALLTHROUGH */ + case K_CNTRLR: + break; + case K_CR: + case K_NL: + /* Terminate with a newline, needed by filter. */ + tp->lb[tp->len] = '\0'; + return (INP_OK); + case K_VERASE: + if (tp->len) + --tp->len; + break; + case K_VKILL: + tp->len = 0; + break; + case K_VLNEXT: + quoted = Q_THISCHAR; + break; + case K_VWERASE: + /* Move to the last non-space character. */ + while (tp->len) + if (!isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + + /* Move to the last space character. */ + while (tp->len) + if (isblank(tp->lb[--tp->len])) { + ++tp->len; + break; + } + break; + default: +ins_ch: tp->lb[tp->len] = ikey.ch; + ++tp->len; + quoted = Q_NOTSET; + break; + } + } + /* NOTREACHED */ +} + +/* + * repaint -- + * Repaint the line. + */ +static void +repaint(sp, prompt, p, len) + SCR *sp; + int prompt; + char *p; + size_t len; +{ + CHNAME const *cname; + size_t col; + u_char width; + + cname = sp->gp->cname; + + (void)putc('\n', stdout); + if (prompt && O_ISSET(sp, O_PROMPT)) { /* Display prompt. */ + (void)fprintf(stdout, "%s", cname[prompt].name); + col = cname[prompt].len; + } else + col = 0; + + while (len--) + DISPLAY(width, *p++, col); +} diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c new file mode 100644 index 000000000000..0ba589299080 --- /dev/null +++ b/usr.bin/vi/sex/sex_refresh.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_refresh.c 8.5 (Berkeley) 11/18/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_refresh -- + * In ex, just display any messages. + */ +int +sex_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + MSG *mp; + + if (F_ISSET(sp, S_RESIZE)) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + F_CLR(sp, S_RESIZE); + } + + /* Ring the bell. */ + if (F_ISSET(sp, S_BELLSCHED)) { + sex_bell(sp); + F_CLR(sp, S_BELLSCHED); + } + + for (mp = sp->msgq.lh_first; + mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) { + (void)fprintf(stdout, "%.*s\n", (int)mp->len, mp->mbuf); + F_SET(mp, M_EMPTY); + } + return (0); +} diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c new file mode 100644 index 000000000000..d087d9f17476 --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_screen.c 8.30 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "sex_screen.h" +#include "../svi/svi_screen.h" + +static void sex_abort __P((void)); +static int sex_noop __P((void)); +static int sex_nope __P((SCR *)); + +/* + * sex_screen_init -- + * Initialize the ex screen. + */ +int +sex_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = sex_bell; + sp->s_bg = (int (*)())sex_nope; + sp->s_busy = (int (*)())sex_busy; + sp->s_change = (int (*)())sex_noop; + sp->s_chposition = (size_t (*)())sex_abort; + sp->s_clear = (int (*)())sex_noop; + sp->s_column = (int (*)())sex_abort; + sp->s_confirm = sex_confirm; + sp->s_down = (int (*)())sex_abort; + sp->s_edit = sex_screen_edit; + sp->s_end = (int (*)())sex_noop; + sp->s_ex_cmd = (int (*)())sex_abort; + sp->s_ex_run = (int (*)())sex_abort; + sp->s_ex_write = (int (*)())sex_abort; + sp->s_fg = (int (*)())sex_nope; + sp->s_fill = (int (*)())sex_abort; + sp->s_get = F_ISSET(sp->gp, + G_ISFROMTTY) ? sex_get : sex_get_notty; + sp->s_key_read = sex_key_read; + sp->s_optchange = (int (*)())sex_noop; + sp->s_position = (int (*)())sex_abort; + sp->s_rabs = (int (*)())sex_nope; + sp->s_refresh = sex_refresh; + sp->s_relative = (size_t (*)())sex_abort; + sp->s_rrel = (int (*)())sex_nope; + sp->s_split = (int (*)())sex_nope; + sp->s_suspend = sex_suspend; + sp->s_up = (int (*)())sex_abort; + + return (0); +} + +/* + * sex_screen_copy -- + * Copy to a new screen. + */ +int +sex_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * sex_screen_end -- + * End a screen. + */ +int +sex_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * sex_screen_edit -- + * Main ex screen loop. The ex screen is relatively uncomplicated. + * As long as it has a stdio FILE pointer for output, it's happy. + */ +int +sex_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + struct termios rawt, t; + GS *saved_gp; + int force, rval; + + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + SEX_RAW(t, rawt); + + /* Write to the terminal. */ + sp->stdfp = stdout; + + for (;;) { + sp->rows = O_VAL(sp, O_LINES); + sp->cols = O_VAL(sp, O_COLUMNS); + + /* + * Run ex. If ex fails, sex data structures + * may be corrupted, be careful what you do. + */ + if (rval = ex(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); + (void)screen_end(sp); /* General SCR info. */ + break; + } + + saved_gp = sp->gp; + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force)) + break; + (void)screen_end(sp); /* General SCR info. */ + goto ret; + case 0: /* Changing from ex mode. */ + goto ret; + case S_FSWITCH: + F_CLR(sp, S_FSWITCH); + break; + case S_SSWITCH: + default: + abort(); + } + } + + /* Reset the terminal state. */ +ret: if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + return (rval); +} + +/* + * sex_abort -- + * Fake function. Die. + */ +static void +sex_abort() +{ + abort(); +} + +/* + * sex_noop -- + * Fake function. Do nothing. + */ +static int +sex_noop() +{ + return (0); +} + +/* + * sex_nope -- + * Fake function. Not in ex, you don't. + */ +static int +sex_nope(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "Command not applicable to ex mode."); + return (1); +} diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h new file mode 100644 index 000000000000..419a2f274ed1 --- /dev/null +++ b/usr.bin/vi/sex/sex_screen.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sex_screen.h 8.12 (Berkeley) 11/29/93 + */ + +#define SEX_NORAW(t) \ + tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t)) + +#define SEX_RAW(t, rawt) { \ + if (tcgetattr(STDIN_FILENO, &(t))) \ + return (1); \ + (rawt) = (t); \ + (rawt).c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL); \ + (rawt).c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \ + (rawt).c_cc[VMIN] = 1; \ + (rawt).c_cc[VTIME] = 0; \ + if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(rawt))) \ + return (1); \ +} + +void sex_bell __P((SCR *)); +void sex_busy __P((SCR *, char const *)); +enum confirm + sex_confirm __P((SCR *, EXF *, MARK *, MARK *)); +enum input + sex_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_get_notty __P((SCR *, EXF *, TEXTH *, int, u_int)); +enum input + sex_key_read __P((SCR *, int *, struct timeval *)); +int sex_refresh __P((SCR *, EXF *)); +int sex_screen_copy __P((SCR *, SCR *)); +int sex_screen_edit __P((SCR *, EXF *)); +int sex_screen_end __P((SCR *)); +int sex_split __P((SCR *, char *[])); +int sex_suspend __P((SCR *)); diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c new file mode 100644 index 000000000000..4e3980f21efd --- /dev/null +++ b/usr.bin/vi/sex/sex_term.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_term.c 8.24 (Berkeley) 12/20/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "excmd.h" +#include "script.h" + +/* + * sex_key_read -- + * Read characters from the input. + */ +enum input +sex_key_read(sp, nrp, timeout) + SCR *sp; + int *nrp; + struct timeval *timeout; +{ + struct timeval t, *tp; + IBUF *tty; + SCR *tsp; + int maxfd, nr; + + *nrp = 0; + tty = sp->gp->tty; + + /* + * We're about to block; check for signals. If a signal received, + * clear it immediately, so that if it's reset while being serviced + * we won't miss it. + * + * Signal recipients set global flags. If one is set, we either + * set local flags or call handling routines. None of this has + * anything to do with input keys, but it's something that can't + * be done asynchronously without doing a lot of locking to handle + * race conditions, and which needs to be done periodically. + */ +sigchk: while (F_ISSET(sp->gp, + G_SIGALRM | G_SIGHUP | G_SIGTERM | G_SIGWINCH)) { + if (F_ISSET(sp->gp, G_SIGALRM)) { + F_CLR(sp->gp, G_SIGALRM); + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp->ep != NULL && + F_ISSET(tsp->ep, F_RCV_ON)) + F_SET(tsp->ep, F_RCV_ALRM); + } + if (F_ISSET(sp->ep, F_RCV_ALRM)) { + F_CLR(sp->ep, F_RCV_ALRM); + (void)rcv_sync(sp, sp->ep); + } + if (F_ISSET(sp->gp, G_SIGHUP)) { + F_CLR(sp->gp, G_SIGHUP); + rcv_hup(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGTERM)) { + F_CLR(sp->gp, G_SIGTERM); + rcv_term(); + /* NOTREACHED */ + } + if (F_ISSET(sp->gp, G_SIGWINCH)) { + F_CLR(sp->gp, G_SIGWINCH); + set_window_size(sp, 0, 1); + F_SET(sp, S_RESIZE); + (void)sp->s_refresh(sp, sp->ep); + } + } + + /* + * There are three cases here: + * + * 1: A read from a file or a pipe. In this case, the reads + * never timeout regardless. This means that we can hang + * when trying to complete a map, but we're going to hang + * on the next read anyway. + */ + if (!F_ISSET(sp->gp, G_ISFROMTTY)) { + if ((nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) > 0) { + tty->cnt += *nrp = nr; + return (INP_OK); + } + return (INP_EOF); + } + + /* + * 2: A read with an associated timeout. In this case, we are trying + * to complete a map sequence. Ignore script windows and timeout + * as specified. If input arrives, we fall into #3, but because + * timeout isn't NULL, don't read anything but command input. + * + * If interrupted, go back and check to see what it was. + */ + if (timeout != NULL) { + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + FD_SET(STDIN_FILENO, &sp->rdfd); + for (;;) { + switch (select(STDIN_FILENO + 1, + &sp->rdfd, NULL, NULL, timeout)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + goto err; + case 1: /* Characters ready. */ + break; + case 0: /* Timeout. */ + return (INP_OK); + } + break; + } + } + + /* + * 3: At this point, we'll take anything that comes. Select on the + * command file descriptor and the file descriptor for the script + * window if there is one. Poll the fd's, increasing the timeout + * each time each time we don't get anything until we're blocked + * on I/O. + * + * If interrupted, go back and check to see what it was. + */ + for (t.tv_sec = t.tv_usec = 0;;) { + /* + * Reset each time -- sscr_input() may call other + * routines which could reset bits. + */ + if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) { + tp = &t; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) { + FD_SET(sp->script->sh_master, &sp->rdfd); + maxfd = + MAX(STDIN_FILENO, sp->script->sh_master); + } else + maxfd = STDIN_FILENO; + } else { + tp = NULL; + + FD_SET(STDIN_FILENO, &sp->rdfd); + if (F_ISSET(sp, S_SCRIPT)) + FD_CLR(sp->script->sh_master, &sp->rdfd); + maxfd = STDIN_FILENO; + } + + switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) { + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; +err: msgq(sp, M_SYSERR, "select"); + return (INP_ERR); + case 0: /* Timeout. */ + if (t.tv_usec) { + ++t.tv_sec; + t.tv_usec = 0; + } else + t.tv_usec += 500000; + continue; + } + + if (timeout == NULL && F_ISSET(sp, S_SCRIPT) && + FD_ISSET(sp->script->sh_master, &sp->rdfd)) { + sscr_input(sp); + continue; + } + + switch (nr = read(STDIN_FILENO, + tty->ch + tty->next + tty->cnt, + tty->len - (tty->next + tty->cnt))) { + case 0: /* EOF. */ + return (INP_EOF); + case -1: /* Error or interrupt. */ + if (errno == EINTR) + goto sigchk; + msgq(sp, M_SYSERR, "read"); + return (INP_ERR); + default: + tty->cnt += *nrp = nr; + return (INP_OK); + } + /* NOTREACHED */ + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c new file mode 100644 index 000000000000..25eb17f049c3 --- /dev/null +++ b/usr.bin/vi/sex/sex_util.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sex_util.c 8.9 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "vi.h" +#include "sex_screen.h" + +/* + * sex_bell -- + * Ring the bell. + */ +void +sex_bell(sp) + SCR *sp; +{ + (void)write(STDOUT_FILENO, "\07", 1); /* \a */ +} + +void +sex_busy(sp, msg) + SCR *sp; + char const *msg; +{ + (void)fprintf(stdout, "%s\n", msg); + (void)fflush(stdout); +} + +/* + * sex_suspend -- + * Suspend an ex screen. + */ +int +sex_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* Save ex/vi terminal settings, and restore the original ones. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSADRAIN, &sp->gp->original_termios); + } + + /* Kill the process group. */ + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(0, SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + /* Restore ex/vi terminal settings. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) + (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &t); + + return (rval); +} diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c new file mode 100644 index 000000000000..01546d7d7716 --- /dev/null +++ b/usr.bin/vi/svi/svi_confirm.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_confirm.c 8.5 (Berkeley) 11/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> + +#include "vi.h" +#include "svi_screen.h" + +enum confirm +svi_confirm(sp, ep, fp, tp) + SCR *sp; + EXF *ep; + MARK *fp, *tp; +{ + CH ikey; + size_t oldy, oldx; + + /* + * Refresh the cursor first -- this means that we won't have to + * set S_UPDATE_MODE to keep refresh from erasing the mode line + * or SVI_CUR_INVALID because we sneaked the cursor off somewhere + * else. + */ + sp->lno = fp->lno; + sp->cno = fp->cno; + if (svi_paint(sp, ep)) + return (CONF_QUIT); + + getyx(stdscr, oldy, oldx); + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + ADDNSTR(CONFSTRING, sizeof(CONFSTRING) - 1); + MOVEA(sp, oldy, oldx); + refresh(); + + if (term_key(sp, &ikey, 0) != INP_OK) + return (CONF_QUIT); + switch (ikey.ch) { + case YES_CH: + return (CONF_YES); + case QUIT_CH: + return (CONF_QUIT); + default: + case NO_CH: + return (CONF_NO); + } + /* NOTREACHED */ +} diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c new file mode 100644 index 000000000000..7a83d409b3cd --- /dev/null +++ b/usr.bin/vi/svi/svi_ex.c @@ -0,0 +1,476 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_ex.c 8.36 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "../sex/sex_screen.h" + +static int svi_ex_done __P((SCR *, EXF *, MARK *)); +static int svi_ex_scroll __P((SCR *, int, int, CH *)); + +/* + * svi_ex_cmd -- + * Execute an ex command. + */ +int +svi_ex_cmd(sp, ep, exp, rp) + SCR *sp; + EXF *ep; + EXCMDARG *exp; + MARK *rp; +{ + SVI_PRIVATE *svp; + int rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + (void)svi_busy(sp, NULL); + rval = exp->cmd->fn(sp, ep, exp); + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + (void)msg_rpt(sp, 0); + (void)ex_fflush(EXCOOKIE); + + /* + * If displayed anything, figure out if we have to wait. If the + * screen wasn't trashed, only one line and there are no waiting + * messages, don't wait, but don't overwrite it with mode information + * either. If there's a screen under this one, change the line to + * inverse video. + */ + if (svp->extotalcount > 0) + if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY))) + F_SET(sp, S_UPDATE_MODE); + else + (void)svi_ex_scroll(sp, 1, 0, NULL); + return (svi_ex_done(sp, ep, rp) || rval); +} + +/* + * svi_ex_run -- + * Execute strings of ex commands. + */ +int +svi_ex_run(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + enum input (*get) __P((SCR *, EXF *, TEXTH *, int, u_int)); + struct termios rawt, t; + CH ikey; + SVI_PRIVATE *svp; + TEXT *tp; + int flags, in_exmode, rval; + + svp = SVP(sp); + svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0; + + /* + * There's some tricky stuff going on here to handle when a user has + * mapped a key to multiple ex commands. Historic practice was that + * vi ran without any special actions, as if the user were entering + * the characters, until ex trashed the screen, e.g. something like a + * '!' command. At that point, we no longer know what the screen + * looks like, so we can't afford to overwrite anything. The solution + * is to go into real ex mode until we get to the end of the command + * strings. + */ + get = svi_get; + flags = TXT_BS | TXT_PROMPT; + for (in_exmode = rval = 0;;) { + if (get(sp, ep, &sp->tiq, ':', flags) != INP_OK) { + rval = 1; + break; + } + + /* + * Len is 0 if the user backspaced over the prompt, + * 1 if only a CR was entered. + */ + tp = sp->tiq.cqh_first; + if (tp->len == 0) + break; + + if (!in_exmode) + (void)svi_busy(sp, NULL); + + (void)ex_icmd(sp, ep, tp->lb, tp->len); + (void)ex_fflush(EXCOOKIE); + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + break; + + /* + * If continue not required, and one or no lines, and there + * are no waiting messages, don't wait, but don't overwrite + * it with mode information either. If there's a screen under + * this one, change the line to inverse video. + */ + if (!F_ISSET(sp, S_CONTINUE) && + (svp->extotalcount == 0 || svp->extotalcount == 1 && + (sp->msgq.lh_first == NULL || + F_ISSET(sp->msgq.lh_first, M_EMPTY)))) { + if (svp->extotalcount == 1) + F_SET(sp, S_UPDATE_MODE); + break; + } + + /* If the screen is trashed, go into ex mode. */ + if (!in_exmode && F_ISSET(sp, S_REFRESH)) { + /* Initialize the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY)) { + SEX_RAW(t, rawt); + get = sex_get; + } else + get = sex_get_notty; + flags = TXT_CR | TXT_NLECHO | TXT_PROMPT; + in_exmode = 1; + } + + /* + * If the user hasn't already indicated that they're done, + * they may continue in ex mode by entering a ':'. + */ + if (F_ISSET(sp, S_INTERRUPTED)) + break; + + /* No longer interruptible. */ + F_CLR(sp, S_INTERRUPTIBLE); + + if (in_exmode) { + (void)write(STDOUT_FILENO, + CONTMSG, sizeof(CONTMSG) - 1); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) { + rval = 1; + goto ret; + } + if (ikey.ch == ' ' || ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + sex_bell(sp); + } + } else + (void)svi_ex_scroll(sp, 1, 1, &ikey); + if (ikey.ch != ':') + break; + + if (in_exmode) + (void)write(STDOUT_FILENO, "\r\n", 2); + else { + ++svp->extotalcount; + ++svp->exlinecount; + } + } + +ret: if (in_exmode) { + /* Reset the terminal state. */ + if (F_ISSET(sp->gp, G_ISFROMTTY) && SEX_NORAW(t)) + rval = 1; + F_SET(sp, S_REFRESH); + } else + if (svi_ex_done(sp, ep, rp)) + rval = 1; + F_CLR(sp, S_CONTINUE); + return (rval); +} + +/* + * svi_ex_done -- + * Cleanup from dipping into ex. + */ +static int +svi_ex_done(sp, ep, rp) + SCR *sp; + EXF *ep; + MARK *rp; +{ + SMAP *smp; + SVI_PRIVATE *svp; + recno_t lno; + size_t cnt, len; + + /* + * The file or screen may have changed, in which case, + * the main editor loop takes care of it. + */ + if (F_ISSET(sp, S_MAJOR_CHANGE)) + return (0); + + /* + * Otherwise, the only cursor modifications will be real, however, the + * underlying line may have changed; don't trust anything. This code + * has been a remarkably fertile place for bugs. + * + * Repaint the entire screen if at least half the screen is trashed. + * Else, repaint only over the overwritten lines. The "-2" comes + * from one for the mode line and one for the fact that it's an offset. + * Note the check for small screens. + * + * Don't trust ANYTHING. + */ + svp = SVP(sp); + if (svp->extotalcount >= HALFTEXT(sp)) + F_SET(sp, S_REDRAW); + else + for (cnt = sp->rows - 2; svp->extotalcount--; --cnt) + if (cnt > sp->t_rows) { + MOVE(sp, cnt, 0); + clrtoeol(); + } else { + smp = HMAP + cnt; + SMAP_FLUSH(smp); + if (svi_line(sp, ep, smp, NULL, NULL)) + return (1); + } + /* + * Do a reality check on a cursor value, and make sure it's okay. + * If necessary, change it. Ex keeps track of the line number, + * but ex doesn't care about the column and it may have disappeared. + */ + if (file_gline(sp, ep, sp->lno, &len) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno != 0) + GETLINE_ERR(sp, sp->lno); + sp->lno = 1; + sp->cno = 0; + } else if (sp->cno >= len) + sp->cno = len ? len - 1 : 0; + + rp->lno = sp->lno; + rp->cno = sp->cno; + return (0); +} + +/* + * svi_ex_write -- + * Write out the ex messages. + * + * There is no tab or character translation going on, so, whatever the ex + * and/or curses routines do with special characters is all that gets done. + * This is probably okay, I don't see any reason that user's tab settings + * should affect ex output, and ex should have displayed everything else + * exactly as it wanted it on the screen. + */ +int +svi_ex_write(cookie, line, llen) + void *cookie; + const char *line; + int llen; +{ + SCR *sp; + SVI_PRIVATE *svp; + size_t oldy, oldx; + int len, rlen; + const char *p; + + /* + * XXX + * If it's a 4.4BSD system, we could just use fpurge(3). + * This shouldn't be too expensive, though. + */ + sp = cookie; + svp = SVP(sp); + if (F_ISSET(sp, S_INTERRUPTED)) + return (llen); + + p = line; /* In case of a write of 0. */ + for (rlen = llen; llen;) { + /* Get the next line. */ + if ((p = memchr(line, '\n', llen)) == NULL) + len = llen; + else + len = p - line; + + /* + * The max is sp->cols characters, and we may + * have already written part of the line. + */ + if (len + svp->exlcontinue > sp->cols) + len = sp->cols - svp->exlcontinue; + + /* + * If the first line output, do nothing. + * If the second line output, draw the divider line. + * If drew a full screen, remove the divider line. + * If it's a continuation line, move to the continuation + * point, else, move the screen up. + */ + if (svp->exlcontinue == 0) { + if (svp->extotalcount == 1) { + MOVE(sp, INFOLINE(sp) - 1, 0); + clrtoeol(); + if (svi_divider(sp)) + return (-1); + F_SET(svp, SVI_DIVIDER); + ++svp->extotalcount; + ++svp->exlinecount; + } + if (svp->extotalcount == sp->t_maxrows && + F_ISSET(svp, SVI_DIVIDER)) { + --svp->extotalcount; + --svp->exlinecount; + F_CLR(svp, SVI_DIVIDER); + } + if (svp->extotalcount != 0 && + svi_ex_scroll(sp, 0, 0, NULL)) + return (-1); + MOVE(sp, INFOLINE(sp), 0); + ++svp->extotalcount; + ++svp->exlinecount; + if (F_ISSET(sp, S_INTERRUPTIBLE) && + F_ISSET(sp, S_INTERRUPTED)) + break; + } else + MOVE(sp, INFOLINE(sp), svp->exlcontinue); + + /* Display the line. */ + if (len) + ADDNSTR(line, len); + + /* Clear to EOL. */ + getyx(stdscr, oldy, oldx); + if (oldx < sp->cols) + clrtoeol(); + + /* If we loop, it's a new line. */ + svp->exlcontinue = 0; + + /* Reset for the next line. */ + line += len; + llen -= len; + if (p != NULL) { + ++line; + --llen; + } + } + /* Refresh the screen, even if it's a partial. */ + refresh(); + + /* Set up next continuation line. */ + if (p == NULL) + getyx(stdscr, oldy, svp->exlcontinue); + return (rlen); +} + +/* + * svi_ex_scroll -- + * Scroll the screen for ex output. + */ +static int +svi_ex_scroll(sp, mustwait, colon_ok, chp) + SCR *sp; + int mustwait, colon_ok; + CH *chp; +{ + CH ikey; + SVI_PRIVATE *svp; + + /* + * Scroll the screen. Instead of scrolling the entire screen, delete + * the line above the first line output so preserve the maximum amount + * of the screen. + */ + svp = SVP(sp); + if (svp->extotalcount >= sp->rows) { + MOVE(sp, 0, 0); + } else + MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0); + + deleteln(); + + /* If there are screens below us, push them back into place. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) { + MOVE(sp, INFOLINE(sp), 0); + insertln(); + } + + /* If just displayed a full screen, wait. */ + if (mustwait || svp->exlinecount == sp->t_maxrows) { + MOVE(sp, INFOLINE(sp), 0); + if (F_ISSET(sp, S_INTERRUPTIBLE)) { + ADDNSTR(CONTMSG_I, (int)sizeof(CONTMSG_I) - 1); + } else { + ADDNSTR(CONTMSG, (int)sizeof(CONTMSG) - 1); + } + clrtoeol(); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + return (-1); + if (ikey.ch == ' ') + break; + if (colon_ok && ikey.ch == ':') + break; + if (ikey.value == K_CR || ikey.value == K_NL) + break; + if (ikey.ch == QUIT_CH && + F_ISSET(sp, S_INTERRUPTIBLE)) { + F_SET(sp, S_INTERRUPTED); + break; + } + svi_bell(sp); + } + if (chp != NULL) + *chp = ikey; + svp->exlinecount = 0; + } + return (0); +} diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c new file mode 100644 index 000000000000..46f5f0e240cb --- /dev/null +++ b/usr.bin/vi/svi/svi_get.c @@ -0,0 +1,150 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_get.c 8.19 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +/* + * svi_get -- + * Fill a buffer from the terminal for vi. + */ +enum input +svi_get(sp, ep, tiqh, prompt, flags) + SCR *sp; + EXF *ep; + TEXTH *tiqh; + int prompt; + u_int flags; +{ + MARK save; + SMAP *esmp; + recno_t bot_lno, top_lno; + size_t bot_off, cnt, top_off; + int eval; + + /* + * The approach used is to fake like the user is doing input on + * the last line of the screen. This makes all of the scrolling + * work correctly, and allows us the use of the vi text editing + * routines, not to mention practically infinite length ex commands. + * + * Save the current location. + */ + bot_lno = TMAP->lno; + bot_off = TMAP->off; + top_lno = HMAP->lno; + top_off = HMAP->off; + save.lno = sp->lno; + save.cno = sp->cno; + + /* + * If it's a small screen, TMAP may be small for the screen. + * Fix it, filling in fake lines as we go. + */ + if (ISSMALLSCREEN(sp)) + for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + } + + /* Build the fake entry. */ + TMAP[1].lno = TMAP[0].lno + 1; + TMAP[1].off = 1; + SMAP_FLUSH(&TMAP[1]); + ++TMAP; + + /* Move to it. */ + sp->lno = TMAP[0].lno; + sp->cno = 0; + + if (O_ISSET(sp, O_ALTWERASE)) + LF_SET(TXT_ALTWERASE); + if (O_ISSET(sp, O_TTYWERASE)) + LF_SET(TXT_TTYWERASE); + LF_SET(TXT_APPENDEOL | + TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); + + /* Don't update the modeline for now. */ + F_SET(SVP(sp), SVI_INFOLINE); + + eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags); + + F_CLR(SVP(sp), SVI_INFOLINE); + + /* Put it all back. */ + --TMAP; + sp->lno = save.lno; + sp->cno = save.cno; + + /* + * If it's a small screen, TMAP may be wrong. Clear any + * lines that might have been overwritten. + */ + if (ISSMALLSCREEN(sp)) { + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } + + /* + * The map may be wrong if the user entered more than one + * (logical) line. Fix it. If the user entered a whole + * screen, this will be slow, but it's not worth caring. + */ + while (bot_lno != TMAP->lno || bot_off != TMAP->off) + if (svi_sm_1down(sp, ep)) + return (INP_ERR); + + /* + * Invalidate the cursor, the line never really existed. This fixes + * a bug where the user searches for the last line on the screen + 1 + * and the refresh routine thinks that's where we just were. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + return (eval ? INP_ERR : INP_OK); +} diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c new file mode 100644 index 000000000000..2370a0f5eea8 --- /dev/null +++ b/usr.bin/vi/svi/svi_line.c @@ -0,0 +1,421 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_line.c 8.18 (Berkeley) 1/22/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +#if defined(DEBUG) && 0 +#define TABCH '-' +#define TABSTR "--------------------" +#else +#define TABSTR " " +#define TABCH ' ' +#endif + +/* + * svi_line -- + * Update one line on the screen. + */ +int +svi_line(sp, ep, smp, yp, xp) + SCR *sp; + EXF *ep; + SMAP *smp; + size_t *xp, *yp; +{ + CHNAME const *cname; + SMAP *tsmp; + size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens; + size_t offset_in_char, offset_in_line; + size_t oldy, oldx; + int ch, is_cached, is_infoline, is_partial, is_tab, listset; + char *p, nbuf[10]; + +#if defined(DEBUG) && 0 + TRACE(sp, "svi_line: row %u: line: %u off: %u\n", + smp - HMAP, smp->lno, smp->off); +#endif + + /* + * Assume that, if the cache entry for the line is filled in, the + * line is already on the screen, and all we need to do is return + * the cursor position. If the calling routine doesn't need the + * cursor position, we can just return. + */ + is_cached = SMAP_CACHE(smp); + if (yp == NULL && is_cached) + return (0); + + /* + * A nasty side effect of this routine is that it returns the screen + * position for the "current" character. Not pretty, but this is the + * only routine that really knows what's out there. + * + * Move to the line. This routine can be called by svi_sm_position(), + * which uses it to fill in the cache entry so it can figure out what + * the real contents of the screen are. Because of this, we have to + * return to whereever we started from. + */ + getyx(stdscr, oldy, oldx); + MOVE(sp, smp - HMAP, 0); + + /* Get the character map. */ + cname = sp->gp->cname; + + /* Get a copy of the line. */ + p = file_gline(sp, ep, smp->lno, &len); + + /* + * Special case if we're printing the info/mode line. Skip printing + * the leading number, as well as other minor setup. If painting the + * line between two screens, it's always in reverse video. The only + * time this code paints the mode line is when the user is entering + * text for a ":" command, so we can put the code here instead of + * dealing with the empty line logic below. This is a kludge, but it's + * pretty much confined to this module. + * + * Set the number of screens to skip until a character is displayed. + * Left-right screens are special, because we don't bother building + * a buffer to be skipped over. + * + * Set the number of columns for this screen. + */ + cols_per_screen = sp->cols; + if (is_infoline = ISINFOLINE(sp, smp)) { + listset = 0; + if (O_ISSET(sp, O_LEFTRIGHT)) + skip_screens = 0; + else + skip_screens = smp->off - 1; + } else { + listset = O_ISSET(sp, O_LIST); + skip_screens = smp->off - 1; + + /* + * If O_NUMBER is set and it's line number 1 or the line exists + * and this is the first screen of a folding line or any left- + * right line, display the line number. + */ + if (O_ISSET(sp, O_NUMBER)) { + cols_per_screen -= O_NUMBER_LENGTH; + if ((smp->lno == 1 || p != NULL) && skip_screens == 0) { + (void)snprintf(nbuf, + sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + } + } + + /* + * Special case non-existent lines and the first line of an empty + * file. In both cases, the cursor position is 0, but corrected + * for the O_NUMBER field if it was displayed. + */ + if (p == NULL || len == 0) { + /* Fill in the cursor. */ + if (yp != NULL && smp->lno == sp->lno) { + *yp = smp - HMAP; + *xp = sp->cols - cols_per_screen; + } + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + + /* Set line cacheing information. */ + smp->c_sboff = smp->c_eboff = 0; + smp->c_scoff = smp->c_eclen = 0; + + if (p == NULL) { + if (smp->lno != 1) + ADDCH(listset && skip_screens == 0 ? '$' : '~'); + } else if (listset && skip_screens == 0) + ADDCH('$'); + + clrtoeol(); + MOVEA(sp, oldy, oldx); + return (0); + } + + /* + * If we wrote a line that's this or a previous one, we can do this + * much more quickly -- we cached the starting and ending positions + * of that line. The way it works is we keep information about the + * lines displayed in the SMAP. If we're painting the screen in + * the forward, this saves us from reformatting the physical line for + * every line on the screen. This wins big on binary files with 10K + * lines. + * + * Test for the first screen of the line, then the current screen line, + * then the line behind us, then do the hard work. Note, it doesn't + * do us any good to have a line in front of us -- it would be really + * hard to try and figure out tabs in the reverse direction, i.e. how + * many spaces a tab takes up in the reverse direction depends on + * what characters preceded it. + */ + if (smp->off == 1) { + smp->c_sboff = offset_in_line = 0; + smp->c_scoff = offset_in_char = 0; + p = &p[offset_in_line]; + } else if (is_cached) { + offset_in_line = smp->c_sboff; + offset_in_char = smp->c_scoff; + p = &p[offset_in_line]; + if (skip_screens != 0) + cols_per_screen = sp->cols; + } else if (smp != HMAP && + SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) { + if (tsmp->c_eclen != tsmp->c_ecsize) { + offset_in_line = tsmp->c_eboff; + offset_in_char = tsmp->c_eclen; + } else { + offset_in_line = tsmp->c_eboff + 1; + offset_in_char = 0; + } + + /* Put starting info for this line in the cache. */ + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + p = &p[offset_in_line]; + if (skip_screens != 0) + cols_per_screen = sp->cols; + } else { + offset_in_line = 0; + offset_in_char = 0; + + /* This is the loop that skips through screens. */ + if (skip_screens == 0) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char; + } else for (scno = 0; offset_in_line < len; ++offset_in_line) { + scno += chlen = + (ch = *(u_char *)p++) == '\t' && !listset ? + TAB_OFF(sp, scno) : cname[ch].len; + if (scno < cols_per_screen) + continue; + /* + * Reset cols_per_screen to second and subsequent line + * length. + */ + scno -= cols_per_screen; + cols_per_screen = sp->cols; + + /* + * If crossed the last skipped screen boundary, start + * displaying the characters. + */ + if (--skip_screens) + continue; + + /* Put starting info for this line in the cache. */ + if (scno) { + smp->c_sboff = offset_in_line; + smp->c_scoff = offset_in_char = chlen - scno; + --p; + } else { + smp->c_sboff = ++offset_in_line; + smp->c_scoff = 0; + } + break; + } + } + + /* + * Set the number of characters to skip before reaching the cursor + * character. Offset by 1 and use 0 as a flag value. Svi_line is + * called repeatedly with a valid pointer to a cursor position. + * Don't fill anything in unless it's the right line and the right + * character, and the right part of the character... + */ + if (yp == NULL || + smp->lno != sp->lno || sp->cno < offset_in_line || + offset_in_line + cols_per_screen < sp->cno) { + cno_cnt = 0; + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } else + cno_cnt = (sp->cno - offset_in_line) + 1; + + /* This is the loop that actually displays characters. */ + for (is_partial = 0, scno = 0; + offset_in_line < len; ++offset_in_line, offset_in_char = 0) { + if ((ch = *(u_char *)p++) == '\t' && !listset) { + scno += chlen = TAB_OFF(sp, scno) - offset_in_char; + is_tab = 1; + } else { + scno += chlen = cname[ch].len - offset_in_char; + is_tab = 0; + } + + /* + * Only display up to the right-hand column. Set a flag if + * the entire character wasn't displayed for use in setting + * the cursor. If reached the end of the line, set the cache + * info for the screen. Don't worry about there not being + * characters to display on the next screen, its lno/off won't + * match up in that case. + */ + if (scno >= cols_per_screen) { + smp->c_ecsize = chlen; + chlen -= scno - cols_per_screen; + smp->c_eclen = chlen; + smp->c_eboff = offset_in_line; + if (scno > cols_per_screen) + is_partial = 1; + + /* Terminate the loop. */ + offset_in_line = len; + } + + /* + * If the caller wants the cursor value, and this was the + * cursor character, set the value. There are two ways to + * put the cursor on a character -- if it's normal display + * mode, it goes on the last column of the character. If + * it's input mode, it goes on the first. In normal mode, + * set the cursor only if the entire character was displayed. + */ + if (cno_cnt && + --cno_cnt == 0 && (F_ISSET(sp, S_INPUT) || !is_partial)) { + *yp = smp - HMAP; + if (F_ISSET(sp, S_INPUT)) + *xp = scno - chlen; + else + *xp = scno - 1; + if (O_ISSET(sp, O_NUMBER) && + !is_infoline && smp->off == 1) + *xp += O_NUMBER_LENGTH; + + /* If the line is on the screen, quit. */ + if (is_cached) + goto ret; + } + + /* If the line is on the screen, don't display anything. */ + if (is_cached) + continue; + + /* + * Display the character. If it's a tab and tabs aren't some + * ridiculous length, do it fast. (We do tab expansion here + * because curses doesn't have a way to set the tab length.) + */ + if (is_tab) { + if (chlen <= sizeof(TABSTR) - 1) { + ADDNSTR(TABSTR, chlen); + } else + while (chlen--) + ADDCH(TABCH); + } else + ADDNSTR(cname[ch].name + offset_in_char, chlen); + } + + if (scno < cols_per_screen) { + /* If didn't paint the whole line, update the cache. */ + smp->c_ecsize = smp->c_eclen = cname[ch].len; + smp->c_eboff = len - 1; + + /* + * If not the info/mode line, and O_LIST set, and at the + * end of the line, and the line ended on this screen, + * add a trailing $. + */ + if (listset) { + ++scno; + ADDCH('$'); + } + + /* If still didn't paint the whole line, clear the rest. */ + if (scno < cols_per_screen) + clrtoeol(); + } + +ret: MOVEA(sp, oldy, oldx); + return (0); +} + +/* + * svi_number -- + * Repaint the numbers on all the lines. + */ +int +svi_number(sp, ep) + SCR *sp; + EXF *ep; +{ + SMAP *smp; + recno_t lno; + size_t oldy, oldx; + char *lp, *p, nbuf[10]; + + /* + * Try and avoid getting the last line in the file, by getting the + * line after the last line in the screen -- if it exists, we know + * we have to to number all the lines in the screen. Get the one + * after the last instead of the last, so that the info line doesn't + * fool us. + * + * If that test fails, we have to check each line for existence. + * + * XXX + * The problem is that file_lline will lie, and tell us that the + * info line is the last line in the file. + */ + lp = file_gline(sp, ep, TMAP->lno + 1, NULL); + + getyx(stdscr, oldy, oldx); + for (smp = HMAP; smp <= TMAP; ++smp) { + if (smp->off != 1) + continue; + if (ISINFOLINE(sp, smp)) + break; + if (smp->lno != 1 && lp == NULL && + (p = file_gline(sp, ep, smp->lno, NULL)) == NULL) + break; + MOVE(sp, smp - HMAP, 0); + (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno); + ADDSTR(nbuf); + } + MOVEA(sp, oldy, oldx); + return (0); +} diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c new file mode 100644 index 000000000000..353c08178e20 --- /dev/null +++ b/usr.bin/vi/svi/svi_refresh.c @@ -0,0 +1,839 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_refresh.c 8.43 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static int svi_modeline __P((SCR *, EXF *)); +static int svi_msgflush __P((SCR *)); + +int +svi_refresh(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + u_int paintbits; + + /* + * 1: Resize the screen. + * + * Notice that a resize is requested, and set up everything so that + * the file gets reinitialized. Done here, instead of in the vi loop + * because there may be other initialization that other screens need + * to do. The actual changing of the row/column values was done by + * calling the ex options code which put them into the environment, + * which is used by curses. Stupid, but ugly. + */ + if (F_ISSET(sp, S_RESIZE)) { + /* Reinitialize curses. */ + if (svi_curses_end(sp) || svi_curses_init(sp)) + return (1); + + /* Lose any svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* + * Fill the map, incidentally losing any svi_line() + * cached information. + */ + if (sp->s_fill(sp, ep, sp->lno, P_FILL)) + return (1); + F_CLR(sp, S_RESIZE | S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: S_REFRESH + * + * If S_REFRESH is set in the current screen, repaint everything + * that we can find. + */ + if (F_ISSET(sp, S_REFRESH)) + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp) + F_SET(tsp, S_REDRAW); + /* + * 3: Related or dirtied screens, or screens with messages. + * + * If related screens share a view into a file, they may have been + * modified as well. Refresh any screens with paint or dirty bits + * set, or where messages are waiting. Finally, if we refresh any + * screens other than the current one, the cursor will be trashed. + */ + paintbits = S_REDRAW | S_REFORMAT | S_REFRESH; + if (O_ISSET(sp, O_NUMBER)) + paintbits |= S_RENUMBER; + for (tsp = sp->gp->dq.cqh_first; + tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next) + if (tsp != sp && + (F_ISSET(tsp, paintbits) || + F_ISSET(SVP(tsp), SVI_SCREENDIRTY) || + tsp->msgq.lh_first != NULL && + !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) { + (void)svi_paint(tsp, tsp->ep); + F_CLR(SVP(tsp), SVI_SCREENDIRTY); + F_SET(SVP(sp), SVI_CUR_INVALID); + } + + /* + * 4: Refresh the current screen. + * + * Always refresh the current screen, it may be a cursor movement. + * Also, always do it last -- that way, S_REFRESH can be set in + * the current screen only, and the screen won't flash. + */ + F_CLR(sp, SVI_SCREENDIRTY); + return (svi_paint(sp, ep)); +} + +/* + * svi_paint -- + * This is the guts of the vi curses screen code. The idea is that + * the SCR structure passed in contains the new coordinates of the + * screen. What makes this hard is that we don't know how big + * characters are, doing input can put the cursor in illegal places, + * and we're frantically trying to avoid repainting unless it's + * absolutely necessary. If you change this code, you'd better know + * what you're doing. It's subtle and quick to anger. + */ +int +svi_paint(sp, ep) + SCR *sp; + EXF *ep; +{ + CHNAME const *cname; + SMAP *smp, tmp; + SVI_PRIVATE *svp; + recno_t lastline, lcnt; + size_t cwtotal, cnt, len, x, y; + int ch, didpaint; + char *p; + +#define LNO sp->lno +#define OLNO svp->olno +#define CNO sp->cno +#define OCNO svp->ocno +#define SCNO svp->sc_col + + didpaint = 0; + svp = SVP(sp); + + /* + * 1: Reformat the lines. + * + * If the lines themselves have changed (:set list, for example), + * fill in the map from scratch. Adjust the screen that's being + * displayed if the leftright flag is set. + */ + if (F_ISSET(sp, S_REFORMAT)) { + /* Toss svi_screens() cached information. */ + SVP(sp)->ss_lno = OOBLNO; + + /* Toss svi_line() cached information. */ + if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP)) + return (1); + if (O_ISSET(sp, O_LEFTRIGHT) && + (cnt = svi_screens(sp, ep, LNO, &CNO)) != 1) + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + F_CLR(sp, S_REFORMAT); + F_SET(sp, S_REDRAW); + } + + /* + * 2: Line movement. + * + * Line changes can cause the top line to change as well. As + * before, if the movement is large, the screen is repainted. + * + * 2a: Tiny screens. + * + * Tiny screens cannot be permitted into the "scrolling" parts of + * the smap code for two reasons. If the screen size is 1 line, + * HMAP == TMAP and the code will quickly drop core. If the screen + * size is 2, none of the divisions by 2 will work, and scrolling + * won't work. In fact, because no line change will be less than + * HALFTEXT(sp), we always ending up "filling" the map, with a + * P_MIDDLE flag, which isn't what the user wanted. Tiny screens + * can go into the "fill" portions of the smap code, however. + */ + if (sp->t_rows <= 2) { + if (LNO < HMAP->lno) { + if (svi_sm_fill(sp, ep, LNO, P_TOP)) + return (1); + } else if (LNO > TMAP->lno) + if (svi_sm_fill(sp, ep, LNO, P_BOTTOM)) + return (1); + if (sp->t_rows == 1) { + HMAP->off = svi_screens(sp, ep, LNO, &CNO); + goto paint; + } + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * 2b: Small screens. + * + * Users can use the window, w300, w1200 and w9600 options to make + * the screen artificially small. The behavior of these options + * in the historic vi wasn't all that consistent, and, in fact, it + * was never documented how various screen movements affected the + * screen size. Generally, one of three things would happen: + * 1: The screen would expand in size, showing the line + * 2: The screen would scroll, showing the line + * 3: The screen would compress to its smallest size and + * repaint. + * In general, scrolling didn't cause compression (200^D was handled + * the same as ^D), movement to a specific line would (:N where N + * was 1 line below the screen caused a screen compress), and cursor + * movement would scroll if it was 11 lines or less, and compress if + * it was more than 11 lines. (And, no, I have no idea where the 11 + * comes from.) + * + * What we do is try and figure out if the line is less than half of + * a full screen away. If it is, we expand the screen if there's + * room, and then scroll as necessary. The alternative is to compress + * and repaint. + * + * !!! + * This code is a special case from beginning to end. Unfortunately, + * home modems are still slow enough that it's worth having. + * + * XXX + * If the line a really long one, i.e. part of the line is on the + * screen but the column offset is not, we'll end up in the adjust + * code, when we should probably have compressed the screen. + */ + if (ISSMALLSCREEN(sp)) + if (LNO < HMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + } + else + goto small_fill; + } else if (LNO > TMAP->lno) { + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows); + if (lcnt <= HALFSCREEN(sp)) + for (; lcnt && sp->t_rows != sp->t_maxrows; + --lcnt, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, TMAP + 1)) + return (1); + ++TMAP; + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + else { +small_fill: MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; sp->t_rows > sp->t_minrows; + --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + if (svi_sm_fill(sp, ep, LNO, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + } + + /* + * 3a: Line down. + */ + if (LNO >= HMAP->lno) { + if (LNO <= TMAP->lno) + goto adjust; + + /* + * If less than half a screen away, scroll down until the + * line is on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1up(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than a full screen from the bottom of the file, put + * the last line of the file on the bottom of the screen. The + * calculation is safe because we know there's at least one + * full screen of lines, otherwise couldn't have gotten here. + */ + if (file_lline(sp, ep, &lastline)) + return (1); + tmp.lno = LNO; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows); + if (lcnt < sp->t_rows) { + if (svi_sm_fill(sp, ep, lastline, P_BOTTOM)) + return (1); + F_SET(sp, S_REDRAW); + goto adjust; + } + + /* + * If more than a full screen from the last line of the file, + * put the new line in the middle of the screen. + */ + goto middle; + } + + /* + * 3b: Line up. + * + * If less than half a screen away, scroll up until the line is + * the first line on the screen. + */ + lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + while (lcnt--) + if (svi_sm_1down(sp, ep)) + return (1); + goto adjust; + } + + /* + * If less than half a screen from the top of the file, put the first + * line of the file at the top of the screen. Otherwise, put the line + * in the middle of the screen. + */ + tmp.lno = 1; + tmp.off = 1; + lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp)); + if (lcnt < HALFTEXT(sp)) { + if (svi_sm_fill(sp, ep, 1, P_TOP)) + return (1); + } else +middle: if (svi_sm_fill(sp, ep, LNO, P_MIDDLE)) + return (1); + F_SET(sp, S_REDRAW); + + /* + * At this point we know part of the line is on the screen. Since + * scrolling is done using logical lines, not physical, all of the + * line may not be on the screen. While that's not necessarily bad, + * if the part the cursor is on isn't there, we're going to lose. + * This can be tricky; if the line covers the entire screen, lno + * may be the same as both ends of the map, that's why we test BOTH + * the top and the bottom of the map. This isn't a problem for + * left-right scrolling, the cursor movement code handles the problem. + * + * There's a performance issue here if editing *really* long lines. + * This gets to the right spot by scrolling, and, in a binary, by + * scrolling hundreds of lines. If the adjustment looks like it's + * going to be a serious problem, refill the screen and repaint. + */ +adjust: if (!O_ISSET(sp, O_LEFTRIGHT) && + (LNO == HMAP->lno || LNO == TMAP->lno)) { + cnt = svi_screens(sp, ep, LNO, &CNO); + if (LNO == HMAP->lno && cnt < HMAP->off) + if ((HMAP->off - cnt) > HALFTEXT(sp)) { + HMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_TOP); + F_SET(sp, S_REDRAW); + } else + while (cnt < HMAP->off) + if (svi_sm_1down(sp, ep)) + return (1); + if (LNO == TMAP->lno && cnt > TMAP->off) + if ((cnt - TMAP->off) > HALFTEXT(sp)) { + TMAP->off = cnt; + svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM); + F_SET(sp, S_REDRAW); + } else + while (cnt > TMAP->off) + if (svi_sm_1up(sp, ep)) + return (1); + } + + /* If the screen needs to be repainted, skip cursor optimization. */ + if (F_ISSET(sp, S_REDRAW)) + goto paint; + + /* + * 4: Cursor movements. + * + * Decide cursor position. If the line has changed, the cursor has + * moved over a tab, or don't know where the cursor was, reparse the + * line. Note, if we think that the cursor "hasn't moved", reparse + * the line. This is 'cause if it hasn't moved, we've almost always + * lost track of it. + * + * Otherwise, we've just moved over fixed-width characters, and can + * calculate the left/right scrolling and cursor movement without + * reparsing the line. Note that we don't know which (if any) of + * the characters between the old and new cursor positions changed. + * + * XXX + * With some work, it should be possible to handle tabs quickly, at + * least in obvious situations, like moving right and encountering + * a tab, without reparsing the whole line. + */ + + /* If the line we're working with has changed, reparse. */ + if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) { + F_CLR(SVP(sp), SVI_CUR_INVALID); + goto slow; + } + + /* Otherwise, if nothing's changed, go fast. */ + if (CNO == OCNO) + goto fast; + + /* + * Get the current line. If this fails, we either have an empty + * file and can just repaint, or there's a real problem. This + * isn't a performance issue because there aren't any ways to get + * here repeatedly. + */ + if ((p = file_gline(sp, ep, LNO, &len)) == NULL) { + if (file_lline(sp, ep, &lastline)) + return (1); + if (lastline == 0) + goto slow; + GETLINE_ERR(sp, LNO); + return (1); + } + +#ifdef DEBUG + /* This is just a test. */ + if (CNO >= len && len != 0) { + msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)", + tail(__FILE__), __LINE__, CNO, len); + return (1); + } +#endif + /* + * The basic scheme here is to look at the characters in between + * the old and new positions and decide how big they are on the + * screen, and therefore, how many screen positions to move. + */ + cname = sp->gp->cname; + if (CNO < OCNO) { + /* + * 4a: Cursor moved left. + * + * Point to the old character. The old cursor position can + * be past EOL if, for example, we just deleted the rest of + * the line. In this case, since we don't know the width of + * the characters we traversed, we have to do it slowly. + */ + p += OCNO; + cnt = (OCNO - CNO) + 1; + if (OCNO >= len) + goto slow; + + /* + * Quit sanity check -- it's hard to figure out exactly when + * we cross a screen boundary as we do in the cursor right + * movement. If cnt is so large that we're going to cross the + * boundary no matter what, stop now. + */ + if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt) + goto lscreen; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. + */ + for (cwtotal = 0; cnt--; cwtotal += cname[ch].len) + if ((ch = *(u_char *)p--) == '\t') + goto slow; + + /* + * Decrement the screen cursor by the total width of the + * characters minus 1. + */ + cwtotal -= 1; + + /* + * If we're moving left, and there's a wide character in the + * current position, go to the end of the character. + */ + if (cname[ch].len > 1) + cwtotal -= cname[ch].len - 1; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO < cwtotal) { +lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) { + for (smp = HMAP; smp <= TMAP; ++smp) + --smp->off; + goto paint; + } + goto slow; + } + SCNO -= cwtotal; + } else { + /* + * 4b: Cursor moved right. + * + * Point to the first character to the right. + */ + p += OCNO + 1; + cnt = CNO - OCNO; + + /* + * Count up the widths of the characters. If it's a tab + * character, go do it the the slow way. If we cross a + * screen boundary, we can quit. + */ + for (cwtotal = SCNO; cnt--;) { + if ((ch = *(u_char *)p++) == '\t') + goto slow; + if ((cwtotal += cname[ch].len) >= SCREEN_COLS(sp)) + break; + } + + /* + * Increment the screen cursor by the total width of the + * characters. + */ + SCNO = cwtotal; + + /* + * If the new column moved us out of the current screen, + * calculate a new screen. + */ + if (SCNO >= SCREEN_COLS(sp)) { + if (O_ISSET(sp, O_LEFTRIGHT)) { + SCNO -= SCREEN_COLS(sp); + for (smp = HMAP; smp <= TMAP; ++smp) + ++smp->off; + goto paint; + } + goto slow; + } + } + + /* + * 4c: Fast cursor update. + * + * Retrieve the current cursor position, and correct it + * for split screens. + */ +fast: getyx(stdscr, y, x); + y -= sp->woff; + goto number; + + /* + * 4d: Slow cursor update. + * + * Walk through the map and find the current line. If doing left-right + * scrolling and the cursor movement has changed the screen displayed, + * scroll the screen left or right, unless we're updating the info line + * in which case we just scroll that one line. Then update the screen + * lines for this file line until we have a new screen cursor position. + */ +slow: for (smp = HMAP; smp->lno != LNO; ++smp); + if (O_ISSET(sp, O_LEFTRIGHT)) { + cnt = svi_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp); + if (cnt != HMAP->off) { + if (ISINFOLINE(sp, smp)) + smp->off = cnt; + else + for (smp = HMAP; smp <= TMAP; ++smp) + smp->off = cnt; + goto paint; + } + } + for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) { + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + if (y != -1) + break; + } + goto number; + + /* + * 5: Repaint the entire screen. + * + * Lost big, do what you have to do. We flush the cache as S_REDRAW + * gets set when the screen isn't worth fixing, and it's simpler to + * repaint. So, don't trust anything that we think we know about it. + */ +paint: for (smp = HMAP; smp <= TMAP; ++smp) + SMAP_FLUSH(smp); + for (smp = HMAP; smp <= TMAP; ++smp) + if (svi_line(sp, ep, smp, &y, &SCNO)) + return (1); + /* + * If it's a small screen and we're redrawing, clear the unused lines, + * ex may have overwritten them. + */ + if (F_ISSET(sp, S_REDRAW)) { + if (ISSMALLSCREEN(sp)) + for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + F_CLR(sp, S_REDRAW); + } + + didpaint = 1; + + /* + * 6: Repaint the line numbers. + * + * If O_NUMBER is set and the S_RENUMBER bit is set, and we didn't + * repaint the screen, repaint all of the line numbers, they've + * changed. + */ +number: if (O_ISSET(sp, O_NUMBER) && F_ISSET(sp, S_RENUMBER) && !didpaint) { + if (svi_number(sp, ep)) + return (1); + F_CLR(sp, S_RENUMBER); + } + + /* + * 7: Refresh the screen. + * + * If the screen was corrupted, refresh it. + */ + if (F_ISSET(sp, S_REFRESH)) { + wrefresh(curscr); + F_CLR(sp, S_REFRESH); + } + + if (F_ISSET(sp, S_BELLSCHED)) + svi_bell(sp); + /* + * If the bottom line isn't in use by the colon command: + * + * Display any messages. Don't test S_UPDATE_MODE. The + * message printing routine set it to avoid anyone else + * destroying the message we're about to display. + * + * If the bottom line isn't in use by anyone, put out the + * standard status line. + */ + if (!F_ISSET(SVP(sp), SVI_INFOLINE)) + if (sp->msgq.lh_first != NULL && + !F_ISSET(sp->msgq.lh_first, M_EMPTY)) + svi_msgflush(sp); + else if (!F_ISSET(sp, S_UPDATE_MODE)) + svi_modeline(sp, ep); + + /* Update saved information. */ + OCNO = CNO; + OLNO = LNO; + + /* Place the cursor. */ + MOVE(sp, y, SCNO); + + /* Flush it all out. */ + refresh(); + + return (0); +} + +/* + * svi_msgflush -- + * Flush any accumulated messages. + */ +static int +svi_msgflush(sp) + SCR *sp; +{ + CH ikey; + CHAR_T ch; + CHNAME const *cname; + MSG *mp; + size_t chlen, len; + char *p; + +#define MCONTMSG " [More ...]" + + /* Display the messages. */ + cname = sp->gp->cname; + for (mp = sp->msgq.lh_first, p = NULL; + mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) { + p = mp->mbuf; + +lcont: /* Move to the message line and clear it. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* + * Turn on standout mode if requested, or, if we've split + * the screen and need a divider. + */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standout(); + + /* + * Print up to the "more" message. Avoid the last character + * in the last line, some hardware doesn't like it. + */ + if (svi_ncols(sp, p, mp->len, NULL) < sp->cols - 1) + len = sp->cols - 1; + else + len = (sp->cols - sizeof(MCONTMSG)) - 1; + for (;; ++p) { + if (!mp->len) + break; + ch = *(u_char *)p; + chlen = cname[ch].len; + if (chlen >= len) + break; + len -= chlen; + --mp->len; + ADDNSTR(cname[ch].name, chlen); + } + + /* + * If more, print continue message. If user key fails, + * keep showing the messages anyway. + */ + if (mp->len || (mp->q.le_next != NULL && + !F_ISSET(mp->q.le_next, M_EMPTY))) { + ADDNSTR(MCONTMSG, sizeof(MCONTMSG) - 1); + refresh(); + for (;;) { + if (term_user_key(sp, &ikey) != INP_OK) + break; + if (ikey.value == K_CR || + ikey.value == K_NL || ikey.ch == ' ') + break; + svi_bell(sp); + } + } + + /* Turn off standout mode. */ + if (F_ISSET(mp, M_INV_VIDEO) || + sp->q.cqe_next != (void *)&sp->gp->dq) + standend(); + + if (mp->len) + goto lcont; + + refresh(); + F_SET(mp, M_EMPTY); + } + return (0); +} + +#define RULERSIZE 15 +#define MODESIZE (RULERSIZE + 15) + +/* + * svi_modeline -- + * Update the mode line. + */ +static int +svi_modeline(sp, ep) + SCR *sp; + EXF *ep; +{ + char *s, buf[RULERSIZE]; + + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + + /* Display a dividing line if not the bottom screen. */ + if (sp->q.cqe_next != (void *)&sp->gp->dq) + svi_divider(sp); + + /* Display the ruler. */ + if (O_ISSET(sp, O_RULER) && sp->cols > RULERSIZE + 2) { + MOVE(sp, INFOLINE(sp), sp->cols / 2 - RULERSIZE / 2); + clrtoeol(); + (void)snprintf(buf, + sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1); + ADDSTR(buf); + } + + /* Show the modified bit. */ + if (O_ISSET(sp, O_SHOWDIRTY) && + F_ISSET(ep, F_MODIFIED) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 9); + ADDSTR("*"); + } + + /* + * Show the mode. Leave the last character blank, in case it's a + * really dumb terminal with hardware scroll. Second, don't try + * to *paint* the last character, SunOS 4.1.1 and Ultrix 4.2 curses + * won't let you paint the last character in the screen. + */ + if (O_ISSET(sp, O_SHOWMODE) && sp->cols > MODESIZE) { + MOVE(sp, INFOLINE(sp), sp->cols - 8); + s = F_ISSET(sp, S_INPUT) ? " Input" : "Command"; + ADDSTR(s); + } + + return (0); +} + +/* + * svi_divider -- + * Draw a dividing line between the screens. + */ +int +svi_divider(sp) + SCR *sp; +{ + size_t len; + +#define DIVIDESTR "+=+=+=+=+=+=+=+" + len = sizeof(DIVIDESTR) - 1 > sp->cols ? + sp->cols : sizeof(DIVIDESTR) - 1; + ADDNSTR(DIVIDESTR, len); + return (0); +} diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c new file mode 100644 index 000000000000..7daa972c8575 --- /dev/null +++ b/usr.bin/vi/svi/svi_relative.c @@ -0,0 +1,205 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_relative.c 8.7 (Berkeley) 12/29/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_column -- + * Return the logical column of the cursor. + */ +int +svi_column(sp, ep, cp) + SCR *sp; + EXF *ep; + size_t *cp; +{ + size_t col; + + col = SVP(sp)->sc_col; + if (O_ISSET(sp, O_NUMBER)) + col -= O_NUMBER_LENGTH; + *cp = col; + return (0); +} + +/* + * svi_relative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. If it's not easy, uses the underlying routine that + * really figures it out. It's broken into two parts because the + * svi_lrelative routine handles "logical" offsets, which nobody + * but the screen routines understand. + */ +size_t +svi_relative(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + size_t cno; + + /* First non-blank character. */ + if (sp->rcmflags == RCM_FNB) { + cno = 0; + (void)nonblank(sp, ep, lno, &cno); + return (cno); + } + + /* First character is easy, and common. */ + if (sp->rcmflags != RCM_LAST && sp->rcm == 0) + return (0); + + return (svi_lrelative(sp, ep, lno, 1)); +} + +/* + * svi_lrelative -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position. The offset is for the commands that move logical + * distances, i.e. if it's a logical scroll the closest physical + * distance is based on the logical line, not the physical line. + */ +size_t +svi_lrelative(sp, ep, lno, off) + SCR *sp; + EXF *ep; + recno_t lno; + size_t off; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &len)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (len == 0) + return (0); + + /* Last character is easy, and common. */ + if (sp->rcmflags == RCM_LAST) + return (len - 1); + + /* Discard logical lines. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, p = lp, llen = len; --off;) { + for (; len && scno < sp->cols; --len) + SCNO_INCREMENT; + if (len == 0) + return (llen - 1); + scno -= sp->cols; + } + + /* Step through the line until reach the right character. */ + while (len--) { + SCNO_INCREMENT; + if (scno >= sp->rcm) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == sp->rcm) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} + +/* + * svi_chposition -- + * Return the physical column from the line that will display a + * character closest to the specified column. + */ +size_t +svi_chposition(sp, ep, lno, cno) + SCR *sp; + EXF *ep; + recno_t lno; + size_t cno; +{ + CHNAME const *cname; + size_t len, llen, scno; + int ch, listset; + char *lp, *p; + + /* Need the line to go any further. */ + if ((lp = file_gline(sp, ep, lno, &llen)) == NULL) + return (0); + + /* Empty lines are easy. */ + if (llen == 0) + return (0); + + /* Step through the line until reach the right character. */ + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + for (scno = 0, len = llen, p = lp; len--;) { + SCNO_INCREMENT; + if (scno >= cno) { + /* Get the offset of this character. */ + len = p - lp; + + /* + * May be the next character, not this one, + * so check to see if we've gone too far. + */ + if (scno == cno) + return (len < llen - 1 ? len : llen - 1); + /* It's this character. */ + return (len - 1); + } + } + /* No such character; return start of last character. */ + return (llen - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c new file mode 100644 index 000000000000..16e2c36d8609 --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_screen.c 8.57 (Berkeley) 1/9/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" +#include "sex/sex_screen.h" + +static void d_to_h __P((SCR *, char *)); +static int svi_initscr_kluge __P((SCR *, struct termios *)); +static void svi_keypad __P((SCR *, int)); +static void svi_keypad_pc __P((int)); + +/* + * svi_screen_init -- + * Initialize a screen. + */ +int +svi_screen_init(sp) + SCR *sp; +{ + /* Initialize support routines. */ + sp->s_bell = svi_bell; + sp->s_bg = svi_bg; + sp->s_busy = svi_busy; + sp->s_change = svi_change; + sp->s_chposition = svi_chposition; + sp->s_clear = svi_clear; + sp->s_column = svi_column; + sp->s_confirm = svi_confirm; + sp->s_down = svi_sm_down; + sp->s_edit = svi_screen_edit; + sp->s_end = svi_screen_end; + sp->s_ex_cmd = svi_ex_cmd; + sp->s_ex_run = svi_ex_run; + sp->s_ex_write = svi_ex_write; + sp->s_fg = svi_fg; + sp->s_fill = svi_sm_fill; + sp->s_get = svi_get; + sp->s_key_read = sex_key_read; + sp->s_optchange = svi_optchange; + sp->s_position = svi_sm_position; + sp->s_rabs = svi_rabs; + sp->s_refresh = svi_refresh; + sp->s_relative = svi_relative; + sp->s_rrel = svi_rrel; + sp->s_split = svi_split; + sp->s_suspend = svi_suspend; + sp->s_up = svi_sm_up; + + return (0); +} + +/* + * svi_screen_copy -- + * Copy to a new screen. + */ +int +svi_screen_copy(orig, sp) + SCR *orig, *sp; +{ + SVI_PRIVATE *osvi, *nsvi; + + /* Create the private screen structure. */ + CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE)); + sp->svi_private = nsvi; + +/* INITIALIZED AT SCREEN CREATE. */ + /* Lose svi_screens() cached information. */ + nsvi->ss_lno = OOBLNO; + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + if (orig == NULL) { + } else { + osvi = SVP(orig); + nsvi->srows = osvi->srows; + if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + + F_SET(nsvi, F_ISSET(osvi, SVI_NO_VBELL)); + } + return (0); +} + +/* + * svi_screen_end -- + * End a screen. + */ +int +svi_screen_end(sp) + SCR *sp; +{ + int rval; + + rval = 0; + + /* + * XXX + * If this is the last screen, end curses + * while we still have screen information. + */ + if (sp->q.cqe_prev == (void *)&sp->gp->dq && + sp->q.cqe_next == (void *)&sp->gp->dq && + sp->gp->hq.cqh_first == (void *)&sp->gp->hq && + svi_curses_end(sp)) + rval = 1; + + /* Free screen map. */ + if (HMAP != NULL) + FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP)); + + /* Free visual bell string. */ + if (SVP(sp)->VB != NULL) + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + + /* Free private memory. */ + FREE(SVP(sp), sizeof(SVI_PRIVATE)); + sp->svi_private = NULL; + + return (rval); +} + +/* + * We use a single curses "window" for each vi screen. The model would be + * simpler with two windows (one for the text, and one for the modeline) + * because scrolling the text window down would work correctly then, not + * affecting the mode line. As it is we have to play games to make it look + * right. The reason for this choice is that it would be difficult for + * curses to optimize the movement, i.e. detect that the downward scroll + * isn't going to change the modeline, set the scrolling region on the + * terminal and only scroll the first part of the text window. (Even if + * curses did detect it, the set-scrolling-region terminal commands can't + * be used by curses because it's indeterminate where the cursor ends up + * after they are sent.) + */ +/* + * svi_screen_edit -- + * Main vi curses screen loop. + */ +int +svi_screen_edit(sp, ep) + SCR *sp; + EXF *ep; +{ + SCR *tsp; + int force; + + /* Get refresh to init curses. */ + F_SET(sp, S_RESIZE); + + for (;;) { + /* Reset the cursor. */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * Run vi. If vi fails, svi data structures may be + * corrupted, be extremely careful what you free up. + */ + if (vi(sp, sp->ep)) { + if (F_ISSET(ep, F_RCV_ON)) { + F_SET(ep, F_RCV_NORM); + (void)rcv_sync(sp, sp->ep); + } + (void)file_end(sp, sp->ep, 1); /* End the file. */ + (void)svi_curses_end(sp); /* End curses. */ + (void)screen_end(sp); /* End the screen. */ + return (1); + } + + force = 0; + switch (F_ISSET(sp, S_MAJOR_CHANGE)) { + case S_EXIT_FORCE: + force = 1; + /* FALLTHROUGH */ + case S_EXIT: + F_CLR(sp, S_EXIT_FORCE | S_EXIT); + if (file_end(sp, sp->ep, force))/* End the file. */ + break; + (void)svi_join(sp, &tsp); /* Find a new screen. */ + if (tsp == NULL) + (void)svi_swap(sp, &tsp, NULL); + (void)screen_end(sp); /* End the screen. */ + if ((sp = tsp) == NULL) /* Switch. */ + return (0); + break; + case 0: /* Exit vi mode. */ + (void)svi_curses_end(sp); /* End curses. */ + d_to_h(sp, "Exit from vi"); + return (0); + case S_FSWITCH: /* File switch. */ + F_CLR(sp, S_FSWITCH); + F_SET(sp, S_REFORMAT); + break; + case S_SSWITCH: /* Screen switch. */ + F_CLR(sp, S_SSWITCH); + sp = sp->nextdisp; + break; + default: + abort(); + } + } + /* NOTREACHED */ +} + +/* + * svi_curses_init -- + * Initialize curses. + */ +int +svi_curses_init(sp) + SCR *sp; +{ + struct termios t; + int ixoff, ixon; + char *p, kbuf[2048]; + + /* + * Vi wants raw mode, excepting flow control characters. Both + * cbreak and raw modes have problems for us. In cbreak mode, + * we have to turn all the signals off explicitly. In raw mode, + * we have to turn flow control back on. Raw chosen for no strong + * reason. In both cases we have to periodically turn various + * signals on. + */ + if (tcgetattr(STDIN_FILENO, &t)) { + msgq(sp, M_SYSERR, "tcgetattr"); + return (1); + } + ixon = t.c_iflag & IXON; + ixoff = t.c_iflag & IXOFF; + + /* + * The initscr() in SunOS curses flushes the terminal driver queue. + * I have no idea if this stark raving insanity appears elsewhere, + * but since the SunOS curses is likely derived from the System III + * or System V versions, here's the workaround. + */ + if (svi_initscr_kluge(sp, &t)) + return (1); + + /* + * Start the screen. Initscr() doesn't provide useful error values + * or messages. Generally, either malloc failed or the terminal + * was unknown or lacked some necesssary feature. Try and guess so + * the user isn't even more pissed off because of the error message. + */ + errno = 0; + if (initscr() == NULL) { + if (errno) + msgq(sp, M_SYSERR, "initscr failed"); + else + msgq(sp, M_ERR, "Error: initscr failed."); + if ((p = getenv("TERM")) == NULL) + msgq(sp, M_ERR, + "Error: no terminal environment variable set."); + else if (tgetent(kbuf, p) != 1) + msgq(sp, M_ERR, +"Error: %s: unknown terminal type, or terminal lacking necessary features.", p); + else + msgq(sp, M_ERR, + "Error: %s: terminal type lacking necessary features.", p); + return (1); + } + + /* + * !!! + * If raw isn't turning off echo and newlines, something's wrong. + * However, just in case... + */ + noecho(); /* No character echo. */ + nonl(); /* No CR/NL translation. */ + raw(); /* No special characters. */ + idlok(stdscr, 1); /* Use hardware insert/delete line. */ + + /* + * Vi wants the cursor keys in application mode. The historic version + * of curses had no way to do this, and the newer versions (System V) + * only enable it through the wgetch() interface. Do it roughly, here. + */ + svi_keypad(sp, 1); + + /* + * XXX + * Major compatibility kluge. When we call the curses raw() routine, + * XON/XOFF flow control is turned off. Old terminals like to have + * it, so if it's originally set for the tty, we turn it back on. For + * some unknown reason, this causes System V curses to NOT correctly + * restore the terminal modes when SIGTSTP is received. + */ + if ((ixon || ixoff) && + !tcgetattr(STDIN_FILENO, &sp->gp->s5_curses_botch)) { + t = sp->gp->s5_curses_botch; + if (ixon) + t.c_iflag |= IXON; + if (ixoff) + t.c_iflag |= IXOFF; + F_SET(sp->gp, G_CURSES_S5CB); + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + } + + /* + * The first screen in the list gets it all. All other screens + * are hidden and lose their maps. + */ + d_to_h(sp, "Window resize"); + + /* Initialize terminal values. */ + SVP(sp)->srows = O_VAL(sp, O_LINES); + + /* + * Initialize screen values. + * + * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * Setup: + * t_minrows is the minimum rows to display + * t_maxrows is the maximum rows to display (rows - 1) + * t_rows is the rows currently being displayed + */ + sp->rows = SVP(sp)->srows; + sp->cols = O_VAL(sp, O_COLUMNS); + sp->woff = 0; + sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > sp->rows - 1) { + sp->t_minrows = sp->t_rows = sp->rows - 1; + msgq(sp, M_INFO, + "Windows option value is too large, max is %u", sp->t_rows); + } + sp->t_maxrows = sp->rows - 1; + + /* Create the screen map. */ + if ((HMAP = malloc(SIZE_HMAP(sp) * sizeof(SMAP))) == NULL) { + msgq(sp, M_SYSERR, NULL); + return (1); + } + memset(HMAP, 0, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + F_SET(sp->gp, G_CURSES_INIT); /* Curses initialized. */ + F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */ + return (0); +} + +/* + * svi_rrel -- + * Change the relative size of the current screen. + */ +int +svi_rrel(sp, count) + SCR *sp; + long count; +{ + /* Can't grow beyond the size of the window. */ + if (count > O_VAL(sp, O_WINDOW)) + count = O_VAL(sp, O_WINDOW); + + sp->t_minrows = sp->t_rows = count; + if (sp->t_rows > sp->rows - 1) + sp->t_minrows = sp->t_rows = sp->rows - 1; + TMAP = HMAP + (sp->t_rows - 1); + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_curses_end -- + * Move to the bottom of the screen, end curses. + */ +int +svi_curses_end(sp) + SCR *sp; +{ + /* We get called before anything has been initialized. */ + if (!F_ISSET(sp->gp, G_CURSES_INIT)) + return (0); + F_CLR(sp->gp, G_CURSES_INIT); + + /* Move to the bottom of the screen. */ + if (move(INFOLINE(sp), 0) == OK) { + clrtoeol(); + refresh(); + } + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + F_CLR(sp->gp, G_CURSES_S5CB); + + svi_keypad(sp, 0); + endwin(); + return (0); +} + +/* + * svi_keypad -- + * Put the keypad/cursor arrows into or out of application mode. + */ +static void +svi_keypad(sp, on) + SCR *sp; + int on; +{ + char *sbp, *t, kbuf[2048], sbuf[128]; + + if (tgetent(kbuf, O_STR(sp, O_TERM)) != 1) + return; + sbp = sbuf; + if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL) + return; + (void)tputs(t, 0, svi_keypad_pc); +} + +/* + * svi_keypad_pc -- + * putchar routine for tputs(). + */ +static void +svi_keypad_pc(argch) + int argch; +{ + char ch; + + ch = argch; + (void)write(STDOUT_FILENO, &ch, sizeof(ch)); +} + +/* + * svi_initscr_kluge -- + * Read all of the waiting keys before calling initscr(). + */ +static int +svi_initscr_kluge(sp, tp) + SCR *sp; + struct termios *tp; +{ + struct termios rawt; + int rval; + + /* + * Turn off canonical input processing so that we get partial + * lines as well as complete ones. Also, set the MIN/TIME + * values -- System V and SMI systems overload VMIN and VTIME, + * such that VMIN is the same as the VEOF element, and VTIME is + * the same as the VEOL element. This means, that if VEOF was + * ^D, the default VMIN is 4. Majorly stupid. + */ + rawt = *tp; + rawt.c_cc[VMIN] = 1; + rawt.c_cc[VTIME] = 0; + rawt.c_lflag &= ~ICANON; + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, &rawt)) + return (1); + rval = term_key_queue(sp); + if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSANOW, tp) || rval) + return (1); + return (0); +} + +/* + * d_to_h -- + * Move all but the current screen to the hidden queue. + */ +static void +d_to_h(sp, emsg) + SCR *sp; + char *emsg; +{ + SCR *tsp; + int hidden; + + for (hidden = 0; + (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) { + if (_HMAP(tsp) != NULL) + FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP)); + CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q); + } + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); + if (hidden > 1) + msgq(sp, M_INFO, + "%s backgrounded %d screens; use :display to list the screens.", + emsg, hidden - 1); +} diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h new file mode 100644 index 000000000000..4bf754473df9 --- /dev/null +++ b/usr.bin/vi/svi/svi_screen.h @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)svi_screen.h 8.30 (Berkeley) 12/22/93 + */ + +/* + * Structure for mapping lines to the screen. An SMAP is an array, with one + * structure element per screen line, which holds information describing the + * physical line which is displayed in the screen line. The first two fields + * (lno and off) are all that are necessary to describe a line. The rest of + * the information is useful to keep information from being re-calculated. + * + * Lno is the line number. Off is the screen offset into the line. For + * example, the pair 2:1 would be the first screen of line 2, and 2:2 would + * be the second. If doing left-right scrolling, all of the offsets will be + * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc. If doing the + * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1, + * etc. + * + * The SMAP is always as large as the physical screen, plus a slot for the + * info line, so that there is room to add any screen into another one at + * screen exit. + */ +typedef struct _smap { + recno_t lno; /* 1-N: Physical file line number. */ + size_t off; /* 1-N: Screen offset in the line. */ + + /* svi_line() cache information. */ + size_t c_sboff; /* 0-N: offset of first character byte. */ + size_t c_eboff; /* 0-N: offset of last character byte. */ + u_char c_scoff; /* 0-N: offset into the first character. */ + u_char c_eclen; /* 1-N: columns from the last character. */ + u_char c_ecsize; /* 1-N: size of the last character. */ +} SMAP; + + /* Macros to flush/test cached information. */ +#define SMAP_CACHE(smp) ((smp)->c_ecsize != 0) +#define SMAP_FLUSH(smp) ((smp)->c_ecsize = 0) + +typedef struct _svi_private { +/* INITIALIZED AT SCREEN CREATE. */ + SMAP *h_smap; /* First slot of the line map. */ + SMAP *t_smap; /* Last slot of the line map. */ + + size_t exlinecount; /* Ex overwrite count. */ + size_t extotalcount; /* Ex overwrite count. */ + size_t exlcontinue; /* Ex line continue value. */ + + /* svi_screens() cache information. */ + recno_t ss_lno; /* 1-N: Line number. */ + size_t ss_screens; /* Return value. */ + + recno_t olno; /* 1-N: old cursor file line. */ + size_t ocno; /* 0-N: old file cursor column. */ + size_t sc_col; /* 0-N: LOGICAL screen column. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + size_t srows; /* 1-N: Rows in the terminal/window. */ + + char *VB; /* Visual bell termcap string. */ + +#define SVI_CUR_INVALID 0x001 /* Cursor position is unknown. */ +#define SVI_DIVIDER 0x002 /* Screen divider is displayed. */ +#define SVI_INFOLINE 0x004 /* The infoline is being used by v_ntext(). */ +#define SVI_NO_VBELL 0x008 /* No visual bell available. */ +#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */ + u_int flags; +} SVI_PRIVATE; + +#define SVP(sp) ((SVI_PRIVATE *)((sp)->svi_private)) +#define HMAP (SVP(sp)->h_smap) +#define TMAP (SVP(sp)->t_smap) +#define _HMAP(sp) (SVP(sp)->h_smap) +#define _TMAP(sp) (SVP(sp)->t_smap) + +/* + * One extra slot is always allocated for the map so that we can use + * it to do vi :colon command input; see svi_get(). + */ +#define SIZE_HMAP(sp) (SVP(sp)->srows + 1) + +#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */ +#define O_NUMBER_LENGTH 8 + /* Columns on a screen. */ +#define SCREEN_COLS(sp) \ + ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols)) + +#define HALFSCREEN(sp) ((sp)->t_maxrows / 2) /* Half the screen. */ +#define HALFTEXT(sp) ((sp)->t_rows / 2) /* Half the text. */ + +#define INFOLINE(sp) ((sp)->t_maxrows) /* Info line test, offset. */ +#define ISINFOLINE(sp, smp) (((smp) - HMAP) == INFOLINE(sp)) + + /* Small screen test. */ +#define ISSMALLSCREEN(sp) ((sp)->t_minrows != (sp)->t_maxrows) + + /* Next tab offset. */ +#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP)) + + +#define SCNO_INCREMENT /* Step through line. */\ + scno += (ch = *(u_char *)p++) == '\t' && !listset ? \ + TAB_OFF(sp, scno) : cname[ch].len + +/* Move in a screen (absolute), and fail if it doesn't work. */ +#define MOVEA(sp, lno, cno) { \ + if (move(lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), abs.", \ + tail(__FILE__), __LINE__, lno, cno); \ + return (1); \ + } \ +} + +/* Move in a window, and fail if it doesn't work. */ +#define MOVE(sp, lno, cno) { \ + size_t __lno = (sp)->woff + (lno); \ + if (move(__lno, cno) == ERR) { \ + msgq(sp, M_ERR, \ + "Error: %s/%d: move:l(%u), c(%u), o(%u).", \ + tail(__FILE__), __LINE__, lno, cno, sp->woff); \ + return (1); \ + } \ +} + +/* Add a character. */ +#define ADDCH(ch) { \ + int __ch = (ch); \ + ADDNSTR(cname[__ch].name, cname[__ch].len); \ +} + +/* Add a string len bytes long. */ +#define ADDNSTR(str, len) { \ + if (addnstr(str, len) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Add a string. */ +#define ADDSTR(str) { \ + if (addstr(str) == ERR) { \ + int __x, __y; \ + getyx(stdscr, __y, __x); \ + msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u).", \ + tail(__FILE__), __LINE__, __y, __x); \ + return (1); \ + } \ +} + +/* Public routines. */ +void svi_bell __P((SCR *)); +int svi_bg __P((SCR *)); +int svi_busy __P((SCR *, char const *)); +int svi_change __P((SCR *, EXF *, recno_t, enum operation)); +size_t svi_chposition __P((SCR *, EXF *, recno_t, size_t)); +int svi_column __P((SCR *, EXF *, size_t *)); +enum confirm + svi_confirm __P((SCR *, EXF *, MARK *, MARK *)); +int svi_clear __P((SCR *)); +int svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *)); +int svi_ex_run __P((SCR *, EXF *, MARK *)); +int svi_ex_write __P((void *, const char *, int)); +int svi_fg __P((SCR *, CHAR_T *)); +enum input + svi_get __P((SCR *, EXF *, TEXTH *, int, u_int)); +int svi_optchange __P((SCR *, int)); +int svi_rabs __P((SCR *, long)); +int svi_refresh __P((SCR *, EXF *)); +size_t svi_relative __P((SCR *, EXF *, recno_t)); +int svi_rrel __P((SCR *, long)); +int svi_screen_copy __P((SCR *, SCR *)); +int svi_screen_edit __P((SCR *, EXF *)); +int svi_screen_end __P((SCR *)); +int svi_sm_down __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_sm_fill __P((SCR *, EXF *, recno_t, enum position)); +int svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position)); +int svi_sm_up __P((SCR *, EXF *, MARK *, recno_t, int)); +int svi_split __P((SCR *, ARGS *[])); +int svi_suspend __P((SCR *)); +int svi_swap __P((SCR *, SCR **, char *)); + +/* Private routines. */ +int svi_curses_end __P((SCR *)); +int svi_curses_init __P((SCR *)); +int svi_divider __P((SCR *)); +int svi_init __P((SCR *)); +int svi_join __P((SCR *, SCR **)); +int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *)); +size_t svi_lrelative __P((SCR *, EXF *, recno_t, size_t)); +size_t svi_ncols __P((SCR *, u_char *, size_t, size_t *)); +int svi_number __P((SCR *, EXF *)); +int svi_paint __P((SCR *, EXF *)); +size_t svi_screens __P((SCR *, EXF *, recno_t, size_t *)); +int svi_sm_1down __P((SCR *, EXF *)); +int svi_sm_1up __P((SCR *, EXF *)); +int svi_sm_cursor __P((SCR *, EXF *, SMAP **)); +int svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *)); +recno_t svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t)); +int svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *)); + +/* Private debugging routines. */ +#ifdef DEBUG +int svi_gdbrefresh __P((void)); +#endif diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c new file mode 100644 index 000000000000..5bcb6e220f3d --- /dev/null +++ b/usr.bin/vi/svi/svi_smap.c @@ -0,0 +1,1041 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_smap.c 8.29 (Berkeley) 11/30/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "vcmd.h" +#include "svi_screen.h" + +static int svi_deleteln __P((SCR *, int)); +static int svi_insertln __P((SCR *, int)); +static int svi_sm_delete __P((SCR *, EXF *, recno_t)); +static int svi_sm_insert __P((SCR *, EXF *, recno_t)); +static int svi_sm_reset __P((SCR *, EXF *, recno_t)); + +/* + * svi_change -- + * Make a change to the screen. + */ +int +svi_change(sp, ep, lno, op) + SCR *sp; + EXF *ep; + recno_t lno; + enum operation op; +{ + SMAP *p; + size_t oldy, oldx; + + /* Appending is the same as inserting, if the line is incremented. */ + if (op == LINE_APPEND) { + ++lno; + op = LINE_INSERT; + } + + /* Ignore the change if the line is after the map. */ + if (lno > TMAP->lno) + return (0); + + /* + * If the line is before the map, and it's a decrement, decrement + * the map. If it's an increment, increment the map. Otherwise, + * ignore it. + */ + if (lno < HMAP->lno) { + switch (op) { + case LINE_APPEND: + abort(); + /* NOTREACHED */ + case LINE_DELETE: + for (p = HMAP; p <= TMAP; ++p) + --p->lno; + if (sp->lno >= lno) + --sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + for (p = HMAP; p <= TMAP; ++p) + ++p->lno; + if (sp->lno >= lno) + ++sp->lno; + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + break; + } + return (0); + } + + F_SET(SVP(sp), SVI_SCREENDIRTY); + + /* Flush cached information from svi_screens(). */ + SVP(sp)->ss_lno = OOBLNO; + + /* Invalidate the cursor, if it's on this line. */ + if (sp->lno == lno) + F_SET(SVP(sp), SVI_CUR_INVALID); + + getyx(stdscr, oldy, oldx); + + switch (op) { + case LINE_DELETE: + if (svi_sm_delete(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_INSERT: + if (svi_sm_insert(sp, ep, lno)) + return (1); + F_SET(sp, S_RENUMBER); + break; + case LINE_RESET: + if (svi_sm_reset(sp, ep, lno)) + return (1); + break; + default: + abort(); + } + + MOVEA(sp, oldy, oldx); + + return (0); +} + +/* + * svi_sm_fill -- + * Fill in the screen map, placing the specified line at the + * right position. There isn't any way to tell if an SMAP + * entry has been filled in, so this routine had better be + * called with P_FILL set before anything else is done. + * + * !!! + * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP + * slot is already filled in, P_BOTTOM means that the TMAP slot is + * already filled in, and we just finish up the job. + */ +int +svi_sm_fill(sp, ep, lno, pos) + SCR *sp; + EXF *ep; + recno_t lno; + enum position pos; +{ + SMAP *p, tmp; + + /* Flush all cached information from the SMAP. */ + for (p = HMAP; p <= TMAP; ++p) + SMAP_FLUSH(p); + + switch (pos) { + case P_FILL: + tmp.lno = 1; + tmp.off = 1; + + /* See if less than half a screen from the top. */ + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + lno = 1; + goto top; + } + + /* See if less than half a screen from the bottom. */ + if (file_lline(sp, ep, &tmp.lno)) + return (1); + tmp.off = svi_screens(sp, ep, tmp.lno, NULL); + if (svi_sm_nlines(sp, ep, + &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { + TMAP->lno = tmp.lno; + TMAP->off = tmp.off; + goto bottom; + } + goto middle; + case P_TOP: + if (lno != OOBLNO) { +top: HMAP->lno = lno; + HMAP->off = 1; + } + /* If we fail, just punt. */ + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_MIDDLE: + /* If we fail, guess that the file is too small. */ +middle: p = HMAP + (TMAP - HMAP) / 2; + for (p->lno = lno, p->off = 1; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + + /* If we fail, just punt. */ + p = HMAP + (TMAP - HMAP) / 2; + for (; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + goto err; + break; + case P_BOTTOM: + if (lno != OOBLNO) { + TMAP->lno = lno; + TMAP->off = svi_screens(sp, ep, lno, NULL); + } + /* If we fail, guess that the file is too small. */ +bottom: for (p = TMAP; p > HMAP; --p) + if (svi_sm_prev(sp, ep, p, p - 1)) { + lno = 1; + goto top; + } + break; + } + return (0); + + /* + * Try and put *something* on the screen. If this fails, + * we have a serious hard error. + */ +err: HMAP->lno = 1; + HMAP->off = 1; + for (p = HMAP; p < TMAP; ++p) + if (svi_sm_next(sp, ep, p, p + 1)) + return (1); + return (0); +} + +/* + * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the + * screen only contains one line, or, if the line is the entire screen, this + * gets fairly exciting. Skip the fun and simply return if there's only one + * line in the screen, or just call fill. Fill may not be entirely accurate, + * i.e. we may be painting the screen with something not even close to the + * cursor, but it's not like we're into serious performance issues here, and + * the refresh routine will fix it for us. + */ +#define TOO_WEIRD { \ + if (cnt_orig >= sp->t_rows) { \ + if (cnt_orig == 1) \ + return (0); \ + if (file_gline(sp, ep, lno, NULL) == NULL) \ + if (file_lline(sp, ep, &lno)) \ + return (1); \ + F_SET(sp, S_REDRAW); \ + return (svi_sm_fill(sp, ep, lno, P_TOP)); \ + } \ +} + +/* + * svi_sm_delete -- + * Delete a line out of the SMAP. + */ +static int +svi_sm_delete(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig; + + /* + * Find the line in the map, and count the number of screen lines + * which display any part of the deleted line. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 1, t = p + 1; + t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + + TOO_WEIRD; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, cnt_orig)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Decrement the line numbers for the rest of the map. */ + for (t = TMAP - cnt_orig; p <= t; ++p) + --p->lno; + + /* Display the new lines. */ + for (p = TMAP - cnt_orig;;) { + if (p < TMAP && svi_sm_next(sp, ep, p, p + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++p, NULL, NULL)) + return (1); + if (p == TMAP) + break; + } + return (0); +} + +/* + * svi_sm_insert -- + * Insert a line into the SMAP. + */ +static int +svi_sm_insert(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt; + + /* + * Find the line in the map, find out how many screen lines + * needed to display the line. + */ + for (p = HMAP; p->lno != lno; ++p); + cnt_orig = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (cnt_orig > cnt) + cnt_orig = cnt; + + /* Push down that many lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, cnt_orig)) + return (1); + + /* Shift the screen map down. */ + memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); + + /* Increment the line numbers for the rest of the map. */ + for (t = p + cnt_orig; t <= TMAP; ++t) + ++t->lno; + + /* Fill in the SMAP for the new lines, and display. */ + for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + return (0); +} + +/* + * svi_sm_reset -- + * Reset a line in the SMAP. + */ +static int +svi_sm_reset(sp, ep, lno) + SCR *sp; + EXF *ep; + recno_t lno; +{ + SMAP *p, *t; + size_t cnt_orig, cnt_new, cnt, diff; + + /* + * See if the number of on-screen rows taken up by the old display + * for the line is the same as the number needed for the new one. + * If so, repaint, otherwise do it the hard way. + */ + for (p = HMAP; p->lno != lno; ++p); + for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); + cnt_new = svi_screens(sp, ep, lno, NULL); + + TOO_WEIRD; + + if (cnt_orig == cnt_new) { + do { + SMAP_FLUSH(p); + if (svi_line(sp, ep, p, NULL, NULL)) + return (1); + } while (++p < t); + return (0); + } + + if (cnt_orig < cnt_new) { + /* Get the difference. */ + diff = cnt_new - cnt_orig; + + /* + * The lines left in the screen override the number of screen + * lines in the inserted line. + */ + cnt = (TMAP - p) + 1; + if (diff > cnt) + diff = cnt; + + /* Push down the extra lines. */ + MOVE(sp, p - HMAP, 0); + if (svi_insertln(sp, diff)) + return (1); + + /* Shift the screen map down. */ + memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + } else { + /* Get the difference. */ + diff = cnt_orig - cnt_new; + + /* Delete that many lines from the screen. */ + MOVE(sp, p - HMAP, 0); + if (svi_deleteln(sp, diff)) + return (1); + + /* Shift the screen map up. */ + memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); + + /* Fill in the SMAP for the replaced line, and display. */ + for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { + t->lno = lno; + t->off = cnt; + SMAP_FLUSH(t); + if (svi_line(sp, ep, t, NULL, NULL)) + return (1); + } + + /* Display the new lines at the bottom of the screen. */ + for (t = TMAP - diff;;) { + if (t < TMAP && svi_sm_next(sp, ep, t, t + 1)) + return (1); + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, ++t, NULL, NULL)) + return (1); + if (t == TMAP) + break; + } + } + return (0); +} + +/* + * svi_sm_up -- + * Scroll the SMAP up count logical lines. + */ +int +svi_sm_up(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap, tmp; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* + * Check to see if movement is possible. Lots of checks... + * + * Find out if it's possible to move past the end of the map. If + * that's okay because we think that we can move the cursor down + * in the map, check to make sure that the map isn't mostly empty. + */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (tmp.lno > TMAP->lno && + !file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + if (!cursor_move || ignore_cursor || p == TMAP) { + v_eof(sp, ep, NULL); + return (1); + } + if (svi_sm_next(sp, ep, p, &tmp)) + return (1); + if (!file_gline(sp, ep, tmp.lno, NULL) || + tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) { + v_eof(sp, ep, NULL); + return (1); + } + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows; + --count, ++sp->t_rows) { + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + if (TMAP->lno != tmp.lno && + !file_gline(sp, ep, tmp.lno, NULL)) + break; + *++TMAP = tmp; + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* Decide what would show up on the screen. */ + if (svi_sm_next(sp, ep, TMAP, &tmp)) + return (1); + + /* If the line doesn't exist, we're done. */ + if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL)) + break; + + /* Scroll the screen cursor up one logical line. */ + if (svi_sm_1up(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p > HMAP) + --p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* + * If we didn't move enough, head toward EOF. Check to make + * sure the lines actually, if the file is smaller than the + * screen they may not. + */ + for (; count; --count, ++p) + if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL)) + break; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_eof(sp, ep, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the top. */ + if (sp->lno < HMAP->lno) + p = HMAP; + } + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1up -- + * Scroll the SMAP up one. + */ +int +svi_sm_1up(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Delete the top line of the screen. Shift the screen map up. + * Display a new line at the bottom of the screen. + */ + MOVE(sp, 0, 0); + if (svi_deleteln(sp, 1)) + return (1); + + /* One-line screens can fail. */ + if (HMAP == TMAP) { + if (svi_sm_next(sp, ep, TMAP, TMAP)) + return (1); + } else { + memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_next(sp, ep, TMAP - 1, TMAP)) + return (1); + } + /* svi_sm_next() flushed the cache. */ + if (svi_line(sp, ep, TMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_deleteln -- + * Delete a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_deleteln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + deleteln(); + MOVE(sp, INFOLINE(sp) - 1, 0); + insertln(); + MOVEA(sp, oldy, oldx); + } + return (0); +} + +/* + * svi_sm_down -- + * Scroll the SMAP down count logical lines. + */ +int +svi_sm_down(sp, ep, rp, count, cursor_move) + SCR *sp; + EXF *ep; + MARK *rp; + recno_t count; + int cursor_move; +{ + SMAP *p, svmap; + int ignore_cursor; + + /* Set the default return position. */ + rp->lno = sp->lno; + rp->cno = sp->cno; + + /* + * Invalidate the cursor. The line is probably going to change, + * but if cursor_move isn't set it may not. In any case, this + * routine moves the cursor to draw things. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * There are two forms of this command, one where the cursor tries to + * follow the line, and one where it doesn't. In the latter, we try + * and keep the cursor at the same position on the screen, but, if the + * screen is small enough and the line length large enough, the cursor + * can end up in very strange places. Probably not worth fixing. + * + * Find the line in the SMAP -- ignore the cursor if it wasn't on the + * screen. + */ + if (svi_sm_cursor(sp, ep, &p)) + return (1); + if (p == NULL) + ignore_cursor = 1; + else { + svmap = *p; + ignore_cursor = 0; + } + + /* Check to see if movement is possible. */ + if (HMAP->lno == 1 && HMAP->off == 1 && + (!cursor_move || ignore_cursor || p == HMAP)) { + v_sof(sp, NULL); + return (1); + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * If it's a small screen, and the movement is small, open up the + * screen. Otherwise, compress and repaint. If we compress, we + * ignore the cursor, the movement is too large to care. + */ + if (ISSMALLSCREEN(sp)) + if (count <= HALFTEXT(sp)) { + for (; count && sp->t_rows != sp->t_maxrows && + (HMAP->lno > 1 || HMAP->off > 1); + --count, ++sp->t_rows) { + ++TMAP; + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move) + ++p; + } + if (count == 0) + return (0); + } else { + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + for (; + sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) { + MOVE(sp, TMAP - HMAP, 0); + clrtoeol(); + } + ignore_cursor = 1; + } + + for (; count; --count) { + /* If the line doesn't exist, we're done. */ + if (HMAP->lno == 1 && HMAP->off == 1) + break; + + /* Scroll the screen and cursor down one logical line. */ + if (svi_sm_1down(sp, ep)) + return (1); + if (!cursor_move && !ignore_cursor && p < TMAP) + ++p; + } + + /* If ignoring the cursor, we're done. */ + if (ignore_cursor) + return (0); + + if (cursor_move) { + /* If we didn't move enough, move to SOF. */ + if (count) + p = HMAP; + } else { + /* + * If the line itself moved, invalidate the cursor, because + * the comparison with the old line/new line won't be right. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* If didn't move enough, it's an error. */ + if (count) { + v_sof(sp, NULL); + return (1); + } + + /* If the cursor moved off the screen, move it to the bottom. */ + if (sp->lno > TMAP->lno) + p = TMAP; + } + + /* + * On a logical movement, we try and keep the cursor as close as + * possible to the last position, but also set it up so that the + * next "real" movement will return the cursor to the closest position + * to the last real movement. + */ + if (p->lno != svmap.lno || p->off != svmap.off) { + rp->lno = p->lno; + rp->cno = svi_lrelative(sp, ep, p->lno, p->off); + } + return (0); +} + +/* + * svi_sm_1down -- + * Scroll the SMAP down one. + */ +int +svi_sm_1down(sp, ep) + SCR *sp; + EXF *ep; +{ + /* + * Clear the bottom line of the screen, insert a line at the top + * of the screen. Shift the screen map down, display a new line + * at the top of the screen. + */ + MOVE(sp, sp->t_rows, 0); + clrtoeol(); + MOVE(sp, 0, 0); + if (svi_insertln(sp, 1)) + return (1); + memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP)); + if (svi_sm_prev(sp, ep, HMAP + 1, HMAP)) + return (1); + /* svi_sm_prev() flushed the cache. */ + if (svi_line(sp, ep, HMAP, NULL, NULL)) + return (1); + return (0); +} + +/* + * svi_insertln -- + * Insert a line a la curses, make sure to put the information + * line and other screens back. + */ +static int +svi_insertln(sp, cnt) + SCR *sp; + int cnt; +{ + size_t oldy, oldx; + + getyx(stdscr, oldy, oldx); + while (cnt--) { + MOVE(sp, INFOLINE(sp) - 1, 0); + deleteln(); + MOVEA(sp, oldy, oldx); + insertln(); + } + return (0); +} + +/* + * svi_sm_next -- + * Fill in the next entry in the SMAP. + */ +int +svi_sm_next(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + size_t lcnt; + + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno + 1; + t->off = p->off; + } else { + lcnt = svi_screens(sp, ep, p->lno, NULL); + if (lcnt == p->off) { + t->lno = p->lno + 1; + t->off = 1; + } else { + t->lno = p->lno; + t->off = p->off + 1; + } + } + return (0); +} + +/* + * svi_sm_prev -- + * Fill in the previous entry in the SMAP. + */ +int +svi_sm_prev(sp, ep, p, t) + SCR *sp; + EXF *ep; + SMAP *p, *t; +{ + SMAP_FLUSH(t); + if (O_ISSET(sp, O_LEFTRIGHT)) { + t->lno = p->lno - 1; + t->off = p->off; + } else if (p->off != 1) { + t->lno = p->lno; + t->off = p->off - 1; + } else { + t->lno = p->lno - 1; + t->off = svi_screens(sp, ep, t->lno, NULL); + } + return (t->lno == 0); +} + +/* + * svi_sm_cursor -- + * Return the SMAP entry referenced by the cursor. + */ +int +svi_sm_cursor(sp, ep, smp) + SCR *sp; + EXF *ep; + SMAP **smp; +{ + SMAP *p; + + /* See if the cursor is not in the map. */ + if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) { + *smp = NULL; + return (0); + } + + /* Find the first occurence of the line. */ + for (p = HMAP; p->lno != sp->lno; ++p); + + /* Fill in the map information until we find the right line. */ + for (; p <= TMAP; ++p) { + /* Short lines are common and easy to detect. */ + if (p != TMAP && (p + 1)->lno != p->lno) { + *smp = p; + return (0); + } + if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL)) + return (1); + if (p->c_eboff >= sp->cno) { + *smp = p; + return (0); + } + } + + /* It was past the end of the map after all. */ + *smp = NULL; + return (0); +} + +/* + * svi_sm_position -- + * Return the line/column of the top, middle or last line on the screen. + * (The vi H, M and L commands.) Here because only the screen routines + * know what's really out there. + */ +int +svi_sm_position(sp, ep, rp, cnt, pos) + SCR *sp; + EXF *ep; + MARK *rp; + u_long cnt; + enum position pos; +{ + SMAP *smp; + recno_t last; + + switch (pos) { + case P_TOP: + if (cnt > TMAP - HMAP) + goto err; + smp = HMAP + cnt; + break; + case P_MIDDLE: + if (cnt > (TMAP - HMAP) / 2) + goto err; + smp = (HMAP + (TMAP - HMAP) / 2) + cnt; + goto eof; + case P_BOTTOM: + if (cnt > TMAP - HMAP) { +err: msgq(sp, M_BERR, "Movement past the end-of-screen."); + return (1); + } + smp = TMAP - cnt; +eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) { + if (file_lline(sp, ep, &last)) + return (1); + for (; smp->lno > last && smp > HMAP; --smp); + } + break; + default: + abort(); + } + + if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL)) + return (1); + rp->lno = smp->lno; + rp->cno = smp->c_sboff; + + return (0); +} + +/* + * svi_sm_nlines -- + * Return the number of screen lines from an SMAP entry to the + * start of some file line, less than a maximum value. + */ +recno_t +svi_sm_nlines(sp, ep, from_sp, to_lno, max) + SCR *sp; + EXF *ep; + SMAP *from_sp; + recno_t to_lno; + size_t max; +{ + recno_t lno, lcnt; + + if (O_ISSET(sp, O_LEFTRIGHT)) + return (from_sp->lno > to_lno ? + from_sp->lno - to_lno : to_lno - from_sp->lno); + + if (from_sp->lno == to_lno) + return (from_sp->off - 1); + + if (from_sp->lno > to_lno) { + lcnt = from_sp->off - 1; /* Correct for off-by-one. */ + for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } else { + lno = from_sp->lno; + lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1; + for (; ++lno < to_lno && lcnt <= max;) + lcnt += svi_screens(sp, ep, lno, NULL); + } + return (lcnt); +} diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c new file mode 100644 index 000000000000..f6be03d38365 --- /dev/null +++ b/usr.bin/vi/svi/svi_split.c @@ -0,0 +1,581 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_split.c 8.29 (Berkeley) 12/22/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "vi.h" +#include "svi_screen.h" + +/* + * svi_split -- + * Split the screen. + */ +int +svi_split(sp, argv) + SCR *sp; + ARGS *argv[]; +{ + MSG *mp, *next; + SCR *tsp, saved_sp; + SVI_PRIVATE saved_svp; + SMAP *smp; + size_t cnt, half; + int issmallscreen, splitup; + + /* Check to see if it's possible. */ + half = sp->rows / 2; + if (half < MINIMUM_SCREEN_ROWS) { + msgq(sp, M_ERR, "Screen must be larger than %d to split", + MINIMUM_SCREEN_ROWS); + return (1); + } + + /* Get a new screen. */ + if (screen_init(sp, &tsp, 0)) + return (1); + MALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + if (_HMAP(tsp) == NULL) + return (1); + + /* + * We're about to modify the current screen. Save the contents + * in case something goes horribly, senselessly wrong. + */ + saved_sp = *sp; + saved_svp = *SVP(sp); + +/* INITIALIZED AT SCREEN CREATE. */ + +/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */ + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * Set a flag so we know to fix the screen up later. + */ + issmallscreen = ISSMALLSCREEN(sp); + + /* + * Split the screen, and link the screens together. If the cursor + * is in the top half of the current screen, the new screen goes + * under the current screen. Else, it goes above the current screen. + * + * The columns in the screen don't change. + */ + tsp->cols = sp->cols; + + cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP; + if (cnt <= half) { /* Parent is top half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff + half; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->t_maxrows = sp->rows - 1; + + splitup = 0; + } else { /* Parent is bottom half. */ + /* Child. */ + tsp->rows = sp->rows - half; + tsp->woff = sp->woff; + tsp->t_maxrows = tsp->rows - 1; + + /* Parent. */ + sp->rows = half; + sp->woff += tsp->rows; + sp->t_maxrows = sp->rows - 1; + + /* Shift the parent's map down. */ + memmove(_HMAP(sp), + _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP)); + + splitup = 1; + } + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The child may have different screen options sizes than the + * parent, so use them. Make sure that the text counts aren't + * larger than the new screen sizes. + */ + if (issmallscreen) { + /* Fix the text line count for the parent. */ + if (splitup) + sp->t_rows -= tsp->rows; + + /* Fix the parent screen. */ + if (sp->t_rows > sp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > sp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + + /* Fix the child screen. */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->t_maxrows) + tsp->t_rows = tsp->t_maxrows; + if (tsp->t_minrows > tsp->t_maxrows) + tsp->t_minrows = tsp->t_maxrows; + + /* + * If we split up, i.e. the child is on top, lines that + * were painted in the parent may not be painted in the + * child. Clear any lines not being used in the child + * screen. + * + */ + if (splitup) + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } else { + sp->t_minrows = sp->t_rows = sp->rows - 1; + + /* + * The new screen may be a small screen, even though the + * parent was not. Don't complain if O_WINDOW is too large, + * we're splitting the screen so the screen is much smaller + * than normal. Clear any lines not being used in the child + * screen. + */ + tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW); + if (tsp->t_rows > tsp->rows - 1) + tsp->t_minrows = tsp->t_rows = tsp->rows - 1; + else + for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) { + MOVE(tsp, cnt, 0) + clrtoeol(); + } + } + + /* Adjust the ends of both maps. */ + _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1); + _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1); + + /* + * In any case, if the size of the scrolling region hasn't been + * modified by the user, reset it so it's reasonable for the split + * screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) { + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + O_VAL(tsp, O_SCROLL) = sp->t_maxrows / 2; + } + + /* + * If files specified, build the file list, else, link to the + * current file. + */ + if (argv == NULL) { + if (file_add(tsp, NULL, FILENAME(sp->frp), 0) == NULL) + goto err; + } else + for (; (*argv)->len != 0; ++argv) + if (file_add(tsp, NULL, (*argv)->bp, 0) == NULL) + goto err; + + /* Set up the argument and current FREF pointers. */ + if ((tsp->frp = file_first(tsp)) == NULL) { + msgq(sp, M_ERR, "No files in the file list."); + goto err; + } + + tsp->a_frp = tsp->frp; + + /* + * Copy the file state flags, start the file. Fill the child's + * screen map. If the file is unchanged, keep the screen and + * cursor the same. + */ + if (argv == NULL) { + tsp->ep = sp->ep; + ++sp->ep->refcnt; + + tsp->frp->flags = sp->frp->flags; + tsp->frp->lno = sp->lno; + tsp->frp->cno = sp->cno; + F_SET(tsp->frp, FR_CURSORSET); + + /* Copy the parent's map into the child's map. */ + memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP)); + } else { + if (file_init(tsp, tsp->frp, NULL, 0)) + goto err; + (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP); + } + + /* Everything's initialized, put the screen on the displayed queue.*/ + if (splitup) { + /* Link in before the parent. */ + CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q); + } else { + /* Link in after the parent. */ + CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q); + } + + /* Clear the current information lines in both screens. */ + MOVE(sp, INFOLINE(sp), 0); + clrtoeol(); + MOVE(tsp, INFOLINE(tsp), 0); + clrtoeol(); + + /* Redraw the status line for the parent screen. */ + (void)status(sp, sp->ep, sp->lno, 0); + + /* Save the parent screen's cursor information. */ + sp->frp->lno = sp->lno; + sp->frp->cno = sp->cno; + F_SET(sp->frp, FR_CURSORSET); + + /* Completely redraw the child screen. */ + F_SET(tsp, S_REDRAW); + + /* Switch screens. */ + sp->nextdisp = tsp; + F_SET(sp, S_SSWITCH); + return (0); + + /* Recover the original screen. */ +err: *sp = saved_sp; + *SVP(sp) = saved_svp; + + /* Copy any (probably error) messages in the new screen. */ + for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) { + if (!F_ISSET(mp, M_EMPTY)) + msg_app(sp->gp, sp, + mp->flags & M_INV_VIDEO, mp->mbuf, mp->len); + next = mp->q.le_next; + if (mp->mbuf != NULL) + free(mp->mbuf); + free(mp); + } + + /* Free the new screen. */ + free(_HMAP(tsp)); + free(SVP(tsp)); + FREE(tsp, sizeof(SCR)); + return (1); +} + +/* + * svi_bg -- + * Background the screen, and switch to the next one. + */ +int +svi_bg(csp) + SCR *csp; +{ + SCR *sp; + + /* Try and join with another screen. */ + if ((svi_join(csp, &sp))) + return (1); + if (sp == NULL) { + msgq(csp, M_ERR, + "You may not background your only displayed screen."); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + return (0); +} + +/* + * svi_join -- + * Join the screen into a related screen, if one exists, + * and return that screen. + */ +int +svi_join(csp, nsp) + SCR *csp, **nsp; +{ + SCR *sp; + size_t cnt; + + /* + * If a split screen, add space to parent/child. Make no effort + * to clean up the screen's values. If it's not exiting, we'll + * get it when the user asks to show it again. + */ + if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) { + if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) { + *nsp = NULL; + return (0); + } + sp->woff = csp->woff; + } + sp->rows += csp->rows; + if (ISSMALLSCREEN(sp)) { + sp->t_maxrows += csp->rows; + for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) { + MOVE(sp, cnt, 0); + clrtoeol(); + } + TMAP = HMAP + (sp->t_rows - 1); + } else { + sp->t_maxrows += csp->rows; + sp->t_rows = sp->t_minrows = sp->t_maxrows; + TMAP = HMAP + (sp->t_rows - 1); + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + F_SET(sp, S_REDRAW); + } + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + *nsp = sp; + return (0); +} + +/* + * svi_fg -- + * Background the current screen, and foreground a new one. + */ +int +svi_fg(csp, name) + SCR *csp; + CHAR_T *name; +{ + SCR *sp; + + if (svi_swap(csp, &sp, name)) + return (1); + if (sp == NULL) { + if (name == NULL) + msgq(csp, M_ERR, "There are no background screens."); + else + msgq(csp, M_ERR, + "There's no background screen editing a file named %s.", + name); + return (1); + } + + /* Move the old screen to the hidden queue. */ + CIRCLEQ_REMOVE(&csp->gp->dq, csp, q); + CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q); + + return (0); +} + +/* + * svi_swap -- + * Swap the current screen with a hidden one. + */ +int +svi_swap(csp, nsp, name) + SCR *csp, **nsp; + char *name; +{ + SCR *sp; + int issmallscreen; + + /* Find the screen, or, if name is NULL, the first screen. */ + for (sp = csp->gp->hq.cqh_first; + sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next) + if (name == NULL || !strcmp(FILENAME(sp->frp), name)) + break; + if (sp == (void *)&csp->gp->hq) { + *nsp = NULL; + return (0); + } + *nsp = sp; + + /* Save the old screen's cursor information. */ + csp->frp->lno = csp->lno; + csp->frp->cno = csp->cno; + F_SET(csp->frp, FR_CURSORSET); + + /* Switch screens. */ + csp->nextdisp = sp; + F_SET(csp, S_SSWITCH); + + /* Initialize terminal information. */ + SVP(sp)->srows = SVP(csp)->srows; + + issmallscreen = ISSMALLSCREEN(sp); + + /* Initialize screen information. */ + sp->rows = csp->rows; + sp->cols = csp->cols; + sp->woff = csp->woff; + + /* + * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b. + * + * The new screens may have different screen options sizes than the + * old one, so use them. Make sure that text counts aren't larger + * than the new screen sizes. + */ + if (issmallscreen) { + sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW); + if (sp->t_rows > csp->t_maxrows) + sp->t_rows = sp->t_maxrows; + if (sp->t_minrows > csp->t_maxrows) + sp->t_minrows = sp->t_maxrows; + } else + sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1; + + /* + * If the size of the scrolling region hasn't been modified by + * the user, reset it so it's reasonable for the new screen. + */ + if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET)) + O_VAL(sp, O_SCROLL) = sp->t_maxrows / 2; + + /* + * Don't change the screen's cursor information other than to + * note that the cursor is wrong. + */ + F_SET(SVP(sp), SVI_CUR_INVALID); + + /* + * The HMAP may be NULL, if the screen got resized and + * a bunch of screens had to be hidden. + */ + if (HMAP == NULL) + MALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp) * sizeof(SMAP)); + TMAP = HMAP + (sp->t_rows - 1); + + /* Fill the map. */ + if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL)) + return (1); + + /* + * The new screen replaces the old screen in the parent/child list. + * We insert the new screen after the old one. If we're exiting, + * the exit will delete the old one, if we're foregrounding, the fg + * code will move the old one to the hidden queue. + */ + CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); + CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q); + + F_SET(sp, S_REDRAW); + return (0); +} + +/* + * svi_rabs -- + * Change the absolute size of the current screen. + */ +int +svi_rabs(sp, count) + SCR *sp; + long count; +{ + SCR *g, *s; + + /* + * Figure out which screens will grow, which will shrink, and + * make sure it's possible. + */ + if (count == 0) + return (0); + if (count < 0) { + count = -count; + s = sp; + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + goto toosmall; + if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) { + if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq) + goto toobig; + g->woff -= count; + } else + s->woff += count; + } else { + g = sp; + if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq) + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) + s = NULL; + else + s->woff += count; + else + s = NULL; + if (s == NULL) { + if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) { +toobig: msgq(sp, M_BERR, "The screen cannot %s.", + count < 0 ? "shrink" : "grow"); + return (1); + } + if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) { +toosmall: msgq(sp, M_BERR, + "The screen can only shrink to %d rows.", + MINIMUM_SCREEN_ROWS); + return (1); + } + g->woff -= count; + } + } + + /* Update the screens. */ + g->rows += count; + g->t_rows += count; + if (g->t_minrows == g->t_maxrows) + g->t_minrows += count; + g->t_maxrows += count; + _TMAP(g) = _HMAP(g) + (g->t_rows - 1); + (void)status(g, g->ep, g->lno, 0); + F_SET(g, S_REDRAW); + + s->rows -= count; + s->t_rows -= count; + s->t_maxrows -= count; + if (s->t_minrows > s->t_maxrows) + s->t_minrows = s->t_maxrows; + _TMAP(s) = _HMAP(s) + (s->t_rows - 1); + (void)status(s, s->ep, s->lno, 0); + F_SET(s, S_REDRAW); + + return (0); +} diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c new file mode 100644 index 000000000000..0da531d8db26 --- /dev/null +++ b/usr.bin/vi/svi/svi_util.c @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)svi_util.c 8.26 (Berkeley) 12/9/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "vcmd.h" +#include "excmd.h" +#include "svi_screen.h" + +/* + * svi_screens -- + * Return the number of screens required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_screens(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + size_t cols, len, screens; + char *p; + + /* + * Check for single cached value. The cache is because, if + * the line is large, this routine gets called repeatedly. + * One other hack, lots of time the user is on column one, + * which is an easy one. + */ + if (cnop == NULL) { + if (SVP(sp)->ss_lno == lno) + return (SVP(sp)->ss_screens); + } else if (*cnop == 0) + return (1); + + /* Get a copy of the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0) + return (1); + + /* Figure out how many columns the line/column needs. */ + cols = svi_ncols(sp, p, len, cnop); + + /* Leading number if O_NUMBER option set. */ + if (O_ISSET(sp, O_NUMBER)) + cols += O_NUMBER_LENGTH; + + /* Trailing '$' if O_LIST option set. */ + if (O_ISSET(sp, O_LIST) && cnop == NULL) + cols += sp->gp->cname['$'].len; + + screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0)); + if (cnop == NULL) { + SVP(sp)->ss_lno = lno; + SVP(sp)->ss_screens = screens; + } + return (screens); +} + +/* + * svi_ncols -- + * Return the number of columns required by the line, or, + * if a column is specified, by the column within the line. + */ +size_t +svi_ncols(sp, p, len, cnop) + SCR *sp; + u_char *p; + size_t len, *cnop; +{ + CHNAME const *cname; + size_t cno_cnt, scno; + int ch, listset; + + cname = sp->gp->cname; + listset = O_ISSET(sp, O_LIST); + + if (cnop == NULL) + for (scno = 0; len; --len) + SCNO_INCREMENT; + else + for (cno_cnt = *cnop, scno = 0; len; --len) { + SCNO_INCREMENT; + if (cno_cnt == 0) + break; + --cno_cnt; + } + return (scno); +} + +/* + * bell_putchar -- + * Functional version of putchar, for tputs. + */ +static void +bell_putchar(ch) + int ch; +{ + (void)putchar(ch); +} + +/* + * vbell -- + * Set up the visual bell information. Broken out into a + * separate routine so don't allocate 4K every time we beep. + */ +static int +vbell(sp) + SCR *sp; +{ + size_t len; + char *s, *t, b1[2048], b2[2048]; + + /* Get the termcap information. */ + s = O_STR(sp, O_TERM); + if (tgetent(b1, s) != 1) { + msgq(sp, M_ERR, "No termcap entry for %s", s); + return (1); + } + + /* Get the visual bell string. */ + t = b2; + if (tgetstr("vb", &t) == NULL) { + msgq(sp, M_VINFO, + "No visual bell for %s terminal type", s); + return (1); + } + len = t - b2; + MALLOC_RET(sp, s, char *, len); + memmove(s, b2, len); + if (SVP(sp)->VB != NULL) + free(SVP(sp)->VB); + SVP(sp)->VB = t; + return (0); +} + +/* + * svi_bell -- + * Ring the bell. + */ +void +svi_bell(sp) + SCR *sp; +{ + if (O_ISSET(sp, O_FLASH) && !F_ISSET(SVP(sp), SVI_NO_VBELL)) + if (SVP(sp)->VB != NULL) { + (void)tputs(SVP(sp)->VB, 1, bell_putchar); + (void)fflush(stdout); + } else { + if (vbell(sp)) + F_SET(SVP(sp), SVI_NO_VBELL); + svi_bell(sp); + } + else + (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */ + F_CLR(sp, S_BELLSCHED); +} + +/* + * svi_optchange -- + * Screen specific "option changed" routine. + */ +int +svi_optchange(sp, opt) + SCR *sp; + int opt; +{ + switch (opt) { + case O_TERM: + /* Toss any saved visual bell information. */ + if (SVP(sp)->VB != NULL) { + FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1); + SVP(sp)->VB = NULL; + } + F_CLR(SVP(sp), SVI_NO_VBELL); + F_SET(sp, S_RESIZE); + break; + case O_WINDOW: + if (svi_rrel(sp, O_VAL(sp, O_WINDOW))) + return (1); + break; + } + + (void)v_optchange(sp, opt); + (void)ex_optchange(sp, opt); + + return (0); +} + +/* + * svi_busy -- + * Put the cursor somewhere so the user will think we're busy. + */ +int +svi_busy(sp, msg) + SCR *sp; + char const *msg; +{ + MOVE(sp, INFOLINE(sp), 0); + if (msg) { + ADDSTR(msg); + clrtoeol(); + } + refresh(); + F_SET(SVP(sp), SVI_CUR_INVALID); + return (0); +} + +/* + * svi_clear -- + * Clear from the row down to the end of the screen. + */ +int +svi_clear(sp) + SCR *sp; +{ + size_t oldy, oldx, row; + + getyx(stdscr, oldy, oldx); + for (row = SVP(sp)->srows - 1; row >= oldy; --row) { + MOVEA(sp, row, 0); + clrtoeol(); + } + MOVEA(sp, oldy, oldx); + refresh(); + return (0); +} + +/* + * svi_suspend -- + * Suspend the svi screen; don't kill the process group, curses is + * expected to do that for us. + */ +int +svi_suspend(sp) + SCR *sp; +{ + struct termios t; + int rval; + + /* + * XXX + * See comment in svi_curses_init(). + */ + if (F_ISSET(sp->gp, G_CURSES_S5CB)) { + (void)tcgetattr(STDIN_FILENO, &t); + (void)tcsetattr(STDIN_FILENO, + TCSASOFT | TCSADRAIN, &sp->gp->s5_curses_botch); + } + + F_SET(sp->gp, G_SLEEPING); + if (rval = kill(getpid(), SIGTSTP)) + msgq(sp, M_SYSERR, "SIGTSTP"); + F_CLR(sp->gp, G_SLEEPING); + + if (F_ISSET(sp->gp, G_CURSES_S5CB)) + (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); + + return (rval); +} + +/* + * svi_gdbrefresh -- + * Stub routine so can flush out screen changes using gdb. + */ +#ifdef DEBUG +int +svi_gdbrefresh() +{ + refresh(); + return (0); +} +#endif diff --git a/usr.bin/vi/term.c b/usr.bin/vi/term.c new file mode 100644 index 000000000000..d23fa80c4d45 --- /dev/null +++ b/usr.bin/vi/term.c @@ -0,0 +1,687 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)term.c 8.41 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "vi.h" +#include "seq.h" + +static int keycmp __P((const void *, const void *)); + +/* + * If we're reading less than 20 characters, up the size of the tty buffer. + * This shouldn't ever happen, other than the first time through, but it's + * possible if a map is large enough. + */ +#define term_read_grow(sp, tty) \ + (tty)->len - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty) +static int __term_read_grow __P((SCR *, IBUF *)); + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. + */ +typedef struct _tklist { + char *ts; /* Key's termcap string. */ + char *output; /* Corresponding vi command. */ + char *name; /* Name. */ +} TKLIST; +static TKLIST const tklist[] = { + {"kA", "O", "insert line"}, + {"kD", "x", "delete character"}, + {"kd", "j", "cursor down"}, + {"kE", "D", "delete to eol"}, + {"kF", "\004", "scroll down"}, + {"kH", "$", "go to eol"}, + {"kh", "^", "go to sol"}, + {"kI", "i", "insert at cursor"}, + {"kL", "dd", "delete line"}, + {"kl", "h", "cursor left"}, + {"kN", "\006", "page down"}, + {"kP", "\002", "page up"}, + {"kR", "\025", "scroll up"}, + {"kS", "dG", "delete to end of screen"}, + {"kr", "l", "cursor right"}, + {"ku", "k", "cursor up"}, + {NULL}, +}; + +/* + * XXX + * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET. + */ +typedef struct _keylist { + u_char value; /* Special value. */ + CHAR_T ch; /* Key. */ +} KEYLIST; +static KEYLIST keylist[] = { + {K_CARAT, '^'}, + {K_CNTRLR, '\022'}, + {K_CNTRLT, '\024'}, + {K_CNTRLZ, '\032'}, + {K_COLON, ':'}, + {K_CR, '\r'}, + {K_ESCAPE, '\033'}, + {K_FORMFEED, '\f'}, + {K_NL, '\n'}, + {K_RIGHTBRACE, '}'}, + {K_RIGHTPAREN, ')'}, + {K_TAB, '\t'}, + {K_VEOF, '\004'}, + {K_VERASE, '\b'}, + {K_VINTR, '\003'}, + {K_VKILL, '\025'}, + {K_VLNEXT, '\026'}, + {K_VWERASE, '\027'}, + {K_ZERO, '0'}, +}; + +/* + * term_init -- + * Initialize the special key lookup table, and the special keys + * defined by the terminal's termcap entry. + */ +int +term_init(sp) + SCR *sp; +{ + extern CHNAME const asciiname[]; /* XXX */ + GS *gp; + KEYLIST *kp; + TKLIST const *tkp; + cc_t ch; + int cnt; + char *sbp, *t, buf[2 * 1024], sbuf[128]; + + /* + * XXX + * 8-bit, ASCII only, for now. Recompilation should get you + * any 8-bit character set, as long as nul isn't a character. + */ + gp = sp->gp; + gp->cname = asciiname; /* XXX */ + + /* Set keys found in the termios structure. */ +#define TERMSET(name, val) { \ + if ((ch = gp->original_termios.c_cc[name]) != _POSIX_VDISABLE) \ + for (kp = keylist;; ++kp) \ + if (kp->value == (val)) { \ + kp->ch = ch; \ + break; \ + } \ +} +/* + * VEOF, VERASE, VKILL are required by POSIX 1003.1-1990, + * VWERASE is a 4.4BSD extension. + */ +#ifdef VEOF + TERMSET(VEOF, K_VEOF); +#endif +#ifdef VERASE + TERMSET(VERASE, K_VERASE); +#endif +#ifdef VINTR + TERMSET(VINTR, K_VINTR); +#endif +#ifdef VKILL + TERMSET(VKILL, K_VKILL); +#endif +#ifdef VWERASE + TERMSET(VWERASE, K_VWERASE); +#endif + + /* Sort the special key list. */ + qsort(keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + + /* Initialize the fast lookup table. */ + CALLOC_RET(sp, + gp->special_key, u_char *, MAX_FAST_KEY + 1, sizeof(u_char)); + for (gp->max_special = 0, kp = keylist, + cnt = sizeof(keylist) / sizeof(keylist[0]); cnt--; ++kp) { + if (gp->max_special < kp->value) + gp->max_special = kp->value; + if (kp->ch <= MAX_FAST_KEY) + gp->special_key[kp->ch] = kp->value; + } + + /* Set key sequences found in the termcap entry. */ + switch (tgetent(buf, O_STR(sp, O_TERM))) { + case -1: + msgq(sp, M_ERR, + "tgetent: %s: %s.", O_STR(sp, O_TERM), strerror(errno)); + return (0); + case 0: + msgq(sp, M_ERR, + "%s: unknown terminal type.", O_STR(sp, O_TERM)); + return (0); + } + + for (tkp = tklist; tkp->name != NULL; ++tkp) { + sbp = sbuf; + if ((t = tgetstr(tkp->ts, &sbp)) == NULL) + continue; + if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), + tkp->output, strlen(tkp->output), SEQ_COMMAND, 0)) + return (1); + } + return (0); +} + +/* + * term_push -- + * Push keys onto the front of a buffer. + * + * There is a single input buffer in ex/vi. Characters are read onto the + * end of the buffer by the terminal input routines, and pushed onto the + * front of the buffer various other functions in ex/vi. Each key has an + * associated flag value, which indicates if it has already been quoted, + * if it is the result of a mapping or an abbreviation. + */ +int +term_push(sp, s, len, cmap, flags) + SCR *sp; + CHAR_T *s; /* Characters. */ + size_t len; /* Number of chars. */ + u_int cmap; /* Map count. */ + u_int flags; /* CH_* flags. */ +{ + IBUF *tty; + size_t nlen; + + /* If we have room, stuff the keys into the buffer. */ + tty = sp->gp->tty; + if (len <= tty->next || + (tty->ch != NULL && tty->cnt == 0 && len <= tty->len)) { + if (tty->cnt != 0) + tty->next -= len; + tty->cnt += len; + memmove(tty->ch + tty->next, s, len * sizeof(CHAR_T)); + memset(tty->chf + tty->next, flags, len); + memset(tty->cmap + tty->next, cmap, len); + return (0); + } + + /* Get enough space plus a little extra. */ + nlen = tty->cnt + len; + if (nlen > tty->len) { + size_t olen; + + nlen += 64; + olen = tty->len; + BINC_RET(sp, tty->ch, olen, nlen * sizeof(tty->ch[0])); + olen = tty->len; + BINC_RET(sp, tty->chf, olen, nlen * sizeof(tty->chf[0])); + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + } + + /* + * If there are currently characters in the queue, shift them up, + * leaving some extra room. + */ +#define TERM_PUSH_SHIFT 30 + if (tty->cnt) { + memmove(tty->ch + TERM_PUSH_SHIFT + len, + tty->ch + tty->next, tty->cnt * sizeof(tty->ch[0])); + memmove(tty->chf + TERM_PUSH_SHIFT + len, + tty->chf + tty->next, tty->cnt * sizeof(tty->chf[0])); + memmove(tty->cmap + TERM_PUSH_SHIFT + len, + tty->cmap + tty->next, tty->cnt * sizeof(tty->cmap[0])); + } + + /* Put the new characters into the queue. */ + tty->next = TERM_PUSH_SHIFT; + tty->cnt += len; + memmove(tty->ch + TERM_PUSH_SHIFT, s, len * sizeof(tty->ch[0])); + memset(tty->chf + TERM_PUSH_SHIFT, flags, len * sizeof(tty->chf[0])); + memset(tty->cmap + TERM_PUSH_SHIFT, cmap, len * sizeof(tty->cmap[0])); + return (0); +} + +/* + * Remove characters from the queue, simultaneously clearing the + * flag and map counts. + */ +#define QREM_HEAD(q, len) { \ + size_t __off = (q)->next; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ + else \ + (q)->next += len; \ +} +#define QREM_TAIL(q, len) { \ + size_t __off = (q)->next + (q)->cnt - 1; \ + if (len == 1) { \ + tty->chf[__off] = 0; \ + tty->cmap[__off] = 0; \ + } else { \ + memset(tty->chf + __off, 0, len); \ + memset(tty->cmap + __off, 0, len); \ + } \ + if (((q)->cnt -= len) == 0) \ + (q)->next = 0; \ +} + +/* + * term_key -- + * Get the next key. + * + * !!! + * The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea + * of mapping keys is that one or more keystrokes act like a function key. + * What's going on is that vi is reading a number, and the character following + * the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the + * user is entering the z command, a valid command is "z40+", and we don't want + * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it + * into "z40xxx". However, if the user enters "35x", we want to put all of the + * characters through the mapping code. + * + * Historical practice is a bit muddled here. (Surprise!) It always permitted + * mapping digits as long as they weren't the first character of the map, e.g. + * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9 + * (the digit 0 was a special case as it doesn't indicate the start of a count) + * as the first character of the map, but then ignored those mappings. While + * it's probably stupid to map digits, vi isn't your mother. + * + * The way this works is that the TXT_MAPNODIGIT causes term_key to return the + * end-of-digit without "looking" at the next character, i.e. leaving it as the + * user entered it. Presumably, the next term_key call will tell us how the + * user wants it handled. + * + * There is one more complication. Users might map keys to digits, and, as + * it's described above, the commands "map g 1G|d2g" would return the keys + * "d2<end-of-digits>1G", when the user probably wanted "d21<end-of-digits>G". + * So, if a map starts off with a digit we continue as before, otherwise, we + * pretend that we haven't mapped the character and return <end-of-digits>. + * + * Now that that's out of the way, let's talk about Energizer Bunny macros. + * It's easy to create macros that expand to a loop, e.g. map x 3x. It's + * fairly easy to detect this example, because it's all internal to term_key. + * If we're expanding a macro and it gets big enough, at some point we can + * assume it's looping and kill it. The examples that are tough are the ones + * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion + * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then + * find the looping macro again. There is no way that we can detect this + * without doing a full parse of the command, because the character that might + * cause the loop (in this case 'x') may be a literal character, e.g. the map + * map x "ayy"xyy"byy is perfectly legal and won't cause a loop. + * + * Historic vi tried to detect looping macros by disallowing obvious cases in + * the map command, maps that that ended with the same letter as they started + * (which wrongly disallowed "map x 'x"), and detecting macros that expanded + * too many times before keys were returned to the command parser. It didn't + * get many (most?) of the tricky cases right, however, and it was certainly + * possible to create macros that ran forever. And, even if it did figure out + * what was going on, the user was usually tossed into ex mode. Finally, any + * changes made before vi realized that the macro was recursing were left in + * place. This implementation counts how many times each input character has + * been mapped. If it reaches some arbitrary value, we flush all mapped keys + * and return an error. + * + * XXX + * The final issue is recovery. It would be possible to undo all of the work + * that was done by the macro if we entered a record into the log so that we + * knew when the macro started, and, in fact, this might be worth doing at some + * point. Given that this might make the log grow unacceptably (consider that + * cursor keys are done with maps), for now we leave any changes made in place. + */ +enum input +term_key(sp, chp, flags) + SCR *sp; + CH *chp; + u_int flags; +{ + enum input rval; + struct timeval t, *tp; + CHAR_T ch; + GS *gp; + IBUF *tty; + SEQ *qp; + int cmap, ispartial, nr; + + gp = sp->gp; + tty = gp->tty; + + /* + * If the queue is empty, read more keys in. Since no timeout is + * requested, s_key_read will either return an error or will read + * some number of characters. + */ +loop: if (tty->cnt == 0) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + /* + * If there's something on the mode line that we wanted + * the user to see, they just entered a character so we + * can presume they saw it. + */ + if (F_ISSET(sp, S_UPDATE_MODE)) + F_CLR(sp, S_UPDATE_MODE); + } + + /* If the key is mappable and should be mapped, look it up. */ + if (!(tty->chf[tty->next] & CH_NOMAP) && + LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) { + /* Set up timeout value. */ + if (O_ISSET(sp, O_TIMEOUT)) { + tp = &t; + t.tv_sec = O_VAL(sp, O_KEYTIME) / 10; + t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L; + } else + tp = NULL; + + /* Get the next key. */ +newmap: ch = tty->ch[tty->next]; + if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch)) + goto nomap; + + /* Search the map. */ +remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt, + LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT, + &ispartial); + + /* + * If get a partial match, read more characters and retry + * the map. If no characters read, return the characters + * unmapped. + */ + if (ispartial) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, tp)) + return (rval); + if (nr) + goto remap; + goto nomap; + } + + /* If no map, return the character. */ + if (qp == NULL) + goto nomap; + + /* + * If looking for the end of a digit string, and the first + * character of the map is it, pretend we haven't seen the + * character. + */ + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(qp->output[0])) + goto not_digit_ch; + + /* + * Only permit a character to be remapped a certain number + * of times before we figure that it's not going to finish. + */ + if ((cmap = tty->cmap[tty->next]) > MAX_MAP_COUNT) { + term_map_flush(sp, "Character remapped too many times"); + return (INP_ERR); + } + + /* Delete the mapped characters from the queue. */ + QREM_HEAD(tty, qp->ilen); + + /* If remapping characters, push the character on the queue. */ + if (O_ISSET(sp, O_REMAP)) { + if (term_push(sp, qp->output, qp->olen, ++cmap, 0)) + return (INP_ERR); + goto newmap; + } + + /* Else, push the characters on the queue and return one. */ + if (term_push(sp, qp->output, qp->olen, 0, CH_NOMAP)) + return (INP_ERR); + } + +nomap: ch = tty->ch[tty->next]; + if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) { +not_digit_ch: chp->ch = NOT_DIGIT_CH; + chp->value = 0; + chp->flags = 0; + return (INP_OK); + } + + /* Fill in the return information. */ + chp->ch = ch; + chp->flags = tty->chf[tty->next]; + chp->value = term_key_val(sp, ch); + + /* Delete the character from the queue. */ + QREM_HEAD(tty, 1); + + /* + * O_BEAUTIFY eliminates all control characters except + * escape, form-feed, newline and tab. + */ + if (isprint(ch) || + !LF_ISSET(TXT_BEAUTIFY) || !O_ISSET(sp, O_BEAUTIFY) || + chp->value == K_ESCAPE || chp->value == K_FORMFEED || + chp->value == K_NL || chp->value == K_TAB) + return (INP_OK); + + goto loop; +} + +/* + * term_ab_flush -- + * Flush any abbreviated keys. + */ +void +term_ab_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !(tty->chf[tty->next] & CH_ABBREVIATED)) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->chf[tty->next] & CH_ABBREVIATED); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} +/* + * term_map_flush -- + * Flush any mapped keys. + */ +void +term_map_flush(sp, msg) + SCR *sp; + char *msg; +{ + IBUF *tty; + + tty = sp->gp->tty; + if (!tty->cnt || !tty->cmap[tty->next]) + return; + do { + QREM_HEAD(tty, 1); + } while (tty->cnt && tty->cmap[tty->next]); + msgq(sp, M_ERR, "%s: keys flushed.", msg); + +} + +/* + * term_user_key -- + * Get the next key, but require the user enter one. + */ +enum input +term_user_key(sp, chp) + SCR *sp; + CH *chp; +{ + enum input rval; + IBUF *tty; + int nr; + + /* + * Read any keys the user has waiting. Make the race + * condition as short as possible. + */ + if (rval = term_key_queue(sp)) + return (rval); + + /* Wait and read another key. */ + if (rval = sp->s_key_read(sp, &nr, NULL)) + return (rval); + + /* Fill in the return information. */ + tty = sp->gp->tty; + chp->ch = tty->ch[tty->next + (tty->cnt - 1)]; + chp->flags = 0; + chp->value = term_key_val(sp, chp->ch); + + QREM_TAIL(tty, 1); + return (INP_OK); +} + +/* + * term_key_queue -- + * Read the keys off of the terminal queue until it's empty. + */ +int +term_key_queue(sp) + SCR *sp; +{ + enum input rval; + struct timeval t; + IBUF *tty; + int nr; + + t.tv_sec = 0; + t.tv_usec = 0; + for (tty = sp->gp->tty;;) { + if (term_read_grow(sp, tty)) + return (INP_ERR); + if (rval = sp->s_key_read(sp, &nr, &t)) + return (rval); + if (nr == 0) + break; + } + return (INP_OK); +} + +/* + * term_key_ch -- + * Fill in the key for a value. + */ +int +term_key_ch(sp, val, chp) + SCR *sp; + int val; + CHAR_T *chp; +{ + KEYLIST *kp; + + for (kp = keylist;; ++kp) + if (kp->value == val) { + *chp = kp->ch; + return (0); + } + /* NOTREACHED */ +} + +/* + * __term_key_val -- + * Fill in the value for a key. This routine is the backup + * for the term_key_val() macro. + */ +int +__term_key_val(sp, ch) + SCR *sp; + ARG_CHAR_T ch; +{ + KEYLIST k, *kp; + + k.ch = ch; + kp = bsearch(&k, keylist, + sizeof(keylist) / sizeof(keylist[0]), sizeof(keylist[0]), keycmp); + return (kp == NULL ? 0 : kp->value); +} + +/* + * __term_read_grow -- + * Grow the terminal queue. This routine is the backup for + * the term_read_grow() macro. + */ +static int +__term_read_grow(sp, tty) + SCR *sp; + IBUF *tty; +{ + size_t alen, len, nlen; + + nlen = tty->len + 64; + alen = tty->len - (tty->next + tty->cnt); + + len = tty->len; + BINC_RET(sp, tty->ch, len, nlen * sizeof(tty->ch[0])); + memset(tty->ch + tty->next + tty->cnt, 0, alen * sizeof(tty->ch[0])); + + len = tty->len; + BINC_RET(sp, tty->chf, len, nlen * sizeof(tty->chf[0])); + memset(tty->chf + tty->next + tty->cnt, 0, alen * sizeof(tty->chf[0])); + + BINC_RET(sp, tty->cmap, tty->len, nlen * sizeof(tty->cmap[0])); + memset(tty->cmap + + tty->next + tty->cnt, 0, alen * sizeof(tty->cmap[0])); + return (0); +} + +static int +keycmp(ap, bp) + const void *ap, *bp; +{ + return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch); +} diff --git a/usr.bin/vi/term.h b/usr.bin/vi/term.h new file mode 100644 index 000000000000..67f7f5d2822c --- /dev/null +++ b/usr.bin/vi/term.h @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)term.h 8.26 (Berkeley) 1/7/94 + */ + +/* Structure to return a character and associated information. */ +struct _ch { + CHAR_T ch; /* Character. */ + +#define K_CARAT 1 +#define K_CNTRLR 2 +#define K_CNTRLT 3 +#define K_CNTRLZ 4 +#define K_COLON 5 +#define K_CR 6 +#define K_ESCAPE 7 +#define K_FORMFEED 8 +#define K_NL 9 +#define K_RIGHTBRACE 10 +#define K_RIGHTPAREN 11 +#define K_TAB 12 +#define K_VEOF 13 +#define K_VERASE 14 +#define K_VINTR 15 +#define K_VKILL 16 +#define K_VLNEXT 17 +#define K_VWERASE 18 +#define K_ZERO 19 + u_char value; /* Special character flag values. */ + +#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */ +#define CH_NOMAP 0x02 /* Do not attempt to map the character. */ +#define CH_QUOTED 0x04 /* Character is already quoted. */ + u_char flags; +}; + +/* + * Structure for the key input buffer. + * + * MAX_MAP_COUNT was chosen based on the vi maze script, which remaps + * characters roughly 250 times. + */ +struct _ibuf { + CHAR_T *ch; /* Array of characters. */ + u_char *chf; /* Array of character flags (CH_*). */ +#define MAX_MAP_COUNT 270 /* Maximum times a character can remap. */ + u_char *cmap; /* Number of times character has been mapped. */ + + size_t cnt; /* Count of remaining characters. */ + size_t len; /* Array length. */ + size_t next; /* Offset of next array entry. */ +}; + /* Return if more keys in queue. */ +#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt) +#define MAPPED_KEYS_WAITING(sp) \ + (KEYS_WAITING(sp) && sp->gp->tty->cmap[sp->gp->tty->next]) + +/* + * Structure to name a character. Used both as an interface to the + * screen and to name objects named by characters in error messages. + */ +struct _chname { + char *name; /* Character name. */ + u_char len; /* Length of the character name. */ +}; + +/* + * Routines that return a key as a side-effect return: + * + * INP_OK Returning a character; must be 0. + * INP_EOF EOF. + * INP_ERR Error. + * + * The vi structure depends on the key routines being able to return INP_EOF + * multiple times without failing -- eventually enough things will end due to + * INP_EOF that vi will reach the command level for the screen, at which point + * the exit flags will be set and vi will exit. + */ +enum input { INP_OK=0, INP_EOF, INP_ERR }; + +/* + * Routines that return a confirmation return: + * + * CONF_NO User answered no. + * CONF_QUIT User answered quit, eof or an error. + * CONF_YES User answered yes. + */ +enum confirm { CONF_NO, CONF_QUIT, CONF_YES }; + +/* + * Ex/vi commands are generally separated by whitespace characters. We + * can't use the standard isspace(3) macro because it returns true for + * characters like ^K in the ASCII character set. The 4.4BSD isblank(3) + * macro does exactly what we want, but it's not portable yet. + * + * XXX + * Note side effect, ch is evaluated multiple times. + */ +#ifndef isblank +#define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +/* Various special characters, messages. */ +#define CURSOR_CH ' ' /* Cursor character. */ +#define END_CH '$' /* End of a range. */ +#define HEX_CH 'x' /* Leading hex number. */ +#define NOT_DIGIT_CH 'a' /* A non-isdigit() character. */ +#define NO_CH 'n' /* No. */ +#define QUIT_CH 'q' /* Quit. */ +#define YES_CH 'y' /* Yes. */ +#define CONFSTRING "confirm? [ynq]" +#define CONTMSG "Enter return to continue: " +#define CONTMSG_I "Enter return to continue [q to quit]: " + +/* Flags describing how input is handled. */ +#define TXT_AICHARS 0x000001 /* Leading autoindent chars. */ +#define TXT_ALTWERASE 0x000002 /* Option: altwerase. */ +#define TXT_APPENDEOL 0x000004 /* Appending after EOL. */ +#define TXT_AUTOINDENT 0x000008 /* Autoindent set this line. */ +#define TXT_BEAUTIFY 0x000010 /* Only printable characters. */ +#define TXT_BS 0x000020 /* Backspace returns the buffer. */ +#define TXT_CNTRLT 0x000040 /* Control-T is an indent special. */ +#define TXT_CR 0x000080 /* CR returns the buffer. */ +#define TXT_EMARK 0x000100 /* End of replacement mark. */ +#define TXT_ESCAPE 0x000200 /* Escape returns the buffer. */ +#define TXT_INFOLINE 0x000400 /* Editing the info line. */ +#define TXT_MAPCOMMAND 0x000800 /* Apply the command map. */ +#define TXT_MAPINPUT 0x001000 /* Apply the input map. */ +#define TXT_MAPNODIGIT 0x002000 /* Return to a digit. */ +#define TXT_NLECHO 0x004000 /* Echo the newline. */ +#define TXT_OVERWRITE 0x008000 /* Overwrite characters. */ +#define TXT_PROMPT 0x010000 /* Display a prompt. */ +#define TXT_RECORD 0x020000 /* Record for replay. */ +#define TXT_REPLACE 0x040000 /* Replace; don't delete overwrite. */ +#define TXT_REPLAY 0x080000 /* Replay the last input. */ +#define TXT_RESOLVE 0x100000 /* Resolve the text into the file. */ +#define TXT_SHOWMATCH 0x200000 /* Option: showmatch. */ +#define TXT_TTYWERASE 0x400000 /* Option: ttywerase. */ +#define TXT_WRAPMARGIN 0x800000 /* Option: wrapmargin. */ + +#define TXT_VALID_EX \ + (TXT_BEAUTIFY | TXT_CR | TXT_NLECHO | TXT_PROMPT) + +/* Support keyboard routines. */ +int __term_key_val __P((SCR *, ARG_CHAR_T)); +void term_ab_flush __P((SCR *, char *)); +int term_init __P((SCR *)); +enum input term_key __P((SCR *, CH *, u_int)); +int term_key_ch __P((SCR *, int, CHAR_T *)); +int term_key_queue __P((SCR *)); +void term_map_flush __P((SCR *, char *)); +int term_push __P((SCR *, CHAR_T *, size_t, u_int, u_int)); +enum input term_user_key __P((SCR *, CH *)); +int term_waiting __P((SCR *)); diff --git a/usr.bin/vi/timer.c b/usr.bin/vi/timer.c new file mode 100644 index 000000000000..e2bcfb1c8852 --- /dev/null +++ b/usr.bin/vi/timer.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)timer.c 8.7 (Berkeley) 12/2/93"; +#endif /* not lint */ + +#include <sys/time.h> + +#include "vi.h" + +static void busy_handler __P((int)); + +/* + * XXX + * There are two uses of timers in nvi. The first is to push the recovery + * information out to disk at periodic intervals. The second is to display + * a "busy" message if an operation takes too long. Rather than solve this + * in a general fashion, we depend on the fact that only a single screen in + * a window is active at a time, and that there are only two parts of the + * systems that use timers. + * + * It would be nice to reimplement this with multiple timers, a la POSIX + * 1003.1, but not many systems offer them yet. + */ + +/* + * busy_on -- + * Display a message if too much time passes. + */ +void +busy_on(sp, seconds, msg) + SCR *sp; + int seconds; + char const *msg; +{ + struct itimerval value; + struct sigaction act; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* Turn off the current timer, saving its current value. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &sp->time_value)) + return; + + /* + * Decrement the original timer by the number of seconds + * we're going to wait. + */ + if (sp->time_value.it_value.tv_sec > seconds) + sp->time_value.it_value.tv_sec -= seconds; + else + sp->time_value.it_value.tv_sec = 1; + + /* Reset the handler, saving its current value. */ + act.sa_handler = busy_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(SIGALRM, &act, &sp->time_handler); + + /* Reset the timer. */ + value.it_value.tv_sec = seconds; + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + (void)setitimer(ITIMER_REAL, &value, NULL); + + sp->time_msg = msg; + F_SET(sp, S_TIMER_SET); +} + +/* + * busy_off -- + * Reset the timer handlers. + */ +void +busy_off(sp) + SCR *sp; +{ + struct itimerval ovalue, value; + + /* No busy messages in batch mode. */ + if (F_ISSET(sp, S_EXSILENT)) + return; + + /* If the timer flag isn't set, it must have fired. */ + if (!F_ISSET(sp, S_TIMER_SET)) + return; + + /* Ignore it if first on one of following system calls. */ + F_CLR(sp, S_TIMER_SET); + + /* Turn off the current timer. */ + value.it_interval.tv_sec = value.it_value.tv_sec = 0; + value.it_interval.tv_usec = value.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &value, &ovalue)) + return; + + /* If the timer wasn't running, we're done. */ + if (sp->time_handler.sa_handler == SIG_DFL) + return; + + /* + * Increment the old timer by the number of seconds + * remaining in the new one. + */ + sp->time_value.it_value.tv_sec += ovalue.it_value.tv_sec; + + /* Reset the handler to the original handler. */ + (void)sigaction(SIGALRM, &sp->time_handler, NULL); + + /* Reset the timer. */ + (void)setitimer(ITIMER_REAL, &sp->time_value, NULL); +} + +/* + * busy_handler -- + * Display a message when the timer goes off, and restore the + * timer to its original values. + */ +static void +busy_handler(signo) + int signo; +{ + SCR *sp; + + for (sp = __global_list->dq.cqh_first; + sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) + if (F_ISSET(sp, S_TIMER_SET)) { + sp->s_busy(sp, sp->time_msg); + busy_off(sp); + } +} diff --git a/usr.bin/vi/trace.c b/usr.bin/vi/trace.c new file mode 100644 index 000000000000..f97e47bdc7db --- /dev/null +++ b/usr.bin/vi/trace.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trace.c 8.1 (Berkeley) 6/9/93 + */ + +#ifdef DEBUG +#include <sys/types.h> + +#include "vi.h" + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#ifdef __STDC__ +TRACE(SCR *sp, const char *fmt, ...) +#else +TRACE(sp, fmt, va_alist) + SCR *sp; + char *fmt; + va_dcl +#endif +{ + FILE *tfp; + va_list ap; + + if ((tfp = sp->gp->tracefp) == NULL) + return; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(tfp, fmt, ap); + va_end(ap); + + (void)fflush(tfp); +} +#endif diff --git a/usr.bin/vi/util.c b/usr.bin/vi/util.c new file mode 100644 index 000000000000..79a06516af48 --- /dev/null +++ b/usr.bin/vi/util.c @@ -0,0 +1,578 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)util.c 8.34 (Berkeley) 12/23/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <ctype.h> +#include <curses.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "vi.h" + +/* + * msgq -- + * Display a message. + */ +void +#ifdef __STDC__ +msgq(SCR *sp, enum msgtype mt, const char *fmt, ...) +#else +msgq(sp, mt, fmt, va_alist) + SCR *sp; + enum msgtype mt; + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char msgbuf[1024]; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + /* + * It's possible to enter msg when there's no screen to hold + * the message. Always check sp before using it, and, if it's + * NULL, use __global_list. + */ + switch (mt) { + case M_BERR: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) { + F_SET(sp, S_BELLSCHED); + return; + } + mt = M_ERR; + break; + case M_VINFO: + if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) + return; + mt = M_INFO; + /* FALLTHROUGH */ + case M_INFO: + if (F_ISSET(sp, S_EXSILENT)) + return; + break; + case M_ERR: + case M_SYSERR: + break; + default: + abort(); + } + + /* Length is the min length of the message or the buffer. */ + if (mt == M_SYSERR) + if (sp->if_name != NULL) + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno, + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else + len = snprintf(msgbuf, sizeof(msgbuf), + "Error: %s%s%s.", + fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ", + strerror(errno)); + else { + len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf), + "%s, %d: ", sp->if_name, sp->if_lno); + len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap); + } + + /* + * If len >= the size, some characters were discarded. + * Ignore trailing nul. + */ + if (len >= sizeof(msgbuf)) + len = sizeof(msgbuf) - 1; + + msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len); +} + +/* + * msg_app -- + * Append a message into the queue. This can fail, but there's + * nothing we can do if it does. + */ +void +msg_app(gp, sp, inv_video, p, len) + GS *gp; + SCR *sp; + int inv_video; + char *p; + size_t len; +{ + static int reenter; /* STATIC: Re-entrancy check. */ + MSG *mp, *nmp; + + /* + * It's possible to reenter msg when it allocates space. + * We're probably dead anyway, but no reason to drop core. + */ + if (reenter) + return; + reenter = 1; + + /* + * Find an empty structure, or allocate a new one. Use the + * screen structure if possible, otherwise the global one. + */ + if (sp != NULL) { + if ((mp = sp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&sp->msgq, mp, q); + goto store; + } + } else if ((mp = gp->msgq.lh_first) == NULL) { + CALLOC(sp, mp, MSG *, 1, sizeof(MSG)); + if (mp == NULL) + goto ret; + LIST_INSERT_HEAD(&gp->msgq, mp, q); + goto store; + } + while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL) + mp = mp->q.le_next; + if (!F_ISSET(mp, M_EMPTY)) { + CALLOC(sp, nmp, MSG *, 1, sizeof(MSG)); + if (nmp == NULL) + goto ret; + LIST_INSERT_AFTER(mp, nmp, q); + mp = nmp; + } + + /* Get enough memory for the message. */ +store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len)) + goto ret; + + /* Store the message. */ + memmove(mp->mbuf, p, len); + mp->len = len; + mp->flags = inv_video ? M_INV_VIDEO : 0; + +ret: reenter = 0; +} + +/* + * msgrpt -- + * Report on the lines that changed. + * + * !!! + * Historic vi documentation (USD:15-8) claimed that "The editor will also + * always tell you when a change you make affects text which you cannot see." + * This isn't true -- edit a large file and do "100d|1". We don't implement + * this semantic as it would require that we track each line that changes + * during a command instead of just keeping count. + */ +int +msg_rpt(sp, is_message) + SCR *sp; + int is_message; +{ + static const char *const action[] = { + "added", "changed", "copied", "deleted", "joined", "moved", + "put", "left shifted", "right shifted", "yanked", NULL, + }; + recno_t total; + u_long rval; + int first, cnt; + size_t blen, len; + const char *const *ap; + char *bp, *p, number[40]; + + if (F_ISSET(sp, S_EXSILENT)) + return (0); + + if ((rval = O_VAL(sp, O_REPORT)) == 0) + goto norpt; + + GET_SPACE_RET(sp, bp, blen, 512); + p = bp; + + total = 0; + for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt) + if (sp->rptlines[cnt] != 0) { + total += sp->rptlines[cnt]; + len = snprintf(number, sizeof(number), + "%s%lu line%s %s", first ? "" : "; ", + sp->rptlines[cnt], + sp->rptlines[cnt] > 1 ? "s" : "", *ap); + memmove(p, number, len); + p += len; + first = 0; + } + + /* + * If nothing to report, return. Note that the number of lines + * must be > than the user's value, not >=. This is historic + * practice and means that users cannot report on single line + * changes. + */ + if (total > rval) { + *p = '\0'; + + if (is_message) + msgq(sp, M_INFO, "%s", bp); + else + ex_printf(EXCOOKIE, "%s\n", bp); + } + + FREE_SPACE(sp, bp, blen); + + /* Clear after each report. */ +norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines)); + return (0); +} + +/* + * binc -- + * Increase the size of a buffer. + */ +int +binc(sp, argp, bsizep, min) + SCR *sp; /* MAY BE NULL */ + void *argp; + size_t *bsizep, min; +{ + void *bpp; + size_t csize; + + /* If already larger than the minimum, just return. */ + csize = *bsizep; + if (min && csize >= min) + return (0); + + csize += MAX(min, 256); + bpp = *(char **)argp; + + /* For non-ANSI C realloc implementations. */ + if (bpp == NULL) + bpp = malloc(csize * sizeof(CHAR_T)); + else + bpp = realloc(bpp, csize * sizeof(CHAR_T)); + if (bpp == NULL) { + msgq(sp, M_SYSERR, NULL); + *bsizep = 0; + return (1); + } + *(char **)argp = bpp; + *bsizep = csize; + return (0); +} + +/* + * nonblank -- + * Set the column number of the first non-blank character + * including or after the starting column. On error, set + * the column to 0, it's safest. + */ +int +nonblank(sp, ep, lno, cnop) + SCR *sp; + EXF *ep; + recno_t lno; + size_t *cnop; +{ + char *p; + size_t cnt, len, off; + + /* Default. */ + off = *cnop; + *cnop = 0; + + /* Get the line. */ + if ((p = file_gline(sp, ep, lno, &len)) == NULL) { + if (file_lline(sp, ep, &lno)) + return (1); + if (lno == 0) + return (0); + GETLINE_ERR(sp, lno); + return (1); + } + + /* Set the offset. */ + if (len == 0 || off >= len) + return (0); + + for (cnt = off, p = &p[off], + len -= off; len && isblank(*p); ++cnt, ++p, --len); + + /* Set the return. */ + *cnop = len ? cnt : cnt - 1; + return (0); +} + +/* + * tail -- + * Return tail of a path. + */ +char * +tail(path) + char *path; +{ + char *p; + + if ((p = strrchr(path, '/')) == NULL) + return (path); + return (p + 1); +} + +/* + * set_window_size -- + * Set the window size, the row may be provided as an argument. + */ +int +set_window_size(sp, set_row, ign_env) + SCR *sp; + u_int set_row; + int ign_env; +{ + struct winsize win; + size_t col, row; + int user_set; + ARGS *argv[2], a, b; + char *s, buf[2048]; + + /* + * Get the screen rows and columns. If the values are wrong, it's + * not a big deal -- as soon as the user sets them explicitly the + * environment will be set and the screen package will use the new + * values. + * + * Try TIOCGWINSZ. + */ + row = col = 0; +#ifdef TIOCGWINSZ + if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { + row = win.ws_row; + col = win.ws_col; + } +#endif + + /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */ + if (row == 0 || col == 0) { + s = NULL; + if (F_ISSET(&sp->opts[O_TERM], OPT_SET)) + s = O_STR(sp, O_TERM); + else + s = getenv("TERM"); + if (s != NULL && tgetent(buf, s) == 1) { + if (row == 0) + row = tgetnum("li"); + if (col == 0) + col = tgetnum("co"); + } + } + /* If nothing else, well, it's probably a VT100. */ + if (row == 0) + row = 24; + if (col == 0) + col = 80; + + /* + * POSIX 1003.2 requires the environment to override, however, + * if we're here because of a signal, we don't want to use the + * old values. + */ + if (!ign_env) { + if ((s = getenv("LINES")) != NULL) + row = strtol(s, NULL, 10); + if ((s = getenv("COLUMNS")) != NULL) + col = strtol(s, NULL, 10); + } + + /* But, if we got an argument for the rows, use it. */ + if (set_row) + row = set_row; + + a.bp = buf; + b.bp = NULL; + b.len = 0; + argv[0] = &a; + argv[1] = &b;; + + /* + * Tell the options code that the screen size has changed. + * Since the user didn't do the set, clear the set bits. + */ + user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "lines=%u", row); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_LINES], OPT_SET); + user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET); + a.len = snprintf(buf, sizeof(buf), "columns=%u", col); + if (opts_set(sp, argv)) + return (1); + if (user_set) + F_CLR(&sp->opts[O_COLUMNS], OPT_SET); + return (0); +} + +/* + * set_alt_name -- + * Set the alternate file name. + * + * Swap the alternate file name. It's a routine because I wanted some place + * to hang this comment. The alternate file name (normally referenced using + * the special character '#' during file expansion) is set by many + * operations. In the historic vi, the commands "ex", and "edit" obviously + * set the alternate file name because they switched the underlying file. + * Less obviously, the "read", "file", "write" and "wq" commands set it as + * well. In this implementation, some new commands have been added to the + * list. Where it gets interesting is that the alternate file name is set + * multiple times by some commands. If an edit attempt fails (for whatever + * reason, like the current file is modified but as yet unwritten), it is + * set to the file name that the user was unable to edit. If the edit + * succeeds, it is set to the last file name that was edited. Good fun. + * + * If the user edits a temporary file, there are time when there isn't an + * alternative file name. A name argument of NULL turns it off. + */ +void +set_alt_name(sp, name) + SCR *sp; + char *name; +{ + if (sp->alt_name != NULL) + FREE(sp->alt_name, strlen(sp->alt_name) + 1); + if (name == NULL) + sp->alt_name = NULL; + else if ((sp->alt_name = strdup(name)) == NULL) + msgq(sp, M_SYSERR, NULL); +} + +/* + * baud_from_bval -- + * Return the baud rate using the standard defines. + */ +u_long +baud_from_bval(sp) + SCR *sp; +{ + speed_t v; + + switch (v = cfgetospeed(&sp->gp->original_termios)) { + case B50: + return (50); + case B75: + return (75); + case B110: + return (110); + case B134: + return (134); + case B150: + return (150); + case B200: + return (200); + case B300: + return (300); + case B600: + return (600); + case B1200: + return (1200); + case B1800: + return (1800); + case B2400: + return (2400); + case B4800: + return (4800); + case B0: /* Hangup -- ignore. */ + case B9600: + return (9600); + case B19200: + return (19200); + case B38400: + return (38400); + default: + /* + * EXTA and EXTB aren't required by POSIX 1003.1, and + * are almost certainly the same as some of the above + * values, so they can't be part of the case statement. + */ +#ifdef EXTA + if (v == EXTA) + return (19200); +#endif +#ifdef EXTB + if (v == EXTB) + return (38400); +#endif +#ifdef B57600 + if (v == B57600) + return (57600); +#endif +#ifdef B115200 + if (v == B115200) + return (115200); +#endif + msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v); + return (9600); + } +} + +/* + * v_strdup -- + * Strdup for wide character strings with an associated length. + */ +CHAR_T * +v_strdup(sp, str, len) + SCR *sp; + CHAR_T *str; + size_t len; +{ + CHAR_T *copy; + + MALLOC(sp, copy, CHAR_T *, len); + if (copy == NULL) + return (NULL); + memmove(copy, str, len * sizeof(CHAR_T)); + return (copy); +} diff --git a/usr.bin/vi/vi.1 b/usr.bin/vi/vi.1 new file mode 100644 index 000000000000..7c39b10c5ae2 --- /dev/null +++ b/usr.bin/vi/vi.1 @@ -0,0 +1,451 @@ +.\" Copyright (c) 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)vi.1 8.3 (Berkeley) 3/19/94 +.\" +.Dd "March 19, 1994" +.Dt EX/VI 1 +.Os +.Sh NAME +.Nm ex, vi, view +.Nd text editors +.Sh SYNOPSIS +.Nm \&ex +.Op Fl eFlRsv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Nm \&vi +.Op Fl eFlRv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Nm view +.Op Fl eFlRv +.Op Fl c Ar cmd +.Op Fl r Ar file +.Op Fl t Ar tag +.Op Fl w Ar size +.Op Fl x Ar \&aw +.Op Ar "file ..." +.Sh DESCRIPTION +.Nm \&Vi +is a screen oriented text editor. +.Nm \&Ex +is a line-oriented text editor. +.Nm \&Ex +and +.Nm \&vi +are different interfaces to the same program, +and it is possible to switch back and forth during an edit session. +.Nm View +is the equivalent of using the +.Fl R +(read-only) option of +.Nm \&vi . +.Pp +This manual page is the one provided with the +.Nm ex/vi +versions of the +.Nm ex/vi +text editors. +.Nm Ex/vi +are intended as bug-for-bug compatible replacements for the original +Fourth Berkeley Software Distribution (4BSD) +.Nm \&ex +and +.Nm \&vi +programs. +For the rest of this manual page, +.Nm ex/vi +is used only when it's necessary to distinguish it from the historic +implementations of +.Nm ex/vi . +.Pp +This manual page is intended for users already familiar with +.Nm ex/vi . +Anyone else should almost certainly read a good tutorial on the +editor before this manual page. +If you're in an unfamiliar environment, and you absolutely have to +get work done immediately, read the section near the end of this +manual page, entitled FAST STARTUP. +It's probably enough to get you going. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl c +Execute +.Ar cmd +immediately after starting the edit session. +Particularly useful for initial positioning in the file, however +.Ar cmd +is not limited to positioning commands. +This is the POSIX 1003.2 interface for the historic +.Dq "+cmd" +syntax. +.Nm Ex/vi +supports both the old and new syntax. +.It Fl e +Start editing in ex mode, as if the command name were +.Nm \&ex . +.It Fl F +Don't copy the entire file when first starting to edit. +(The default is to make a copy in case someone else modifies +the file during your edit session.) +.It Fl l +List the files that may be recovered using the +.Fl r +option of +.Nm \&vi . +This is the new interface for the historic syntax of the +.Fl r +option without a file argument. +.Nm Ex/vi +supports both the old and new syntax. +.It Fl R +Start editing in read-only mode, as if the command name was +.Nm view , +or the readonly option was set. +.It Fl r +Recover the specified file. +.It Fl s +Enter batch mode; applicable only to +.Nm \&ex +edit sessions. +Batch mode is useful when running +.Nm \&ex +scripts. +Prompts, informative messages and other user oriented message +are turned off, +and no startup files or environmental variables are read. +This is the POSIX 1003.2 interface for the historic +.Dq \&\- +argument. +.Nm \&Ex/vi +supports both the old and new syntax. +.It Fl t +Start editing at the specified tag. +(See +.Xr ctags 1 ). +.It Fl w +Set the initial window size to the specified number of lines. +.It Fl v +Start editing in vi mode, as if the command name was +.Nm \&vi +or +.Nm view . +.It Fl x +Reserved for X11 interfaces. +.Em "No X11 support is currently implemented." +.El +.Pp +.Nm Ex/vi +exit 0 on success, and greater than 0 if an error occurs. +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width XXXX -compact +.It Ev COLUMNS +The number of columns on the screen. +This value overrides any system or terminal specific values. +If the COLUMNS environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy columns +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev EXINIT +A list of +.Nm \&ex +startup commands. +.It Ev HOME +The user's home directory, used as the initial directory path +for the startup +.Pa $HOME/.exrc +file. +This value is also used as the default directory for the +.Nm \&vi +.Sy \&cd +command. +.It Ev LINES +The number of rows on the screen. +This value overrides any system or terminal specific values. +If the LINES environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy lines +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev SHELL +The user's shell of choice (see also the +.Sy shell +option). +.It Ev TERM +The user's terminal type. +The default is the type +.Dq unknown . +If the TERM environmental variable is not set when +.Nm ex/vi +runs, or the +.Sy term +option is explicitly reset by the user, +.Nm ex/vi +enters the value into the environment. +.It Ev TMPDIR +The location used to stored temporary files (see also the +.Sy directory +option). +.El +.Sh SET OPTIONS +#include <set.opt.roff> +.Sh FAST STARTUP +This section will tell you the minimum amount that you need to +do simple editing tasks using +.Nm \&vi . +If you've never used any screen editor before, you're likely to have +problems even with this simple introduction. +In that case you should find someone that already knows +.Nm \&vi +and have them walk you through this section. +.Pp +.Nm \&Vi +is a screen editor. +This means that it takes up almost the entire screen, displaying part +of the file on each screen line, except for the last line of the screen. +The last line of the screen is used for you to give commands to +.Nm \&vi , +and for +.Nm \&vi +to give information to you. +.Pp +The other fact that you need to understand is that +.Nm \&vi +is a modeful editor, i.e. you are either entering text or you +are executing commands, and you have to be in the right mode +to do one or the other. +You will be in command mode when you first start editing a file. +There are commands that switch you into input mode. +There is only one key that takes you out of input mode, +and that is the <escape> key. +(Key names are written using less-than and greater-than signs, e.g. +<escape> means the +.Dq escape +key, usually labeled +.Dq esc +on your terminal's keyboard.) +If you're ever confused as to which mode you're in, +keep entering the <escape> key until +.Nm \&vi +beeps at you. +(Generally, +.Nm \&vi +will beep at you if you try and do something that's not allowed. +It will also display error messages.) +.Pp +To start editing a file, enter the command +.Dq Li "vi file_name<carriage-return>" . +The command you should enter as soon as you start editing is +.Dq Li ":set verbose showmode<carriage-return>" . +This will make the editor give you verbose error messages and display +the current mode at the bottom of the screen. +.Pp +The commands to move around the file are: +.Bl -tag -width XXXX -compact +.It Sy h +Move the cursor left one character. +.It Sy j +Move the cursor down one line. +.It Sy k +Move the cursor up one line. +.It Sy l +Move the cursor right one character. +.It Sy <cursor-arrows> +The cursor arrow keys should work, too. +.It Sy /text<carriage-return> +Search for the string +.Dq text +in the file, and move the cursor to its first character. +.El +.Pp +The commands to enter new text are: +.Bl -tag -width XXXX -compact +.It Sy a +Append new text, +.Em after +the cursor. +.It Sy i +Insert new text, +.Em before +the cursor. +.It Sy o +Open a new line below the line the cursor is on, and start +entering text. +.It Sy O +Open a new line above the line the cursor is on, and start +entering text. +.It Sy <escape> +Once you've entered input mode using the one of the +.Sy \&a , +.Sy \&i , +.Sy \&O , +or +.Sy \&o +commands, use +.Sy <escape> +to quit entering text and return to command mode. +.El +.Pp +The commands to copy text are: +.Bl -tag -width XXXX -compact +.It Sy yy +Copy the line the cursor is on. +.It Sy p +Append the copied line after the line the cursor is on. +.El +.Pp +The commands to delete text are: +.Bl -tag -width XXXX -compact +.It Sy dd +Delete the line the cursor is on. +.It Sy x +Delete the character the cursor is on. +.El +.Pp +The commands to write the file are: +.Bl -tag -width XXXX -compact +.It Sy :w<carriage-return> +Write the file back to the file with the name that you originally used +as an argument on the +.Nm \&vi +command line. +.It Sy :w file_name<carriage-return> +Write the file back to the file with the name +.Dq file_name . +.El +.Pp +The commands to quit editing and exit the editor are: +.Bl -tag -width XXXX -compact +.It Sy :q<carriage-return> +Quit editing and leave vi (if you've modified the file, but not +saved your changes, +.Nm \&vi +will refuse to quit). +.It Sy :q!<carriage-return> +Quit, discarding any modifications that you may have made. +.El +.Pp +One final caution. +Unusual characters can take up more than one column on the screen, +and long lines can take up more than a single screen line. +The above commands work on +.Dq physical +characters and lines, i.e. they affect the entire line no matter +how many screen lines it takes up and the entire character no matter +how many screen columns it takes up. +.Sh BUGS +See the file +.Pa vi/docs/bugs.current +for a list of the known bugs in this version. +.Sh FILES +.Bl -tag -width /var/tmp/vi.recover -compact +.It Pa /bin/sh +The default user shell. +.It Pa /etc/vi.exrc +System-wide vi startup file. +.It Pa /tmp +Temporary file directory. +.It Pa /var/tmp/vi.recover +Recovery file directory. +.It Pa $HOME/.exrc +user's home directory startup file. +.It Pa .exrc +local directory startup file. +.El +.Sh SEE ALSO +.Xr ctags 1 , +.Xr more 1 , +.Xr curses 3 , +.Xr dbopen 3 +.sp +The +.Dq "Vi Quick Reference" +card. +.sp +.Dq "An Introduction to Display Editing with Vi" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.sp +.Dq "Edit: A tutorial" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.sp +.Dq "\&Ex Reference Manual (Version 3.7)" , +found in the +.Dq "UNIX User's Manual Supplementary Documents" . +.Pp +.Nm Nroff/troff +source for the previous three documents are distributed with +.Nm ex/vi +in the +.Pa vi/docs/USD.doc +directory of the +.Nm ex/vi +source code. +.sp +The files +.Dq autowrite , +.Dq input , +.Dq quoting , +and +.Dq structures , +found in the +.Pa vi/docs/internals +directory of the +.Nm ex/vi +source code. +.Sh HISTORY +The +.Nm ex/vi +replacements for the +.Nm ex/vi +editor first appeared in 4.4BSD. +.Sh STANDARDS +.Nm \&Ex/vi +is close to IEEE Std1003.2 (``POSIX''). +That document differs from historical +.Nm ex/vi +practice in several places; there are changes to be made on both sides. diff --git a/usr.bin/vi/vi.h b/usr.bin/vi/vi.h new file mode 100644 index 000000000000..a979925c2b05 --- /dev/null +++ b/usr.bin/vi/vi.h @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vi.h 8.33 (Berkeley) 1/9/94 + */ + +/* System includes. */ +#include <queue.h> /* Required by screen.h. */ +#include <sys/time.h> /* Required by screen.h. */ + +#include <bitstring.h> /* Required by screen.h. */ +#include <limits.h> /* Required by screen.h. */ +#include <signal.h> /* Required by screen.h. */ +#include <stdio.h> /* Required by screen.h. */ +#include <termios.h> /* Required by gs.h. */ + +/* + * Required by screen.h. This is the first include that can pull + * in "compat.h". Should be after every other system include. + */ +#include <regex.h> + +/* + * Forward structure declarations. Not pretty, but the include files + * are far too interrelated for a clean solution. + */ +typedef struct _cb CB; +typedef struct _ch CH; +typedef struct _chname CHNAME; +typedef struct _excmdarg EXCMDARG; +typedef struct _exf EXF; +typedef struct _fref FREF; +typedef struct _gs GS; +typedef struct _ibuf IBUF; +typedef struct _mark MARK; +typedef struct _msg MSG; +typedef struct _option OPTION; +typedef struct _optlist OPTLIST; +typedef struct _scr SCR; +typedef struct _script SCRIPT; +typedef struct _seq SEQ; +typedef struct _tag TAG; +typedef struct _tagf TAGF; +typedef struct _text TEXT; + +/* + * Fundamental character types. + * + * CHAR_T An integral type that can hold any character. + * ARG_CHAR_T The type of a CHAR_T when passed as an argument using + * traditional promotion rules. It should also be able + * to be compared against any CHAR_T for equality without + * problems. + * MAX_CHAR_T The maximum value of any character. + * + * If no integral type can hold a character, don't even try the port. + */ +typedef u_char CHAR_T; +typedef u_int ARG_CHAR_T; +#define MAX_CHAR_T 0xff + +/* The maximum number of columns any character can take up on a screen. */ +#define MAX_CHARACTER_COLUMNS 4 + +/* + * Local includes. + */ +#include <db.h> /* Required by exf.h; includes compat.h. */ + +#include "search.h" /* Required by screen.h. */ +#include "args.h" /* Required by options.h. */ +#include "options.h" /* Required by screen.h. */ +#include "term.h" /* Required by screen.h. */ + +#include "msg.h" /* Required by gs.h. */ +#include "cut.h" /* Required by gs.h. */ +#include "gs.h" /* Required by screen.h. */ +#include "screen.h" /* Required by exf.h. */ +#include "mark.h" /* Required by exf.h. */ +#include "exf.h" +#include "log.h" +#include "mem.h" + +#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */ +#define EXCOOKIE sp +int ex_fflush __P((SCR *)); +int ex_printf __P((SCR *, const char *, ...)); +FILE *fwopen __P((SCR *, void *)); +#else +#define EXCOOKIE sp->stdfp +#define ex_fflush fflush +#define ex_printf fprintf +#endif + +/* Macros to set/clear/test flags. */ +#define F_SET(p, f) (p)->flags |= (f) +#define F_CLR(p, f) (p)->flags &= ~(f) +#define F_ISSET(p, f) ((p)->flags & (f)) + +#define LF_INIT(f) flags = (f) +#define LF_SET(f) flags |= (f) +#define LF_CLR(f) flags &= ~(f) +#define LF_ISSET(f) (flags & (f)) + +/* + * XXX + * MIN/MAX have traditionally been in <sys/param.h>. Don't + * try to get them from there, it's just not worth the effort. + */ +#ifndef MAX +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* Function prototypes that don't seem to belong anywhere else. */ +u_long baud_from_bval __P((SCR *)); +char *charname __P((SCR *, ARG_CHAR_T)); +void busy_off __P((SCR *)); +void busy_on __P((SCR *, int, char const *)); +int nonblank __P((SCR *, EXF *, recno_t, size_t *)); +void set_alt_name __P((SCR *, char *)); +int set_window_size __P((SCR *, u_int, int)); +int status __P((SCR *, EXF *, recno_t, int)); +char *tail __P((char *)); +CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t)); + +#ifdef DEBUG +void TRACE __P((SCR *, const char *, ...)); +#endif + +/* Digraphs (not currently real). */ +int digraph __P((SCR *, int, int)); +int digraph_init __P((SCR *)); +void digraph_save __P((SCR *, int)); diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c new file mode 100644 index 000000000000..ec76a0668cf9 --- /dev/null +++ b/usr.bin/vi/xaw/xaw_screen.c @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)xaw_screen.c 8.4 (Berkeley) 1/11/94"; +#endif /* not lint */ + +#include "vi.h" + +/* + * xaw_init -- + * Athena widget screen initialization. + */ +int +xaw_screen_init(sp) + SCR *sp; +{ + msgq(sp, M_ERR, "The Athena widget screen not yet implemented."); + return (1); +} + +/* + * xaw_screen_copy -- + * Copy to a new screen. + */ +int +xaw_screen_copy(orig, sp) + SCR *orig, *sp; +{ + return (0); +} + +/* + * xaw_screen_end -- + * End a screen. + */ +int +xaw_screen_end(sp) + SCR *sp; +{ + return (0); +} + +/* + * xaw -- + * Main vi Athena widget screen loop. + */ +int +xaw(sp, ep, spp) + SCR *sp, **spp; + EXF *ep; +{ + *spp = NULL; + return (0); +} |
