summaryrefslogtreecommitdiffstats
path: root/usr.bin/more
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:33:43 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-27 12:33:43 +0000
commitf9ab90d9d6d02989a075d0f0074496d5b1045e4b (patch)
treeadd7e996bac5289cdc55e6935750c352505560a9 /usr.bin/more
parentbe22b15ae2ff8d7fe06b6e14fddf0c5b444a95da (diff)
downloadFreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.zip
FreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.tar.gz
BSD 4.4 Lite Usr.bin Sources
Diffstat (limited to 'usr.bin/more')
-rw-r--r--usr.bin/more/Makefile15
-rw-r--r--usr.bin/more/ch.c454
-rw-r--r--usr.bin/more/command.c655
-rw-r--r--usr.bin/more/decode.c201
-rw-r--r--usr.bin/more/help.c49
-rw-r--r--usr.bin/more/input.c241
-rw-r--r--usr.bin/more/less.h87
-rw-r--r--usr.bin/more/line.c508
-rw-r--r--usr.bin/more/linenum.c383
-rw-r--r--usr.bin/more/main.c367
-rw-r--r--usr.bin/more/more.1298
-rw-r--r--usr.bin/more/more.help39
-rw-r--r--usr.bin/more/option.c128
-rw-r--r--usr.bin/more/os.c283
-rw-r--r--usr.bin/more/output.c252
-rw-r--r--usr.bin/more/pathnames.h38
-rw-r--r--usr.bin/more/position.c163
-rw-r--r--usr.bin/more/prim.c834
-rw-r--r--usr.bin/more/screen.c587
-rw-r--r--usr.bin/more/signal.c220
-rw-r--r--usr.bin/more/tags.c205
-rw-r--r--usr.bin/more/ttyin.c79
22 files changed, 6086 insertions, 0 deletions
diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile
new file mode 100644
index 0000000..58f9e81
--- /dev/null
+++ b/usr.bin/more/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= more
+CFLAGS+=-I${.CURDIR}
+SRCS= ch.c command.c decode.c help.c input.c line.c linenum.c main.c \
+ option.c os.c output.c position.c prim.c screen.c signal.c tags.c \
+ ttyin.c
+DPADD= ${LIBTERM} ${LIBCOMPAT}
+LDADD= -ltermcap -lcompat
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/more.help \
+ ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/more/ch.c b/usr.bin/more/ch.c
new file mode 100644
index 0000000..668e601
--- /dev/null
+++ b/usr.bin/more/ch.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Low level character input from the input file.
+ * We use these special purpose routines which optimize moving
+ * both forward and backward from the current read pointer.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <less.h>
+
+int file = -1; /* File descriptor of the input file */
+
+/*
+ * Pool of buffers holding the most recently used blocks of the input file.
+ */
+struct buf {
+ struct buf *next, *prev;
+ long block;
+ int datasize;
+ char data[BUFSIZ];
+};
+int nbufs;
+
+/*
+ * The buffer pool is kept as a doubly-linked circular list, in order from
+ * most- to least-recently used. The circular list is anchored by buf_anchor.
+ */
+#define END_OF_CHAIN ((struct buf *)&buf_anchor)
+#define buf_head buf_anchor.next
+#define buf_tail buf_anchor.prev
+
+static struct {
+ struct buf *next, *prev;
+} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
+
+extern int ispipe, cbufs, sigs;
+
+/*
+ * Current position in file.
+ * Stored as a block number and an offset into the block.
+ */
+static long ch_block;
+static int ch_offset;
+
+/* Length of file, needed if input is a pipe. */
+static off_t ch_fsize;
+
+/* Number of bytes read, if input is standard input (a pipe). */
+static off_t last_piped_pos;
+
+/*
+ * Get the character pointed to by the read pointer. ch_get() is a macro
+ * which is more efficient to call than fch_get (the function), in the usual
+ * case that the block desired is at the head of the chain.
+ */
+#define ch_get() \
+ ((buf_head->block == ch_block && \
+ ch_offset < buf_head->datasize) ? \
+ buf_head->data[ch_offset] : fch_get())
+
+static
+fch_get()
+{
+ extern int bs_mode;
+ register struct buf *bp;
+ register int n, ch;
+ register char *p, *t;
+ off_t pos, lseek();
+
+ /* look for a buffer holding the desired block. */
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == ch_block) {
+ if (ch_offset >= bp->datasize)
+ /*
+ * Need more data in this buffer.
+ */
+ goto read_more;
+ /*
+ * On a pipe, we don't sort the buffers LRU
+ * because this can cause gaps in the buffers.
+ * For example, suppose we've got 12 1K buffers,
+ * and a 15K input stream. If we read the first 12K
+ * sequentially, then jump to line 1, then jump to
+ * the end, the buffers have blocks 0,4,5,6,..,14.
+ * If we then jump to line 1 again and try to
+ * read sequentially, we're out of luck when we
+ * get to block 1 (we'd get the "pipe error" below).
+ * To avoid this, we only sort buffers on a pipe
+ * when we actually READ the data, not when we
+ * find it already buffered.
+ */
+ if (ispipe)
+ return(bp->data[ch_offset]);
+ goto found;
+ }
+ /*
+ * Block is not in a buffer. Take the least recently used buffer
+ * and read the desired block into it. If the LRU buffer has data
+ * in it, and input is a pipe, then try to allocate a new buffer first.
+ */
+ if (ispipe && buf_tail->block != (long)(-1))
+ (void)ch_addbuf(1);
+ bp = buf_tail;
+ bp->block = ch_block;
+ bp->datasize = 0;
+
+read_more:
+ pos = (ch_block * BUFSIZ) + bp->datasize;
+ if (ispipe) {
+ /*
+ * The data requested should be immediately after
+ * the last data read from the pipe.
+ */
+ if (pos != last_piped_pos) {
+ error("pipe error");
+ quit();
+ }
+ } else
+ (void)lseek(file, pos, L_SET);
+
+ /*
+ * Read the block.
+ * If we read less than a full block, we just return the
+ * partial block and pick up the rest next time.
+ */
+ n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
+ if (n == READ_INTR)
+ return (EOI);
+ if (n < 0) {
+ error("read error");
+ quit();
+ }
+ if (ispipe)
+ last_piped_pos += n;
+
+ p = &bp->data[bp->datasize];
+ bp->datasize += n;
+
+ /*
+ * Set an EOI marker in the buffered data itself. Then ensure the
+ * data is "clean": there are no extra EOI chars in the data and
+ * that the "meta" bit (the 0200 bit) is reset in each char;
+ * also translate \r\n sequences to \n if -u flag not set.
+ */
+ if (n == 0) {
+ ch_fsize = pos;
+ bp->data[bp->datasize++] = EOI;
+ }
+
+ if (bs_mode) {
+ for (p = &bp->data[bp->datasize]; --n >= 0;) {
+ *--p &= 0177;
+ if (*p == EOI)
+ *p = 0200;
+ }
+ }
+ else {
+ for (t = p; --n >= 0; ++p) {
+ ch = *p & 0177;
+ if (ch == '\r' && n && (p[1] & 0177) == '\n') {
+ ++p;
+ *t++ = '\n';
+ }
+ else
+ *t++ = (ch == EOI) ? 0200 : ch;
+ }
+ if (p != t) {
+ bp->datasize -= p - t;
+ if (ispipe)
+ last_piped_pos -= p - t;
+ }
+ }
+
+found:
+ if (buf_head != bp) {
+ /*
+ * Move the buffer to the head of the buffer chain.
+ * This orders the buffer chain, most- to least-recently used.
+ */
+ bp->next->prev = bp->prev;
+ bp->prev->next = bp->next;
+
+ bp->next = buf_head;
+ bp->prev = END_OF_CHAIN;
+ buf_head->prev = bp;
+ buf_head = bp;
+ }
+
+ if (ch_offset >= bp->datasize)
+ /*
+ * After all that, we still don't have enough data.
+ * Go back and try again.
+ */
+ goto read_more;
+
+ return(bp->data[ch_offset]);
+}
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+static
+buffered(block)
+ long block;
+{
+ register struct buf *bp;
+
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == block)
+ return(1);
+ return(0);
+}
+
+/*
+ * Seek to a specified position in the file.
+ * Return 0 if successful, non-zero if can't seek there.
+ */
+ch_seek(pos)
+ register off_t pos;
+{
+ long new_block;
+
+ new_block = pos / BUFSIZ;
+ if (!ispipe || pos == last_piped_pos || buffered(new_block)) {
+ /*
+ * Set read pointer.
+ */
+ ch_block = new_block;
+ ch_offset = pos % BUFSIZ;
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * Seek to the end of the file.
+ */
+ch_end_seek()
+{
+ off_t ch_length();
+
+ if (!ispipe)
+ return(ch_seek(ch_length()));
+
+ /*
+ * Do it the slow way: read till end of data.
+ */
+ while (ch_forw_get() != EOI)
+ if (sigs)
+ return(1);
+ return(0);
+}
+
+/*
+ * Seek to the beginning of the file, or as close to it as we can get.
+ * We may not be able to seek there if input is a pipe and the
+ * beginning of the pipe is no longer buffered.
+ */
+ch_beg_seek()
+{
+ register struct buf *bp, *firstbp;
+
+ /*
+ * Try a plain ch_seek first.
+ */
+ if (ch_seek((off_t)0) == 0)
+ return(0);
+
+ /*
+ * Can't get to position 0.
+ * Look thru the buffers for the one closest to position 0.
+ */
+ firstbp = bp = buf_head;
+ if (bp == END_OF_CHAIN)
+ return(1);
+ while ((bp = bp->next) != END_OF_CHAIN)
+ if (bp->block < firstbp->block)
+ firstbp = bp;
+ ch_block = firstbp->block;
+ ch_offset = 0;
+ return(0);
+}
+
+/*
+ * Return the length of the file, if known.
+ */
+off_t
+ch_length()
+{
+ off_t lseek();
+
+ if (ispipe)
+ return(ch_fsize);
+ return((off_t)(lseek(file, (off_t)0, L_XTND)));
+}
+
+/*
+ * Return the current position in the file.
+ */
+off_t
+ch_tell()
+{
+ return(ch_block * BUFSIZ + ch_offset);
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+ch_forw_get()
+{
+ register int c;
+
+ c = ch_get();
+ if (c != EOI && ++ch_offset >= BUFSIZ) {
+ ch_offset = 0;
+ ++ch_block;
+ }
+ return(c);
+}
+
+/*
+ * Pre-decrement the read pointer and get the new current char.
+ */
+ch_back_get()
+{
+ if (--ch_offset < 0) {
+ if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) {
+ ch_offset = 0;
+ return(EOI);
+ }
+ ch_offset = BUFSIZ - 1;
+ ch_block--;
+ }
+ return(ch_get());
+}
+
+/*
+ * Allocate buffers.
+ * Caller wants us to have a total of at least want_nbufs buffers.
+ * keep==1 means keep the data in the current buffers;
+ * otherwise discard the old data.
+ */
+ch_init(want_nbufs, keep)
+ int want_nbufs;
+ int keep;
+{
+ register struct buf *bp;
+ char message[80];
+
+ cbufs = nbufs;
+ if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs)) {
+ /*
+ * Cannot allocate enough buffers.
+ * If we don't have ANY, then quit.
+ * Otherwise, just report the error and return.
+ */
+ (void)sprintf(message, "cannot allocate %d buffers",
+ want_nbufs - nbufs);
+ error(message);
+ if (nbufs == 0)
+ quit();
+ return;
+ }
+
+ if (keep)
+ return;
+
+ /*
+ * We don't want to keep the old data,
+ * so initialize all the buffers now.
+ */
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ bp->block = (long)(-1);
+ last_piped_pos = (off_t)0;
+ ch_fsize = NULL_POSITION;
+ (void)ch_seek((off_t)0);
+}
+
+/*
+ * Allocate some new buffers.
+ * The buffers are added to the tail of the buffer chain.
+ */
+ch_addbuf(nnew)
+ int nnew;
+{
+ register struct buf *bp;
+ register struct buf *newbufs;
+ char *calloc();
+
+ /*
+ * We don't have enough buffers.
+ * Allocate some new ones.
+ */
+ newbufs = (struct buf *)calloc((u_int)nnew, sizeof(struct buf));
+ if (newbufs == NULL)
+ return(1);
+
+ /*
+ * Initialize the new buffers and link them together.
+ * Link them all onto the tail of the buffer list.
+ */
+ nbufs += nnew;
+ cbufs = nbufs;
+ for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) {
+ bp->next = bp + 1;
+ bp->prev = bp - 1;
+ bp->block = (long)(-1);
+ }
+ newbufs[nnew-1].next = END_OF_CHAIN;
+ newbufs[0].prev = buf_tail;
+ buf_tail->next = &newbufs[0];
+ buf_tail = &newbufs[nnew-1];
+ return(0);
+}
diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c
new file mode 100644
index 0000000..51f5847
--- /dev/null
+++ b/usr.bin/more/command.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)command.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <less.h>
+#include "pathnames.h"
+
+#define NO_MCA 0
+#define MCA_DONE 1
+#define MCA_MORE 2
+
+extern int erase_char, kill_char, werase_char;
+extern int ispipe;
+extern int sigs;
+extern int quit_at_eof;
+extern int hit_eof;
+extern int sc_width;
+extern int sc_height;
+extern int sc_window;
+extern int curr_ac;
+extern int ac;
+extern int quitting;
+extern int scroll;
+extern int screen_trashed; /* The screen has been overwritten */
+
+static char cmdbuf[120]; /* Buffer for holding a multi-char command */
+static char *cp; /* Pointer into cmdbuf */
+static int cmd_col; /* Current column of the multi-char command */
+static int longprompt; /* if stat command instead of prompt */
+static int mca; /* The multicharacter command (action) */
+static int last_mca; /* The previous mca */
+static int number; /* The number typed by the user */
+static int wsearch; /* Search for matches (1) or non-matches (0) */
+
+#define CMD_RESET cp = cmdbuf /* reset command buffer to empty */
+#define CMD_EXEC lower_left(); flush()
+
+/* backspace in command buffer. */
+static
+cmd_erase()
+{
+ /*
+ * backspace past beginning of the string: this usually means
+ * abort the command.
+ */
+ if (cp == cmdbuf)
+ return(1);
+
+ /* erase an extra character, for the carat. */
+ if (CONTROL_CHAR(*--cp)) {
+ backspace();
+ --cmd_col;
+ }
+
+ backspace();
+ --cmd_col;
+ return(0);
+}
+
+/* set up the display to start a new multi-character command. */
+start_mca(action, prompt)
+ int action;
+ char *prompt;
+{
+ lower_left();
+ clear_eol();
+ putstr(prompt);
+ cmd_col = strlen(prompt);
+ mca = action;
+}
+
+/*
+ * process a single character of a multi-character command, such as
+ * a number, or the pattern of a search command.
+ */
+static
+cmd_char(c)
+ int c;
+{
+ if (c == erase_char)
+ return(cmd_erase());
+ /* in this order, in case werase == erase_char */
+ if (c == werase_char) {
+ if (cp > cmdbuf) {
+ while (isspace(cp[-1]) && !cmd_erase());
+ while (!isspace(cp[-1]) && !cmd_erase());
+ while (isspace(cp[-1]) && !cmd_erase());
+ }
+ return(cp == cmdbuf);
+ }
+ if (c == kill_char) {
+ while (!cmd_erase());
+ return(1);
+ }
+ /*
+ * No room in the command buffer, or no room on the screen;
+ * {{ Could get fancy here; maybe shift the displayed line
+ * and make room for more chars, like ksh. }}
+ */
+ if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3)
+ bell();
+ else {
+ *cp++ = c;
+ if (CONTROL_CHAR(c)) {
+ putchr('^');
+ cmd_col++;
+ c = CARAT_CHAR(c);
+ }
+ putchr(c);
+ cmd_col++;
+ }
+ return(0);
+}
+
+prompt()
+{
+ extern int linenums, short_file;
+ extern char *current_name, *firstsearch, *next_name;
+ off_t len, pos, ch_length(), position(), forw_line();
+ char pbuf[40];
+
+ /*
+ * if nothing is displayed yet, display starting from line 1;
+ * if search string provided, go there instead.
+ */
+ if (position(TOP) == NULL_POSITION) {
+ if (forw_line((off_t)0) == NULL_POSITION)
+ return(0);
+ if (!firstsearch || !search(1, firstsearch, 1, 1))
+ jump_back(1);
+ }
+ else if (screen_trashed)
+ repaint();
+
+ /* if no -e flag and we've hit EOF on the last file, quit. */
+ if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac)
+ quit();
+
+ /* select the proper prompt and display it. */
+ lower_left();
+ clear_eol();
+ if (longprompt) {
+ so_enter();
+ putstr(current_name);
+ putstr(":");
+ if (!ispipe) {
+ (void)sprintf(pbuf, " file %d/%d", curr_ac + 1, ac);
+ putstr(pbuf);
+ }
+ if (linenums) {
+ (void)sprintf(pbuf, " line %d", currline(BOTTOM));
+ putstr(pbuf);
+ }
+ if ((pos = position(BOTTOM)) != NULL_POSITION) {
+ (void)sprintf(pbuf, " byte %qd", pos);
+ putstr(pbuf);
+ if (!ispipe && (len = ch_length())) {
+ (void)sprintf(pbuf, "/%qd pct %qd%%",
+ len, ((100 * pos) / len));
+ putstr(pbuf);
+ }
+ }
+ so_exit();
+ longprompt = 0;
+ }
+ else {
+ so_enter();
+ putstr(current_name);
+ if (hit_eof)
+ if (next_name) {
+ putstr(": END (next file: ");
+ putstr(next_name);
+ putstr(")");
+ }
+ else
+ putstr(": END");
+ else if (!ispipe &&
+ (pos = position(BOTTOM)) != NULL_POSITION &&
+ (len = ch_length())) {
+ (void)sprintf(pbuf, " (%qd%%)", ((100 * pos) / len));
+ putstr(pbuf);
+ }
+ so_exit();
+ }
+ return(1);
+}
+
+/* get command character. */
+static
+getcc()
+{
+ extern int cmdstack;
+ int ch;
+ off_t position();
+
+ /* left over from error() routine. */
+ if (cmdstack) {
+ ch = cmdstack;
+ cmdstack = NULL;
+ return(ch);
+ }
+ if (cp > cmdbuf && position(TOP) == NULL_POSITION) {
+ /*
+ * Command is incomplete, so try to complete it.
+ * There are only two cases:
+ * 1. We have "/string" but no newline. Add the \n.
+ * 2. We have a number but no command. Treat as #g.
+ * (This is all pretty hokey.)
+ */
+ if (mca != A_DIGIT)
+ /* Not a number; must be search string */
+ return('\n');
+ else
+ /* A number; append a 'g' */
+ return('g');
+ }
+ return(getchr());
+}
+
+/* execute a multicharacter command. */
+static
+exec_mca()
+{
+ extern int file;
+ extern char *tagfile;
+ register char *p;
+ char *glob();
+
+ *cp = '\0';
+ CMD_EXEC;
+ switch (mca) {
+ case A_F_SEARCH:
+ (void)search(1, cmdbuf, number, wsearch);
+ break;
+ case A_B_SEARCH:
+ (void)search(0, cmdbuf, number, wsearch);
+ break;
+ case A_EXAMINE:
+ for (p = cmdbuf; isspace(*p); ++p);
+ (void)edit(glob(p));
+ break;
+ case A_TAGFILE:
+ for (p = cmdbuf; isspace(*p); ++p);
+ findtag(p);
+ if (tagfile == NULL)
+ break;
+ if (edit(tagfile))
+ (void)tagsearch();
+ break;
+ }
+}
+
+/* add a character to a multi-character command. */
+static
+mca_char(c)
+ int c;
+{
+ switch (mca) {
+ case 0: /* not in a multicharacter command. */
+ case A_PREFIX: /* in the prefix of a command. */
+ return(NO_MCA);
+ case A_DIGIT:
+ /*
+ * Entering digits of a number.
+ * Terminated by a non-digit.
+ */
+ if (!isascii(c) || !isdigit(c) &&
+ c != erase_char && c != kill_char && c != werase_char) {
+ /*
+ * Not part of the number.
+ * Treat as a normal command character.
+ */
+ *cp = '\0';
+ number = atoi(cmdbuf);
+ CMD_RESET;
+ mca = 0;
+ return(NO_MCA);
+ }
+ break;
+ }
+
+ /*
+ * Any other multicharacter command
+ * is terminated by a newline.
+ */
+ if (c == '\n' || c == '\r') {
+ exec_mca();
+ return(MCA_DONE);
+ }
+
+ /* append the char to the command buffer. */
+ if (cmd_char(c))
+ return(MCA_DONE);
+
+ return(MCA_MORE);
+}
+
+/*
+ * Main command processor.
+ * Accept and execute commands until a quit command, then return.
+ */
+commands()
+{
+ register int c;
+ register int action;
+
+ last_mca = 0;
+ scroll = (sc_height + 1) / 2;
+
+ for (;;) {
+ mca = 0;
+ number = 0;
+
+ /*
+ * See if any signals need processing.
+ */
+ if (sigs) {
+ psignals();
+ if (quitting)
+ quit();
+ }
+ /*
+ * Display prompt and accept a character.
+ */
+ CMD_RESET;
+ if (!prompt()) {
+ next_file(1);
+ continue;
+ }
+ noprefix();
+ c = getcc();
+
+again: if (sigs)
+ continue;
+
+ /*
+ * If we are in a multicharacter command, call mca_char.
+ * Otherwise we call cmd_decode to determine the
+ * action to be performed.
+ */
+ if (mca)
+ switch (mca_char(c)) {
+ case MCA_MORE:
+ /*
+ * Need another character.
+ */
+ c = getcc();
+ goto again;
+ case MCA_DONE:
+ /*
+ * Command has been handled by mca_char.
+ * Start clean with a prompt.
+ */
+ continue;
+ case NO_MCA:
+ /*
+ * Not a multi-char command
+ * (at least, not anymore).
+ */
+ break;
+ }
+
+ /* decode the command character and decide what to do. */
+ switch (action = cmd_decode(c)) {
+ case A_DIGIT: /* first digit of a number */
+ start_mca(A_DIGIT, ":");
+ goto again;
+ case A_F_SCREEN: /* forward one screen */
+ CMD_EXEC;
+ if (number <= 0 && (number = sc_window) <= 0)
+ number = sc_height - 1;
+ forward(number, 1);
+ break;
+ case A_B_SCREEN: /* backward one screen */
+ CMD_EXEC;
+ if (number <= 0 && (number = sc_window) <= 0)
+ number = sc_height - 1;
+ backward(number, 1);
+ break;
+ case A_F_LINE: /* forward N (default 1) line */
+ CMD_EXEC;
+ forward(number <= 0 ? 1 : number, 0);
+ break;
+ case A_B_LINE: /* backward N (default 1) line */
+ CMD_EXEC;
+ backward(number <= 0 ? 1 : number, 0);
+ break;
+ case A_F_SCROLL: /* forward N lines */
+ CMD_EXEC;
+ if (number > 0)
+ scroll = number;
+ forward(scroll, 0);
+ break;
+ case A_B_SCROLL: /* backward N lines */
+ CMD_EXEC;
+ if (number > 0)
+ scroll = number;
+ backward(scroll, 0);
+ break;
+ case A_FREPAINT: /* flush buffers and repaint */
+ if (!ispipe) {
+ ch_init(0, 0);
+ clr_linenum();
+ }
+ /* FALLTHROUGH */
+ case A_REPAINT: /* repaint the screen */
+ CMD_EXEC;
+ repaint();
+ break;
+ case A_GOLINE: /* go to line N, default 1 */
+ CMD_EXEC;
+ if (number <= 0)
+ number = 1;
+ jump_back(number);
+ break;
+ case A_PERCENT: /* go to percent of file */
+ CMD_EXEC;
+ if (number < 0)
+ number = 0;
+ else if (number > 100)
+ number = 100;
+ jump_percent(number);
+ break;
+ case A_GOEND: /* go to line N, default end */
+ CMD_EXEC;
+ if (number <= 0)
+ jump_forw();
+ else
+ jump_back(number);
+ break;
+ case A_STAT: /* print file name, etc. */
+ longprompt = 1;
+ continue;
+ case A_QUIT: /* exit */
+ quit();
+ case A_F_SEARCH: /* search for a pattern */
+ case A_B_SEARCH:
+ if (number <= 0)
+ number = 1;
+ start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
+ last_mca = mca;
+ wsearch = 1;
+ c = getcc();
+ if (c == '!') {
+ /*
+ * Invert the sense of the search; set wsearch
+ * to 0 and get a new character for the start
+ * of the pattern.
+ */
+ start_mca(action,
+ (action == A_F_SEARCH) ? "!/" : "!?");
+ wsearch = 0;
+ c = getcc();
+ }
+ goto again;
+ case A_AGAIN_SEARCH: /* repeat previous search */
+ if (number <= 0)
+ number = 1;
+ if (wsearch)
+ start_mca(last_mca,
+ (last_mca == A_F_SEARCH) ? "/" : "?");
+ else
+ start_mca(last_mca,
+ (last_mca == A_F_SEARCH) ? "!/" : "!?");
+ CMD_EXEC;
+ (void)search(mca == A_F_SEARCH, (char *)NULL,
+ number, wsearch);
+ break;
+ case A_HELP: /* help */
+ lower_left();
+ clear_eol();
+ putstr("help");
+ CMD_EXEC;
+ help();
+ break;
+ case A_TAGFILE: /* tag a new file */
+ CMD_RESET;
+ start_mca(A_TAGFILE, "Tag: ");
+ c = getcc();
+ goto again;
+ case A_FILE_LIST: /* show list of file names */
+ CMD_EXEC;
+ showlist();
+ repaint();
+ break;
+ case A_EXAMINE: /* edit a new file */
+ CMD_RESET;
+ start_mca(A_EXAMINE, "Examine: ");
+ c = getcc();
+ goto again;
+ case A_VISUAL: /* invoke the editor */
+ if (ispipe) {
+ error("Cannot edit standard input");
+ break;
+ }
+ CMD_EXEC;
+ editfile();
+ ch_init(0, 0);
+ clr_linenum();
+ break;
+ case A_NEXT_FILE: /* examine next file */
+ if (number <= 0)
+ number = 1;
+ next_file(number);
+ break;
+ case A_PREV_FILE: /* examine previous file */
+ if (number <= 0)
+ number = 1;
+ prev_file(number);
+ break;
+ case A_SETMARK: /* set a mark */
+ lower_left();
+ clear_eol();
+ start_mca(A_SETMARK, "mark: ");
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ setmark(c);
+ break;
+ case A_GOMARK: /* go to mark */
+ lower_left();
+ clear_eol();
+ start_mca(A_GOMARK, "goto mark: ");
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ gomark(c);
+ break;
+ case A_PREFIX:
+ /*
+ * The command is incomplete (more chars are needed).
+ * Display the current char so the user knows what's
+ * going on and get another character.
+ */
+ if (mca != A_PREFIX)
+ start_mca(A_PREFIX, "");
+ if (CONTROL_CHAR(c)) {
+ putchr('^');
+ c = CARAT_CHAR(c);
+ }
+ putchr(c);
+ c = getcc();
+ goto again;
+ default:
+ bell();
+ break;
+ }
+ }
+}
+
+editfile()
+{
+ extern char *current_file;
+ static int dolinenumber;
+ static char *editor;
+ int c;
+ char buf[MAXPATHLEN * 2 + 20], *getenv();
+
+ if (editor == NULL) {
+ editor = getenv("EDITOR");
+ /* pass the line number to vi */
+ if (editor == NULL || *editor == '\0') {
+ editor = _PATH_VI;
+ dolinenumber = 1;
+ }
+ else
+ dolinenumber = 0;
+ }
+ if (dolinenumber && (c = currline(MIDDLE)))
+ (void)sprintf(buf, "%s +%d %s", editor, c, current_file);
+ else
+ (void)sprintf(buf, "%s %s", editor, current_file);
+ lsystem(buf);
+}
+
+showlist()
+{
+ extern int sc_width;
+ extern char **av;
+ register int indx, width;
+ int len;
+ char *p;
+
+ if (ac <= 0) {
+ error("No files provided as arguments.");
+ return;
+ }
+ for (width = indx = 0; indx < ac;) {
+ p = strcmp(av[indx], "-") ? av[indx] : "stdin";
+ len = strlen(p) + 1;
+ if (curr_ac == indx)
+ len += 2;
+ if (width + len + 1 >= sc_width) {
+ if (!width) {
+ if (curr_ac == indx)
+ putchr('[');
+ putstr(p);
+ if (curr_ac == indx)
+ putchr(']');
+ ++indx;
+ }
+ width = 0;
+ putchr('\n');
+ continue;
+ }
+ if (width)
+ putchr(' ');
+ if (curr_ac == indx)
+ putchr('[');
+ putstr(p);
+ if (curr_ac == indx)
+ putchr(']');
+ width += len;
+ ++indx;
+ }
+ putchr('\n');
+ error((char *)NULL);
+}
diff --git a/usr.bin/more/decode.c b/usr.bin/more/decode.c
new file mode 100644
index 0000000..83fa624
--- /dev/null
+++ b/usr.bin/more/decode.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)decode.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines to decode user commands.
+ *
+ * This is all table driven.
+ * A command table is a sequence of command descriptors.
+ * Each command descriptor is a sequence of bytes with the following format:
+ * <c1><c2>...<cN><0><action>
+ * The characters c1,c2,...,cN are the command string; that is,
+ * the characters which the user must type.
+ * It is terminated by a null <0> byte.
+ * The byte after the null byte is the action code associated
+ * with the command string.
+ *
+ * The default commands are described by cmdtable.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <less.h>
+
+/*
+ * Command table is ordered roughly according to expected
+ * frequency of use, so the common commands are near the beginning.
+ */
+#define CONTROL(c) ((c)&037)
+
+static char cmdtable[] = {
+ '\r',0, A_F_LINE,
+ '\n',0, A_F_LINE,
+ 'j',0, A_F_LINE,
+ 'k',0, A_B_LINE,
+ 'd',0, A_F_SCROLL,
+ CONTROL('D'),0, A_F_SCROLL,
+ 'u',0, A_B_SCROLL,
+ CONTROL('U'),0, A_B_SCROLL,
+ ' ',0, A_F_SCREEN,
+ 'f',0, A_F_SCREEN,
+ CONTROL('F'),0, A_F_SCREEN,
+ 'b',0, A_B_SCREEN,
+ CONTROL('B'),0, A_B_SCREEN,
+ 'R',0, A_FREPAINT,
+ 'r',0, A_REPAINT,
+ CONTROL('L'),0, A_REPAINT,
+ 'g',0, A_GOLINE,
+ 'p',0, A_PERCENT,
+ '%',0, A_PERCENT,
+ 'G',0, A_GOEND,
+ '0',0, A_DIGIT,
+ '1',0, A_DIGIT,
+ '2',0, A_DIGIT,
+ '3',0, A_DIGIT,
+ '4',0, A_DIGIT,
+ '5',0, A_DIGIT,
+ '6',0, A_DIGIT,
+ '7',0, A_DIGIT,
+ '8',0, A_DIGIT,
+ '9',0, A_DIGIT,
+
+ '=',0, A_STAT,
+ CONTROL('G'),0, A_STAT,
+ '/',0, A_F_SEARCH,
+ '?',0, A_B_SEARCH,
+ 'n',0, A_AGAIN_SEARCH,
+ 'm',0, A_SETMARK,
+ '\'',0, A_GOMARK,
+ 'E',0, A_EXAMINE,
+ 'N',0, A_NEXT_FILE,
+ ':','n',0, A_NEXT_FILE,
+ 'P',0, A_PREV_FILE,
+ ':','p',0, A_PREV_FILE,
+ 'v',0, A_VISUAL,
+
+ 'h',0, A_HELP,
+ 'q',0, A_QUIT,
+ ':','q',0, A_QUIT,
+ ':','t',0, A_TAGFILE,
+ ':', 'a', 0, A_FILE_LIST,
+ 'Z','Z',0, A_QUIT,
+};
+
+char *cmdendtable = cmdtable + sizeof(cmdtable);
+
+#define MAX_CMDLEN 16
+
+static char kbuf[MAX_CMDLEN+1];
+static char *kp = kbuf;
+
+/*
+ * Indicate that we're not in a prefix command
+ * by resetting the command buffer pointer.
+ */
+noprefix()
+{
+ kp = kbuf;
+}
+
+/*
+ * Decode a command character and return the associated action.
+ */
+cmd_decode(c)
+ int c;
+{
+ register int action = A_INVALID;
+
+ /*
+ * Append the new command character to the command string in kbuf.
+ */
+ *kp++ = c;
+ *kp = '\0';
+
+ action = cmd_search(cmdtable, cmdendtable);
+
+ /* This is not a prefix character. */
+ if (action != A_PREFIX)
+ noprefix();
+ return(action);
+}
+
+/*
+ * Search a command table for the current command string (in kbuf).
+ */
+cmd_search(table, endtable)
+ char *table;
+ char *endtable;
+{
+ register char *p, *q;
+
+ for (p = table, q = kbuf; p < endtable; p++, q++) {
+ if (*p == *q) {
+ /*
+ * Current characters match.
+ * If we're at the end of the string, we've found it.
+ * Return the action code, which is the character
+ * after the null at the end of the string
+ * in the command table.
+ */
+ if (*p == '\0')
+ return(p[1]);
+ }
+ else if (*q == '\0') {
+ /*
+ * Hit the end of the user's command,
+ * but not the end of the string in the command table.
+ * The user's command is incomplete.
+ */
+ return(A_PREFIX);
+ } else {
+ /*
+ * Not a match.
+ * Skip ahead to the next command in the
+ * command table, and reset the pointer
+ * to the user's command.
+ */
+ while (*p++ != '\0');
+ q = kbuf-1;
+ }
+ }
+ /*
+ * No match found in the entire command table.
+ */
+ return(A_INVALID);
+}
diff --git a/usr.bin/more/help.c b/usr.bin/more/help.c
new file mode 100644
index 0000000..eeb3795
--- /dev/null
+++ b/usr.bin/more/help.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)help.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <less.h>
+#include "pathnames.h"
+
+help()
+{
+ char cmd[MAXPATHLEN + 20];
+
+ (void)sprintf(cmd, "-more %s", _PATH_HELPFILE);
+ lsystem(cmd);
+}
diff --git a/usr.bin/more/input.c b/usr.bin/more/input.c
new file mode 100644
index 0000000..521bc53
--- /dev/null
+++ b/usr.bin/more/input.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * High level routines dealing with getting lines of input
+ * from the file being viewed.
+ *
+ * When we speak of "lines" here, we mean PRINTABLE lines;
+ * lines processed with respect to the screen width.
+ * We use the term "raw line" to refer to lines simply
+ * delimited by newlines; not processed with respect to screen width.
+ */
+
+#include <sys/types.h>
+#include <less.h>
+
+extern int squeeze;
+extern int sigs;
+extern char *line;
+
+off_t ch_tell();
+
+/*
+ * Get the next line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the NEXT line. The line obtained is the line starting at curr_pos.
+ */
+off_t
+forw_line(curr_pos)
+ off_t curr_pos;
+{
+ off_t new_pos;
+ register int c;
+
+ if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
+ return (NULL_POSITION);
+
+ c = ch_forw_get();
+ if (c == EOI)
+ return (NULL_POSITION);
+
+ prewind();
+ for (;;)
+ {
+ if (sigs)
+ return (NULL_POSITION);
+ if (c == '\n' || c == EOI)
+ {
+ /*
+ * End of the line.
+ */
+ new_pos = ch_tell();
+ break;
+ }
+
+ /*
+ * Append the char to the line and get the next char.
+ */
+ if (pappend(c))
+ {
+ /*
+ * The char won't fit in the line; the line
+ * is too long to print in the screen width.
+ * End the line here.
+ */
+ new_pos = ch_tell() - 1;
+ break;
+ }
+ c = ch_forw_get();
+ }
+ (void) pappend('\0');
+
+ if (squeeze && *line == '\0')
+ {
+ /*
+ * This line is blank.
+ * Skip down to the last contiguous blank line
+ * and pretend it is the one which we are returning.
+ */
+ while ((c = ch_forw_get()) == '\n')
+ if (sigs)
+ return (NULL_POSITION);
+ if (c != EOI)
+ (void) ch_back_get();
+ new_pos = ch_tell();
+ }
+
+ return (new_pos);
+}
+
+/*
+ * Get the previous line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the PREVIOUS line. The line obtained is the one starting at new_pos.
+ */
+off_t
+back_line(curr_pos)
+ off_t curr_pos;
+{
+ off_t new_pos, begin_new_pos;
+ int c;
+
+ if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
+ ch_seek(curr_pos-1))
+ return (NULL_POSITION);
+
+ if (squeeze)
+ {
+ /*
+ * Find out if the "current" line was blank.
+ */
+ (void) ch_forw_get(); /* Skip the newline */
+ c = ch_forw_get(); /* First char of "current" line */
+ (void) ch_back_get(); /* Restore our position */
+ (void) ch_back_get();
+
+ if (c == '\n')
+ {
+ /*
+ * The "current" line was blank.
+ * Skip over any preceeding blank lines,
+ * since we skipped them in forw_line().
+ */
+ while ((c = ch_back_get()) == '\n')
+ if (sigs)
+ return (NULL_POSITION);
+ if (c == EOI)
+ return (NULL_POSITION);
+ (void) ch_forw_get();
+ }
+ }
+
+ /*
+ * Scan backwards until we hit the beginning of the line.
+ */
+ for (;;)
+ {
+ if (sigs)
+ return (NULL_POSITION);
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = ch_tell();
+ break;
+ }
+ }
+
+ /*
+ * Now scan forwards from the beginning of this line.
+ * We keep discarding "printable lines" (based on screen width)
+ * until we reach the curr_pos.
+ *
+ * {{ This algorithm is pretty inefficient if the lines
+ * are much longer than the screen width,
+ * but I don't know of any better way. }}
+ */
+ if (ch_seek(new_pos))
+ return (NULL_POSITION);
+ loop:
+ begin_new_pos = new_pos;
+ prewind();
+
+ do
+ {
+ c = ch_forw_get();
+ if (c == EOI || sigs)
+ return (NULL_POSITION);
+ new_pos++;
+ if (c == '\n')
+ break;
+ if (pappend(c))
+ {
+ /*
+ * Got a full printable line, but we haven't
+ * reached our curr_pos yet. Discard the line
+ * and start a new one.
+ */
+ (void) pappend('\0');
+ (void) ch_back_get();
+ new_pos--;
+ goto loop;
+ }
+ } while (new_pos < curr_pos);
+
+ (void) pappend('\0');
+
+ return (begin_new_pos);
+}
diff --git a/usr.bin/more/less.h b/usr.bin/more/less.h
new file mode 100644
index 0000000..70de6de
--- /dev/null
+++ b/usr.bin/more/less.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)less.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define RECOMP
+
+#define NULL_POSITION ((off_t)(-1))
+
+#define EOI (0)
+#define READ_INTR (-2)
+
+/* Special chars used to tell put_line() to do something special */
+#define UL_CHAR '\201' /* Enter underline mode */
+#define UE_CHAR '\202' /* Exit underline mode */
+#define BO_CHAR '\203' /* Enter boldface mode */
+#define BE_CHAR '\204' /* Exit boldface mode */
+
+#define CONTROL_CHAR(c) (iscntrl(c))
+#define CARAT_CHAR(c) ((c == '\177') ? '?' : (c | 0100))
+
+#define TOP (0)
+#define TOP_PLUS_ONE (1)
+#define BOTTOM (-1)
+#define BOTTOM_PLUS_ONE (-2)
+#define MIDDLE (-3)
+
+#define A_INVALID -1
+
+#define A_AGAIN_SEARCH 1
+#define A_B_LINE 2
+#define A_B_SCREEN 3
+#define A_B_SCROLL 4
+#define A_B_SEARCH 5
+#define A_DIGIT 6
+#define A_EXAMINE 7
+#define A_FREPAINT 8
+#define A_F_LINE 9
+#define A_F_SCREEN 10
+#define A_F_SCROLL 11
+#define A_F_SEARCH 12
+#define A_GOEND 13
+#define A_GOLINE 14
+#define A_GOMARK 15
+#define A_HELP 16
+#define A_NEXT_FILE 17
+#define A_PERCENT 18
+#define A_PREFIX 19
+#define A_PREV_FILE 20
+#define A_QUIT 21
+#define A_REPAINT 22
+#define A_SETMARK 23
+#define A_STAT 24
+#define A_VISUAL 25
+#define A_TAGFILE 26
+#define A_FILE_LIST 27
diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c
new file mode 100644
index 0000000..7634f35
--- /dev/null
+++ b/usr.bin/more/line.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)line.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines to manipulate the "line buffer".
+ * The line buffer holds a line of output as it is being built
+ * in preparation for output to the screen.
+ * We keep track of the PRINTABLE length of the line as it is being built.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <less.h>
+
+static char linebuf[1024]; /* Buffer which holds the current output line */
+static char *curr; /* Pointer into linebuf */
+static int column; /* Printable length, accounting for
+ backspaces, etc. */
+/*
+ * A ridiculously complex state machine takes care of backspaces. The
+ * complexity arises from the attempt to deal with all cases, especially
+ * involving long lines with underlining, boldfacing or whatever. There
+ * are still some cases which will break it.
+ *
+ * There are four states:
+ * LN_NORMAL is the normal state (not in underline mode).
+ * LN_UNDERLINE means we are in underline mode. We expect to get
+ * either a sequence like "_\bX" or "X\b_" to continue
+ * underline mode, or anything else to end underline mode.
+ * LN_BOLDFACE means we are in boldface mode. We expect to get sequences
+ * like "X\bX\b...X\bX" to continue boldface mode, or anything
+ * else to end boldface mode.
+ * LN_UL_X means we are one character after LN_UNDERLINE
+ * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
+ * LN_UL_XB means we are one character after LN_UL_X
+ * (we have gotten the backspace in "_\bX" or "X\b_";
+ * we expect one more ordinary character,
+ * which will put us back in state LN_UNDERLINE).
+ * LN_BO_X means we are one character after LN_BOLDFACE
+ * (we have gotten the 'X' in "X\bX").
+ * LN_BO_XB means we are one character after LN_BO_X
+ * (we have gotten the backspace in "X\bX";
+ * we expect one more 'X' which will put us back
+ * in LN_BOLDFACE).
+ */
+static int ln_state; /* Currently in normal/underline/bold/etc mode? */
+#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */
+#define LN_UNDERLINE 1 /* In underline, need next char */
+#define LN_UL_X 2 /* In underline, got char, need \b */
+#define LN_UL_XB 3 /* In underline, got char & \b, need one more */
+#define LN_BOLDFACE 4 /* In boldface, need next char */
+#define LN_BO_X 5 /* In boldface, got char, need \b */
+#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */
+
+char *line; /* Pointer to the current line.
+ Usually points to linebuf. */
+
+extern int bs_mode;
+extern int tabstop;
+extern int bo_width, be_width;
+extern int ul_width, ue_width;
+extern int sc_width, sc_height;
+
+/*
+ * Rewind the line buffer.
+ */
+prewind()
+{
+ line = curr = linebuf;
+ ln_state = LN_NORMAL;
+ column = 0;
+}
+
+/*
+ * Append a character to the line buffer.
+ * Expand tabs into spaces, handle underlining, boldfacing, etc.
+ * Returns 0 if ok, 1 if couldn't fit in buffer.
+ */
+#define NEW_COLUMN(addon) \
+ if (column + addon + (ln_state ? ue_width : 0) > sc_width) \
+ return(1); \
+ else \
+ column += addon
+
+pappend(c)
+ int c;
+{
+ if (c == '\0') {
+ /*
+ * Terminate any special modes, if necessary.
+ * Append a '\0' to the end of the line.
+ */
+ switch (ln_state) {
+ case LN_UL_X:
+ curr[0] = curr[-1];
+ curr[-1] = UE_CHAR;
+ curr++;
+ break;
+ case LN_BO_X:
+ curr[0] = curr[-1];
+ curr[-1] = BE_CHAR;
+ curr++;
+ break;
+ case LN_UL_XB:
+ case LN_UNDERLINE:
+ *curr++ = UE_CHAR;
+ break;
+ case LN_BO_XB:
+ case LN_BOLDFACE:
+ *curr++ = BE_CHAR;
+ break;
+ }
+ ln_state = LN_NORMAL;
+ *curr = '\0';
+ return(0);
+ }
+
+ if (curr > linebuf + sizeof(linebuf) - 12)
+ /*
+ * Almost out of room in the line buffer.
+ * Don't take any chances.
+ * {{ Linebuf is supposed to be big enough that this
+ * will never happen, but may need to be made
+ * bigger for wide screens or lots of backspaces. }}
+ */
+ return(1);
+
+ if (!bs_mode) {
+ /*
+ * Advance the state machine.
+ */
+ switch (ln_state) {
+ case LN_NORMAL:
+ if (curr <= linebuf + 1
+ || curr[-1] != (char)('H' | 0200))
+ break;
+ column -= 2;
+ if (c == curr[-2])
+ goto enter_boldface;
+ if (c == '_' || curr[-2] == '_')
+ goto enter_underline;
+ curr -= 2;
+ break;
+
+enter_boldface:
+ /*
+ * We have "X\bX" (including the current char).
+ * Switch into boldface mode.
+ */
+ column--;
+ if (column + bo_width + be_width + 1 >= sc_width)
+ /*
+ * Not enough room left on the screen to
+ * enter and exit boldface mode.
+ */
+ return (1);
+
+ if (bo_width > 0 && curr > linebuf + 2
+ && curr[-3] == ' ') {
+ /*
+ * Special case for magic cookie terminals:
+ * if the previous char was a space, replace
+ * it with the "enter boldface" sequence.
+ */
+ curr[-3] = BO_CHAR;
+ column += bo_width-1;
+ } else {
+ curr[-1] = curr[-2];
+ curr[-2] = BO_CHAR;
+ column += bo_width;
+ curr++;
+ }
+ goto ln_bo_xb_case;
+
+enter_underline:
+ /*
+ * We have either "_\bX" or "X\b_" (including
+ * the current char). Switch into underline mode.
+ */
+ column--;
+ if (column + ul_width + ue_width + 1 >= sc_width)
+ /*
+ * Not enough room left on the screen to
+ * enter and exit underline mode.
+ */
+ return (1);
+
+ if (ul_width > 0 &&
+ curr > linebuf + 2 && curr[-3] == ' ')
+ {
+ /*
+ * Special case for magic cookie terminals:
+ * if the previous char was a space, replace
+ * it with the "enter underline" sequence.
+ */
+ curr[-3] = UL_CHAR;
+ column += ul_width-1;
+ } else
+ {
+ curr[-1] = curr[-2];
+ curr[-2] = UL_CHAR;
+ column += ul_width;
+ curr++;
+ }
+ goto ln_ul_xb_case;
+ /*NOTREACHED*/
+ case LN_UL_XB:
+ /*
+ * Termination of a sequence "_\bX" or "X\b_".
+ */
+ if (c != '_' && curr[-2] != '_' && c == curr[-2])
+ {
+ /*
+ * We seem to have run on from underlining
+ * into boldfacing - this is a nasty fix, but
+ * until this whole routine is rewritten as a
+ * real DFA, ... well ...
+ */
+ curr[0] = curr[-2];
+ curr[-2] = UE_CHAR;
+ curr[-1] = BO_CHAR;
+ curr += 2; /* char & non-existent backspace */
+ ln_state = LN_BO_XB;
+ goto ln_bo_xb_case;
+ }
+ln_ul_xb_case:
+ if (c == '_')
+ c = curr[-2];
+ curr -= 2;
+ ln_state = LN_UNDERLINE;
+ break;
+ case LN_BO_XB:
+ /*
+ * Termination of a sequnce "X\bX".
+ */
+ if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
+ {
+ /*
+ * We seem to have run on from
+ * boldfacing into underlining.
+ */
+ curr[0] = curr[-2];
+ curr[-2] = BE_CHAR;
+ curr[-1] = UL_CHAR;
+ curr += 2; /* char & non-existent backspace */
+ ln_state = LN_UL_XB;
+ goto ln_ul_xb_case;
+ }
+ln_bo_xb_case:
+ curr -= 2;
+ ln_state = LN_BOLDFACE;
+ break;
+ case LN_UNDERLINE:
+ if (column + ue_width + bo_width + 1 + be_width >= sc_width)
+ /*
+ * We have just barely enough room to
+ * exit underline mode and handle a possible
+ * underline/boldface run on mixup.
+ */
+ return (1);
+ ln_state = LN_UL_X;
+ break;
+ case LN_BOLDFACE:
+ if (c == '\b')
+ {
+ ln_state = LN_BO_XB;
+ break;
+ }
+ if (column + be_width + ul_width + 1 + ue_width >= sc_width)
+ /*
+ * We have just barely enough room to
+ * exit underline mode and handle a possible
+ * underline/boldface run on mixup.
+ */
+ return (1);
+ ln_state = LN_BO_X;
+ break;
+ case LN_UL_X:
+ if (c == '\b')
+ ln_state = LN_UL_XB;
+ else
+ {
+ /*
+ * Exit underline mode.
+ * We have to shuffle the chars a bit
+ * to make this work.
+ */
+ curr[0] = curr[-1];
+ curr[-1] = UE_CHAR;
+ column += ue_width;
+ if (ue_width > 0 && curr[0] == ' ')
+ /*
+ * Another special case for magic
+ * cookie terminals: if the next
+ * char is a space, replace it
+ * with the "exit underline" sequence.
+ */
+ column--;
+ else
+ curr++;
+ ln_state = LN_NORMAL;
+ }
+ break;
+ case LN_BO_X:
+ if (c == '\b')
+ ln_state = LN_BO_XB;
+ else
+ {
+ /*
+ * Exit boldface mode.
+ * We have to shuffle the chars a bit
+ * to make this work.
+ */
+ curr[0] = curr[-1];
+ curr[-1] = BE_CHAR;
+ column += be_width;
+ if (be_width > 0 && curr[0] == ' ')
+ /*
+ * Another special case for magic
+ * cookie terminals: if the next
+ * char is a space, replace it
+ * with the "exit boldface" sequence.
+ */
+ column--;
+ else
+ curr++;
+ ln_state = LN_NORMAL;
+ }
+ break;
+ }
+ }
+
+ if (c == '\t') {
+ /*
+ * Expand a tab into spaces.
+ */
+ do {
+ NEW_COLUMN(1);
+ } while ((column % tabstop) != 0);
+ *curr++ = '\t';
+ return (0);
+ }
+
+ if (c == '\b') {
+ if (ln_state == LN_NORMAL)
+ NEW_COLUMN(2);
+ else
+ column--;
+ *curr++ = ('H' | 0200);
+ return(0);
+ }
+
+ if (CONTROL_CHAR(c)) {
+ /*
+ * Put a "^X" into the buffer. The 0200 bit is used to tell
+ * put_line() to prefix the char with a ^. We don't actually
+ * put the ^ in the buffer because we sometimes need to move
+ * chars around, and such movement might separate the ^ from
+ * its following character.
+ */
+ NEW_COLUMN(2);
+ *curr++ = (CARAT_CHAR(c) | 0200);
+ return(0);
+ }
+
+ /*
+ * Ordinary character. Just put it in the buffer.
+ */
+ NEW_COLUMN(1);
+ *curr++ = c;
+ return (0);
+}
+
+/*
+ * Analogous to forw_line(), but deals with "raw lines":
+ * lines which are not split for screen width.
+ * {{ This is supposed to be more efficient than forw_line(). }}
+ */
+off_t
+forw_raw_line(curr_pos)
+ off_t curr_pos;
+{
+ register char *p;
+ register int c;
+ off_t new_pos, ch_tell();
+
+ if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
+ (c = ch_forw_get()) == EOI)
+ return (NULL_POSITION);
+
+ p = linebuf;
+
+ for (;;)
+ {
+ if (c == '\n' || c == EOI)
+ {
+ new_pos = ch_tell();
+ break;
+ }
+ if (p >= &linebuf[sizeof(linebuf)-1])
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ * {{ The line buffer is supposed to be big
+ * enough that this never happens. }}
+ */
+ new_pos = ch_tell() - 1;
+ break;
+ }
+ *p++ = c;
+ c = ch_forw_get();
+ }
+ *p = '\0';
+ line = linebuf;
+ return (new_pos);
+}
+
+/*
+ * Analogous to back_line(), but deals with "raw lines".
+ * {{ This is supposed to be more efficient than back_line(). }}
+ */
+off_t
+back_raw_line(curr_pos)
+ off_t curr_pos;
+{
+ register char *p;
+ register int c;
+ off_t new_pos, ch_tell();
+
+ if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
+ ch_seek(curr_pos-1))
+ return (NULL_POSITION);
+
+ p = &linebuf[sizeof(linebuf)];
+ *--p = '\0';
+
+ for (;;)
+ {
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = (off_t)0;
+ break;
+ }
+ if (p <= linebuf)
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ *--p = c;
+ }
+ line = p;
+ return (new_pos);
+}
diff --git a/usr.bin/more/linenum.c b/usr.bin/more/linenum.c
new file mode 100644
index 0000000..4bfefa8
--- /dev/null
+++ b/usr.bin/more/linenum.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)linenum.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Code to handle displaying line numbers.
+ *
+ * Finding the line number of a given file position is rather tricky.
+ * We don't want to just start at the beginning of the file and
+ * count newlines, because that is slow for large files (and also
+ * wouldn't work if we couldn't get to the start of the file; e.g.
+ * if input is a long pipe).
+ *
+ * So we use the function add_lnum to cache line numbers.
+ * We try to be very clever and keep only the more interesting
+ * line numbers when we run out of space in our table. A line
+ * number is more interesting than another when it is far from
+ * other line numbers. For example, we'd rather keep lines
+ * 100,200,300 than 100,101,300. 200 is more interesting than
+ * 101 because 101 can be derived very cheaply from 100, while
+ * 200 is more expensive to derive from 100.
+ *
+ * The function currline() returns the line number of a given
+ * position in the file. As a side effect, it calls add_lnum
+ * to cache the line number. Therefore currline is occasionally
+ * called to make sure we cache line numbers often enough.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <less.h>
+
+/*
+ * Structure to keep track of a line number and the associated file position.
+ * A doubly-linked circular list of line numbers is kept ordered by line number.
+ */
+struct linenum
+{
+ struct linenum *next; /* Link to next in the list */
+ struct linenum *prev; /* Line to previous in the list */
+ off_t pos; /* File position */
+ off_t gap; /* Gap between prev and next */
+ int line; /* Line number */
+};
+/*
+ * "gap" needs some explanation: the gap of any particular line number
+ * is the distance between the previous one and the next one in the list.
+ * ("Distance" means difference in file position.) In other words, the
+ * gap of a line number is the gap which would be introduced if this
+ * line number were deleted. It is used to decide which one to replace
+ * when we have a new one to insert and the table is full.
+ */
+
+#define NPOOL 50 /* Size of line number pool */
+
+#define LONGTIME (2) /* In seconds */
+
+int lnloop = 0; /* Are we in the line num loop? */
+
+static struct linenum anchor; /* Anchor of the list */
+static struct linenum *freelist; /* Anchor of the unused entries */
+static struct linenum pool[NPOOL]; /* The pool itself */
+static struct linenum *spare; /* We always keep one spare entry */
+
+extern int linenums;
+extern int sigs;
+
+/*
+ * Initialize the line number structures.
+ */
+clr_linenum()
+{
+ register struct linenum *p;
+
+ /*
+ * Put all the entries on the free list.
+ * Leave one for the "spare".
+ */
+ for (p = pool; p < &pool[NPOOL-2]; p++)
+ p->next = p+1;
+ pool[NPOOL-2].next = NULL;
+ freelist = pool;
+
+ spare = &pool[NPOOL-1];
+
+ /*
+ * Initialize the anchor.
+ */
+ anchor.next = anchor.prev = &anchor;
+ anchor.gap = 0;
+ anchor.pos = (off_t)0;
+ anchor.line = 1;
+}
+
+/*
+ * Calculate the gap for an entry.
+ */
+static
+calcgap(p)
+ register struct linenum *p;
+{
+ /*
+ * Don't bother to compute a gap for the anchor.
+ * Also don't compute a gap for the last one in the list.
+ * The gap for that last one should be considered infinite,
+ * but we never look at it anyway.
+ */
+ if (p == &anchor || p->next == &anchor)
+ return;
+ p->gap = p->next->pos - p->prev->pos;
+}
+
+/*
+ * Add a new line number to the cache.
+ * The specified position (pos) should be the file position of the
+ * FIRST character in the specified line.
+ */
+add_lnum(line, pos)
+ int line;
+ off_t pos;
+{
+ register struct linenum *p;
+ register struct linenum *new;
+ register struct linenum *nextp;
+ register struct linenum *prevp;
+ register off_t mingap;
+
+ /*
+ * Find the proper place in the list for the new one.
+ * The entries are sorted by position.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ if (p->line == line)
+ /* We already have this one. */
+ return;
+ nextp = p;
+ prevp = p->prev;
+
+ if (freelist != NULL)
+ {
+ /*
+ * We still have free (unused) entries.
+ * Use one of them.
+ */
+ new = freelist;
+ freelist = freelist->next;
+ } else
+ {
+ /*
+ * No free entries.
+ * Use the "spare" entry.
+ */
+ new = spare;
+ spare = NULL;
+ }
+
+ /*
+ * Fill in the fields of the new entry,
+ * and insert it into the proper place in the list.
+ */
+ new->next = nextp;
+ new->prev = prevp;
+ new->pos = pos;
+ new->line = line;
+
+ nextp->prev = new;
+ prevp->next = new;
+
+ /*
+ * Recalculate gaps for the new entry and the neighboring entries.
+ */
+ calcgap(new);
+ calcgap(nextp);
+ calcgap(prevp);
+
+ if (spare == NULL)
+ {
+ /*
+ * We have used the spare entry.
+ * Scan the list to find the one with the smallest
+ * gap, take it out and make it the spare.
+ * We should never remove the last one, so stop when
+ * we get to p->next == &anchor. This also avoids
+ * looking at the gap of the last one, which is
+ * not computed by calcgap.
+ */
+ mingap = anchor.next->gap;
+ for (p = anchor.next; p->next != &anchor; p = p->next)
+ {
+ if (p->gap <= mingap)
+ {
+ spare = p;
+ mingap = p->gap;
+ }
+ }
+ spare->next->prev = spare->prev;
+ spare->prev->next = spare->next;
+ }
+}
+
+/*
+ * If we get stuck in a long loop trying to figure out the
+ * line number, print a message to tell the user what we're doing.
+ */
+static
+longloopmessage()
+{
+ ierror("Calculating line numbers");
+ /*
+ * Set the lnloop flag here, so if the user interrupts while
+ * we are calculating line numbers, the signal handler will
+ * turn off line numbers (linenums=0).
+ */
+ lnloop = 1;
+}
+
+/*
+ * Find the line number associated with a given position.
+ * Return 0 if we can't figure it out.
+ */
+find_linenum(pos)
+ off_t pos;
+{
+ register struct linenum *p;
+ register int lno;
+ register int loopcount;
+ off_t cpos, back_raw_line(), forw_raw_line();
+ time_t startime, time();
+
+ if (!linenums)
+ /*
+ * We're not using line numbers.
+ */
+ return (0);
+ if (pos == NULL_POSITION)
+ /*
+ * Caller doesn't know what he's talking about.
+ */
+ return (0);
+ if (pos == (off_t)0)
+ /*
+ * Beginning of file is always line number 1.
+ */
+ return (1);
+
+ /*
+ * Find the entry nearest to the position we want.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ continue;
+ if (p->pos == pos)
+ /* Found it exactly. */
+ return (p->line);
+
+ /*
+ * This is the (possibly) time-consuming part.
+ * We start at the line we just found and start
+ * reading the file forward or backward till we
+ * get to the place we want.
+ *
+ * First decide whether we should go forward from the
+ * previous one or backwards from the next one.
+ * The decision is based on which way involves
+ * traversing fewer bytes in the file.
+ */
+ flush();
+ (void)time(&startime);
+ if (p == &anchor || pos - p->prev->pos < p->pos - pos)
+ {
+ /*
+ * Go forward.
+ */
+ p = p->prev;
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos < pos; lno++)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = forw_raw_line(cpos);
+ if (sigs || cpos == NULL_POSITION)
+ return (0);
+ if (loopcount >= 0 && ++loopcount > 100) {
+ loopcount = 0;
+ if (time((time_t *)NULL)
+ >= startime + LONGTIME) {
+ longloopmessage();
+ loopcount = -1;
+ }
+ }
+ }
+ lnloop = 0;
+ /*
+ * If the given position is not at the start of a line,
+ * make sure we return the correct line number.
+ */
+ if (cpos > pos)
+ lno--;
+ } else
+ {
+ /*
+ * Go backward.
+ */
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos > pos; lno--)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = back_raw_line(cpos);
+ if (sigs || cpos == NULL_POSITION)
+ return (0);
+ if (loopcount >= 0 && ++loopcount > 100) {
+ loopcount = 0;
+ if (time((time_t *)NULL)
+ >= startime + LONGTIME) {
+ longloopmessage();
+ loopcount = -1;
+ }
+ }
+ }
+ lnloop = 0;
+ }
+
+ /*
+ * We might as well cache it.
+ */
+ add_lnum(lno, cpos);
+ return (lno);
+}
+
+/*
+ * Return the line number of the "current" line.
+ * The argument "where" tells which line is to be considered
+ * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
+ */
+currline(where)
+ int where;
+{
+ off_t pos, ch_length(), position();
+
+ if ((pos = position(where)) == NULL_POSITION)
+ pos = ch_length();
+ return(find_linenum(pos));
+}
diff --git a/usr.bin/more/main.c b/usr.bin/more/main.c
new file mode 100644
index 0000000..f019bdc
--- /dev/null
+++ b/usr.bin/more/main.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988 Mark Nudleman.\n\
+@(#) Copyright (c) 1988, 1993
+ Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/7/93";
+#endif /* not lint */
+
+/*
+ * Entry point, initialization, miscellaneous routines.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <less.h>
+
+int ispipe;
+int new_file;
+int is_tty;
+char *current_file, *previous_file, *current_name, *next_name;
+off_t prev_pos;
+int any_display;
+int scroll;
+int ac;
+char **av;
+int curr_ac;
+int quitting;
+
+extern int file;
+extern int cbufs;
+extern int errmsgs;
+
+extern char *tagfile;
+extern int tagoption;
+
+/*
+ * Edit a new file.
+ * Filename "-" means standard input.
+ * No filename means the "current" file, from the command line.
+ */
+edit(filename)
+ register char *filename;
+{
+ extern int errno;
+ register int f;
+ register char *m;
+ off_t initial_pos, position();
+ static int didpipe;
+ char message[100], *p;
+ char *rindex(), *strerror(), *save(), *bad_file();
+
+ initial_pos = NULL_POSITION;
+ if (filename == NULL || *filename == '\0') {
+ if (curr_ac >= ac) {
+ error("No current file");
+ return(0);
+ }
+ filename = save(av[curr_ac]);
+ }
+ else if (strcmp(filename, "#") == 0) {
+ if (*previous_file == '\0') {
+ error("no previous file");
+ return(0);
+ }
+ filename = save(previous_file);
+ initial_pos = prev_pos;
+ } else
+ filename = save(filename);
+
+ /* use standard input. */
+ if (!strcmp(filename, "-")) {
+ if (didpipe) {
+ error("Can view standard input only once");
+ return(0);
+ }
+ f = 0;
+ }
+ else if ((m = bad_file(filename, message, sizeof(message))) != NULL) {
+ error(m);
+ free(filename);
+ return(0);
+ }
+ else if ((f = open(filename, O_RDONLY, 0)) < 0) {
+ (void)sprintf(message, "%s: %s", filename, strerror(errno));
+ error(message);
+ free(filename);
+ return(0);
+ }
+
+ if (isatty(f)) {
+ /*
+ * Not really necessary to call this an error,
+ * but if the control terminal (for commands)
+ * and the input file (for data) are the same,
+ * we get weird results at best.
+ */
+ error("Can't take input from a terminal");
+ if (f > 0)
+ (void)close(f);
+ (void)free(filename);
+ return(0);
+ }
+
+ /*
+ * We are now committed to using the new file.
+ * Close the current input file and set up to use the new one.
+ */
+ if (file > 0)
+ (void)close(file);
+ new_file = 1;
+ if (previous_file != NULL)
+ free(previous_file);
+ previous_file = current_file;
+ current_file = filename;
+ pos_clear();
+ prev_pos = position(TOP);
+ ispipe = (f == 0);
+ if (ispipe) {
+ didpipe = 1;
+ current_name = "stdin";
+ } else
+ current_name = (p = rindex(filename, '/')) ? p + 1 : filename;
+ if (curr_ac >= ac)
+ next_name = NULL;
+ else
+ next_name = av[curr_ac + 1];
+ file = f;
+ ch_init(cbufs, 0);
+ init_mark();
+
+ if (is_tty) {
+ int no_display = !any_display;
+ any_display = 1;
+ if (no_display && errmsgs > 0) {
+ /*
+ * We displayed some messages on error output
+ * (file descriptor 2; see error() function).
+ * Before erasing the screen contents,
+ * display the file name and wait for a keystroke.
+ */
+ error(filename);
+ }
+ /*
+ * Indicate there is nothing displayed yet.
+ */
+ if (initial_pos != NULL_POSITION)
+ jump_loc(initial_pos);
+ clr_linenum();
+ }
+ return(1);
+}
+
+/*
+ * Edit the next file in the command line list.
+ */
+next_file(n)
+ int n;
+{
+ extern int quit_at_eof;
+ off_t position();
+
+ if (curr_ac + n >= ac) {
+ if (quit_at_eof || position(TOP) == NULL_POSITION)
+ quit();
+ error("No (N-th) next file");
+ }
+ else
+ (void)edit(av[curr_ac += n]);
+}
+
+/*
+ * Edit the previous file in the command line list.
+ */
+prev_file(n)
+ int n;
+{
+ if (curr_ac - n < 0)
+ error("No (N-th) previous file");
+ else
+ (void)edit(av[curr_ac -= n]);
+}
+
+/*
+ * copy a file directly to standard output; used if stdout is not a tty.
+ * the only processing is to squeeze multiple blank input lines.
+ */
+static
+cat_file()
+{
+ extern int squeeze;
+ register int c, empty;
+
+ if (squeeze) {
+ empty = 0;
+ while ((c = ch_forw_get()) != EOI)
+ if (c != '\n') {
+ putchr(c);
+ empty = 0;
+ }
+ else if (empty < 2) {
+ putchr(c);
+ ++empty;
+ }
+ }
+ else while ((c = ch_forw_get()) != EOI)
+ putchr(c);
+ flush();
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int envargc, argcnt;
+ char *envargv[2], *getenv();
+
+ /*
+ * Process command line arguments and MORE environment arguments.
+ * Command line arguments override environment arguments.
+ */
+ if (envargv[1] = getenv("MORE")) {
+ envargc = 2;
+ envargv[0] = "more";
+ envargv[2] = NULL;
+ (void)option(envargc, envargv);
+ }
+ argcnt = option(argc, argv);
+ argv += argcnt;
+ argc -= argcnt;
+
+ /*
+ * Set up list of files to be examined.
+ */
+ ac = argc;
+ av = argv;
+ curr_ac = 0;
+
+ /*
+ * Set up terminal, etc.
+ */
+ is_tty = isatty(1);
+ if (!is_tty) {
+ /*
+ * Output is not a tty.
+ * Just copy the input file(s) to output.
+ */
+ if (ac < 1) {
+ (void)edit("-");
+ cat_file();
+ } else {
+ do {
+ (void)edit((char *)NULL);
+ if (file >= 0)
+ cat_file();
+ } while (++curr_ac < ac);
+ }
+ exit(0);
+ }
+
+ raw_mode(1);
+ get_term();
+ open_getchr();
+ init();
+ init_signals(1);
+
+ /* select the first file to examine. */
+ if (tagoption) {
+ /*
+ * A -t option was given; edit the file selected by the
+ * "tags" search, and search for the proper line in the file.
+ */
+ if (!tagfile || !edit(tagfile) || tagsearch())
+ quit();
+ }
+ else if (ac < 1)
+ (void)edit("-"); /* Standard input */
+ else {
+ /*
+ * Try all the files named as command arguments.
+ * We are simply looking for one which can be
+ * opened without error.
+ */
+ do {
+ (void)edit((char *)NULL);
+ } while (file < 0 && ++curr_ac < ac);
+ }
+
+ if (file >= 0)
+ commands();
+ quit();
+ /*NOTREACHED*/
+}
+
+/*
+ * Copy a string to a "safe" place
+ * (that is, to a buffer allocated by malloc).
+ */
+char *
+save(s)
+ char *s;
+{
+ char *p, *strcpy(), *malloc();
+
+ p = malloc((u_int)strlen(s)+1);
+ if (p == NULL)
+ {
+ error("cannot allocate memory");
+ quit();
+ }
+ return(strcpy(p, s));
+}
+
+/*
+ * Exit the program.
+ */
+quit()
+{
+ /*
+ * Put cursor at bottom left corner, clear the line,
+ * reset the terminal modes, and exit.
+ */
+ quitting = 1;
+ lower_left();
+ clear_eol();
+ deinit();
+ flush();
+ raw_mode(0);
+ exit(0);
+}
diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1
new file mode 100644
index 0000000..209b617
--- /dev/null
+++ b/usr.bin/more/more.1
@@ -0,0 +1,298 @@
+.\" Copyright (c) 1988, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)more.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt MORE 1
+.Os
+.Sh NAME
+.Nm more
+.Nd file perusal filter for crt viewing
+.Sh SYNOPSIS
+.Nm more
+.Op Fl ceinus
+.Op Fl t Ar tag
+.Op Fl x Ar tabs
+.Op Fl / Ar pattern
+.Op Fl #
+.Op Ar
+.Sh DESCRIPTION
+.Nm More
+is a filter for paging through text one screenful at a time. It
+uses
+.Xr termcap 3
+so it can run on a variety of terminals. There is even limited support
+for hardcopy terminals. (On a hardcopy terminal, lines which should be
+printed at the top of the screen are prefixed with an up-arrow.)
+.Ar File
+may be a single dash (``-''), implying stdin.
+.Sh OPTIONS
+Command line options are described below.
+Options are also taken from the environment variable
+.Ev MORE
+(make sure to precede them with a dash (``-'')) but command
+line options will override them.
+.Bl -tag -width flag
+.It Fl c
+Normally,
+.Nm more
+will repaint the screen by scrolling from the bottom of the screen.
+If the
+.Fl c
+option is set, when
+.Nm more
+needs to change the entire display, it will paint from the top line down.
+.It Fl e
+Normally, if displaying a single file,
+.Nm more
+exits as soon as it reaches end-of-file. The
+.Fl e
+option tells more to
+exit if it reaches end-of-file twice without an intervening operation.
+If the file is shorter than a single screen
+.Nm more
+will exit at end-of-file regardless.
+.It Fl i
+The
+.Fl i
+option causes searches to ignore case; that is,
+uppercase and lowercase are considered identical.
+.It Fl n
+The
+.Fl n
+flag suppresses line numbers.
+The default (to use line numbers) may cause
+.Nm more
+to run more slowly in some cases, especially with a very large input file.
+Suppressing line numbers with the
+.Fl n
+flag will avoid this problem.
+Using line numbers means: the line number will be displayed in the
+.Cm =
+command, and the
+.Cm v
+command will pass the current line number to the editor.
+.It Fl s
+The
+.Fl s
+option causes
+consecutive blank lines to be squeezed into a single blank line.
+.It Fl t
+The
+.Fl t
+option, followed immediately by a tag, will edit the file
+containing that tag. For more information, see the
+.Xr ctags 1
+command.
+.It Fl u
+By default,
+.Nm more
+treats backspaces and
+.Dv CR-LF
+sequences specially. Backspaces which appear
+adjacent to an underscore character are displayed as underlined text.
+Backspaces which appear between two identical characters are displayed
+as emboldened text.
+.Dv CR-LF
+sequences are compressed to a single linefeed
+character. The
+.Fl u
+option causes backspaces to always be displayed as
+control characters, i.e. as the two character sequence ``^H'', and
+.Dv CR-LF
+to be left alone.
+.It Fl x
+The
+.Fl x
+option sets tab stops every
+.Ar N
+positions. The default for
+.Ar N
+is 8.
+.It Fl /
+The
+.Fl /
+option specifies a string that will be searched for before
+each file is displayed.
+.Sh COMMANDS
+Interactive commands for
+.Nm more
+are based on
+.Xr vi 1 .
+Some commands may be preceded by a decimal number, called N in the
+descriptions below.
+In the following descriptions, ^X means control-X.
+.Pp
+.Bl -tag -width Ic
+.It Ic h
+Help: display a summary of these commands.
+If you forget all the other commands, remember this one.
+.It Xo
+.Ic SPACE
+.No or
+.Ic f
+.No or
+.Ic \&^F
+.Xc
+Scroll forward N lines, default one window.
+If N is more than the screen size, only the final screenful is displayed.
+.It Ic b No or Ic \&^B
+Scroll backward N lines, default one window (see option -z below).
+If N is more than the screen size, only the final screenful is displayed.
+.It Ic j No or Ic RETURN
+Scroll forward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.It Ic k
+Scroll backward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.It Ic d No or Ic \&^D
+Scroll forward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.It Ic u No or Ic \&^U
+Scroll backward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.It Ic g
+Go to line N in the file, default 1 (beginning of file).
+.It Ic G
+Go to line N in the file, default the end of the file.
+.It Ic p No or Ic \&%
+Go to a position N percent into the file. N should be between 0
+and 100. (This works if standard input is being read, but only if
+.Nm more
+has already read to the end of the file. It is always fast, but
+not always useful.)
+.It Ic r No or Ic \&^L
+Repaint the screen.
+.It Ic R
+Repaint the screen, discarding any buffered input.
+Useful if the file is changing while it is being viewed.
+.It Ic m
+Followed by any lowercase letter,
+marks the current position with that letter.
+.It Ic \&'
+(Single quote.)
+Followed by any lowercase letter, returns to the position which
+was previously marked with that letter.
+Followed by another single quote, returns to the position at
+which the last "large" movement command was executed, or the
+beginning of the file if no such movements have occurred.
+All marks are lost when a new file is examined.
+.It Ic \&/ Ns Ar pattern
+Search forward in the file for the N-th line containing the pattern.
+N defaults to 1.
+The pattern is a regular expression, as recognized by
+.Xr ed .
+The search starts at the second line displayed.
+.It Ic \&? Ns Ar pattern
+Search backward in the file for the N-th line containing the pattern.
+The search starts at the line immediately before the top line displayed.
+.It Ic \&/\&! Ns Ar pattern
+Like /, but the search is for the N-th line
+which does NOT contain the pattern.
+.It Ic \&?\&! Ns Ar pattern
+Like ?, but the search is for the N-th line
+which does NOT contain the pattern.
+.It Ic n
+Repeat previous search, for N-th line containing the last pattern
+(or
+.Tn NOT
+containing the last pattern, if the previous search
+was /! or ?!).
+.It Ic E Ns Op Ar filename
+Examine a new file.
+If the filename is missing, the "current" file (see the N and P commands
+below) from the list of files in the command line is re-examined.
+If the filename is a pound sign (#), the previously examined file is
+re-examined.
+.It Ic N No or Ic \&:n
+Examine the next file (from the list of files given in the command line).
+If a number N is specified (not to be confused with the command N),
+the N-th next file is examined.
+.It Ic P No or Ic \&:p
+Examine the previous file.
+If a number N is specified, the N-th previous file is examined.
+.It Ic \&:t
+Go to supplied tag.
+.It Ic v
+Invokes an editor to edit the current file being viewed.
+The editor is taken from the environment variable
+.Ev EDITOR ,
+or defaults to
+.Xr vi 1 .
+.It Ic \&= No or Ic \&^G
+These options print out the number of the file currently being displayed
+relative to the total number of files there are to display, the current
+line number, the current byte number and the total bytes to display, and
+what percentage of the file has been displayed. If
+.Nm more
+is reading from stdin, or the file is shorter than a single screen, some
+of these items may not be available. Note, all of these items reference
+the first byte of the last line displayed on the screen.
+.It Xo
+.Ic q
+.No or
+.Ic \&:q
+.No or
+.Ic ZZ
+.Xc
+Exits
+.Nm more .
+.El
+.Sh ENVIRONMENT
+.Nm More
+utilizes the following environment variables, if they exist:
+.Bl -tag -width Fl
+.It Ev MORE
+This variable may be set with favored options to
+.Nm more .
+.It Ev EDITOR
+Specify default editor.
+.It Ev SHELL
+Current shell in use (normally set by the shell at login time).
+.It Ev TERM
+Specifies terminal type, used by more to get the terminal
+characteristics necessary to manipulate the screen.
+.El
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr vi 1
+.Sh AUTHOR
+This software is derived from software contributed to Berkeley
+by Mark Nudleman.
+.Sh HISTORY
+The
+.Nm more
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/more/more.help b/usr.bin/more/more.help
new file mode 100644
index 0000000..fcef82f
--- /dev/null
+++ b/usr.bin/more/more.help
@@ -0,0 +1,39 @@
+ Commands flagged with an asterisk (``*'') may be preceeded by a number.
+ Commands of the form ``^X'' are control characters, i.e. control-X.
+
+ h Display this help.
+
+ f, ^F, SPACE * Forward N lines, default one screen.
+ b, ^B * Backward N lines, default one screen.
+ j, CR * Forward N lines, default 1 line.
+ k * Backward N lines, default 1 line.
+ d, ^D * Forward N lines, default half screen or last N to d/u.
+ u, ^U * Backward N lines, default half screen or last N to d/u.
+ g * Go to line N, default 1.
+ G * Go to line N, default the end of the file.
+ p, % * Position to N percent into the file.
+
+ r, ^L Repaint screen.
+ R Repaint screen, discarding buffered input.
+
+ m[a-z] Mark the current position with the supplied letter.
+ '[a-z] Return to the position previously marked by this letter.
+ '' Return to previous position.
+
+ /pattern * Search forward for N-th line containing the pattern.
+ /!pattern * Search forward for N-th line NOT containing the pattern.
+ ?pattern * Search backward for N-th line containing the pattern.
+ ?!pattern * Search backward for N-th line NOT containing the pattern.
+ n * Repeat previous search (for N-th occurence).
+
+ :a Display the list of files.
+ E [file] Examine a new file.
+ :n, N * Examine the next file.
+ :p, P * Examine the previous file.
+ :t [tag] Examine the tag.
+ v Run an editor on the current file.
+
+ =, ^G Print current file name and stats.
+
+ q, :q, or ZZ Exit.
+
diff --git a/usr.bin/more/option.c b/usr.bin/more/option.c
new file mode 100644
index 0000000..29349d0
--- /dev/null
+++ b/usr.bin/more/option.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)option.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <less.h>
+
+int top_scroll; /* Repaint screen from top */
+int bs_mode; /* How to process backspaces */
+int caseless; /* Do "caseless" searches */
+int cbufs = 10; /* Current number of buffers */
+int linenums = 1; /* Use line numbers */
+int quit_at_eof;
+int squeeze; /* Squeeze multiple blank lines into one */
+int tabstop = 8; /* Tab settings */
+int tagoption;
+
+char *firstsearch;
+extern int sc_height;
+
+option(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ static int sc_window_set = 0;
+ int ch;
+ char *p;
+
+ /* backward compatible processing for "+/search" */
+ char **a;
+ for (a = argv; *a; ++a)
+ if ((*a)[0] == '+' && (*a)[1] == '/')
+ (*a)[0] = '-';
+
+ optind = 1; /* called twice, re-init getopt. */
+ while ((ch = getopt(argc, argv, "0123456789/:ceinst:ux:f")) != EOF)
+ switch((char)ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * kludge: more was originally designed to take
+ * a number after a dash.
+ */
+ if (!sc_window_set) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ sc_height = atoi(++p);
+ else
+ sc_height = atoi(argv[optind] + 1);
+ sc_window_set = 1;
+ }
+ break;
+ case '/':
+ firstsearch = optarg;
+ break;
+ case 'c':
+ top_scroll = 1;
+ break;
+ case 'e':
+ quit_at_eof = 1;
+ break;
+ case 'i':
+ caseless = 1;
+ break;
+ case 'n':
+ linenums = 0;
+ break;
+ case 's':
+ squeeze = 1;
+ break;
+ case 't':
+ tagoption = 1;
+ findtag(optarg);
+ break;
+ case 'u':
+ bs_mode = 1;
+ break;
+ case 'x':
+ tabstop = atoi(optarg);
+ if (tabstop <= 0)
+ tabstop = 8;
+ break;
+ case 'f': /* ignore -f, compatability with old more */
+ break;
+ case '?':
+ default:
+ fprintf(stderr,
+ "usage: more [-ceinus] [-t tag] [-x tabs] [-/ pattern] [-#] [file ...]\n");
+ exit(1);
+ }
+ return(optind);
+}
diff --git a/usr.bin/more/os.c b/usr.bin/more/os.c
new file mode 100644
index 0000000..75b7a6e
--- /dev/null
+++ b/usr.bin/more/os.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)os.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Operating system dependent routines.
+ *
+ * Most of the stuff in here is based on Unix, but an attempt
+ * has been made to make things work on other operating systems.
+ * This will sometimes result in a loss of functionality, unless
+ * someone rewrites code specifically for the new operating system.
+ *
+ * The makefile provides defines to decide whether various
+ * Unix features are present.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <less.h>
+#include "pathnames.h"
+
+int reading;
+
+extern int screen_trashed;
+
+static jmp_buf read_label;
+
+/*
+ * Pass the specified command to a shell to be executed.
+ * Like plain "system()", but handles resetting terminal modes, etc.
+ */
+lsystem(cmd)
+ char *cmd;
+{
+ int inp;
+ char cmdbuf[256];
+ char *shell, *getenv();
+
+ /*
+ * Print the command which is to be executed,
+ * unless the command starts with a "-".
+ */
+ if (cmd[0] == '-')
+ cmd++;
+ else
+ {
+ lower_left();
+ clear_eol();
+ putstr("!");
+ putstr(cmd);
+ putstr("\n");
+ }
+
+ /*
+ * De-initialize the terminal and take out of raw mode.
+ */
+ deinit();
+ flush();
+ raw_mode(0);
+
+ /*
+ * Restore signals to their defaults.
+ */
+ init_signals(0);
+
+ /*
+ * Force standard input to be the terminal, "/dev/tty",
+ * even if less's standard input is coming from a pipe.
+ */
+ inp = dup(0);
+ (void)close(0);
+ if (open(_PATH_TTY, O_RDONLY, 0) < 0)
+ (void)dup(inp);
+
+ /*
+ * Pass the command to the system to be executed.
+ * If we have a SHELL environment variable, use
+ * <$SHELL -c "command"> instead of just <command>.
+ * If the command is empty, just invoke a shell.
+ */
+ if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
+ {
+ if (*cmd == '\0')
+ cmd = shell;
+ else
+ {
+ (void)sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
+ cmd = cmdbuf;
+ }
+ }
+ if (*cmd == '\0')
+ cmd = "sh";
+
+ (void)system(cmd);
+
+ /*
+ * Restore standard input, reset signals, raw mode, etc.
+ */
+ (void)close(0);
+ (void)dup(inp);
+ (void)close(inp);
+
+ init_signals(1);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+#if defined(SIGWINCH) || defined(SIGWIND)
+ /*
+ * Since we were ignoring window change signals while we executed
+ * the system command, we must assume the window changed.
+ */
+ winch();
+#endif
+}
+
+/*
+ * Like read() system call, but is deliberately interruptable.
+ * A call to intread() from a signal handler will interrupt
+ * any pending iread().
+ */
+iread(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ register int n;
+
+ if (setjmp(read_label))
+ /*
+ * We jumped here from intread.
+ */
+ return (READ_INTR);
+
+ flush();
+ reading = 1;
+ n = read(fd, buf, len);
+ reading = 0;
+ if (n < 0)
+ return (-1);
+ return (n);
+}
+
+intread()
+{
+ (void)sigsetmask(0L);
+ longjmp(read_label, 1);
+}
+
+/*
+ * Expand a filename, substituting any environment variables, etc.
+ * The implementation of this is necessarily very operating system
+ * dependent. This implementation is unabashedly only for Unix systems.
+ */
+FILE *popen();
+
+char *
+glob(filename)
+ char *filename;
+{
+ FILE *f;
+ char *p;
+ int ch;
+ char *cmd, *malloc(), *getenv();
+ static char buffer[MAXPATHLEN];
+
+ if (filename[0] == '#')
+ return (filename);
+
+ /*
+ * We get the shell to expand the filename for us by passing
+ * an "echo" command to the shell and reading its output.
+ */
+ p = getenv("SHELL");
+ if (p == NULL || *p == '\0')
+ {
+ /*
+ * Read the output of <echo filename>.
+ */
+ cmd = malloc((u_int)(strlen(filename)+8));
+ if (cmd == NULL)
+ return (filename);
+ (void)sprintf(cmd, "echo \"%s\"", filename);
+ } else
+ {
+ /*
+ * Read the output of <$SHELL -c "echo filename">.
+ */
+ cmd = malloc((u_int)(strlen(p)+12));
+ if (cmd == NULL)
+ return (filename);
+ (void)sprintf(cmd, "%s -c \"echo %s\"", p, filename);
+ }
+
+ if ((f = popen(cmd, "r")) == NULL)
+ return (filename);
+ free(cmd);
+
+ for (p = buffer; p < &buffer[sizeof(buffer)-1]; p++)
+ {
+ if ((ch = getc(f)) == '\n' || ch == EOF)
+ break;
+ *p = ch;
+ }
+ *p = '\0';
+ (void)pclose(f);
+ return(buffer);
+}
+
+char *
+bad_file(filename, message, len)
+ char *filename, *message;
+ u_int len;
+{
+ extern int errno;
+ struct stat statbuf;
+ char *strcat(), *strerror();
+
+ if (stat(filename, &statbuf) < 0) {
+ (void)sprintf(message, "%s: %s", filename, strerror(errno));
+ return(message);
+ }
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ static char is_dir[] = " is a directory";
+
+ strtcpy(message, filename, (int)(len-sizeof(is_dir)-1));
+ (void)strcat(message, is_dir);
+ return(message);
+ }
+ return((char *)NULL);
+}
+
+/*
+ * Copy a string, truncating to the specified length if necessary.
+ * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
+ */
+strtcpy(to, from, len)
+ char *to, *from;
+ int len;
+{
+ char *strncpy();
+
+ (void)strncpy(to, from, (int)len);
+ to[len-1] = '\0';
+}
+
diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c
new file mode 100644
index 0000000..c721f4e
--- /dev/null
+++ b/usr.bin/more/output.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * High level routines dealing with the output to the screen.
+ */
+
+#include <stdio.h>
+#include <less.h>
+
+int errmsgs; /* Count of messages displayed by error() */
+
+extern int sigs;
+extern int sc_width, sc_height;
+extern int ul_width, ue_width;
+extern int so_width, se_width;
+extern int bo_width, be_width;
+extern int tabstop;
+extern int screen_trashed;
+extern int any_display;
+extern char *line;
+
+/* display the line which is in the line buffer. */
+put_line()
+{
+ register char *p;
+ register int c;
+ register int column;
+ extern int auto_wrap, ignaw;
+
+ if (sigs)
+ {
+ /*
+ * Don't output if a signal is pending.
+ */
+ screen_trashed = 1;
+ return;
+ }
+
+ if (line == NULL)
+ line = "";
+
+ column = 0;
+ for (p = line; *p != '\0'; p++)
+ {
+ switch (c = *p)
+ {
+ case UL_CHAR:
+ ul_enter();
+ column += ul_width +1;
+ break;
+ case UE_CHAR:
+ ul_exit();
+ column += ue_width;
+ break;
+ case BO_CHAR:
+ bo_enter();
+ column += bo_width +1;
+ break;
+ case BE_CHAR:
+ bo_exit();
+ column += be_width;
+ break;
+ case '\t':
+ do
+ {
+ putchr(' ');
+ column++;
+ } while ((column % tabstop) != 0);
+ break;
+ case '\b':
+ putbs();
+ column--;
+ break;
+ default:
+ if (c & 0200)
+ {
+ /*
+ * Control characters arrive here as the
+ * normal character [CARAT_CHAR(c)] with
+ * the 0200 bit set. See pappend().
+ */
+ putchr('^');
+ putchr(c & 0177);
+ column += 2;
+ } else
+ {
+ putchr(c);
+ column++;
+ }
+ }
+ }
+ if (column < sc_width || !auto_wrap || ignaw)
+ putchr('\n');
+}
+
+static char obuf[1024];
+static char *ob = obuf;
+
+/*
+ * Flush buffered output.
+ */
+flush()
+{
+ register int n;
+
+ n = ob - obuf;
+ if (n == 0)
+ return;
+ if (write(1, obuf, n) != n)
+ screen_trashed = 1;
+ ob = obuf;
+}
+
+/*
+ * Purge any pending output.
+ */
+purge()
+{
+
+ ob = obuf;
+}
+
+/*
+ * Output a character.
+ */
+putchr(c)
+ int c;
+{
+ if (ob >= &obuf[sizeof(obuf)])
+ flush();
+ *ob++ = c;
+}
+
+/*
+ * Output a string.
+ */
+putstr(s)
+ register char *s;
+{
+ while (*s != '\0')
+ putchr(*s++);
+}
+
+int cmdstack;
+static char return_to_continue[] = "(press RETURN)";
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+error(s)
+ char *s;
+{
+ int ch;
+
+ ++errmsgs;
+ if (!any_display) {
+ /*
+ * Nothing has been displayed yet. Output this message on
+ * error output (file descriptor 2) and don't wait for a
+ * keystroke to continue.
+ *
+ * This has the desirable effect of producing all error
+ * messages on error output if standard output is directed
+ * to a file. It also does the same if we never produce
+ * any real output; for example, if the input file(s) cannot
+ * be opened. If we do eventually produce output, code in
+ * edit() makes sure these messages can be seen before they
+ * are overwritten or scrolled away.
+ */
+ (void)write(2, s, strlen(s));
+ (void)write(2, "\n", 1);
+ return;
+ }
+
+ lower_left();
+ clear_eol();
+ so_enter();
+ if (s) {
+ putstr(s);
+ putstr(" ");
+ }
+ putstr(return_to_continue);
+ so_exit();
+
+ if ((ch = getchr()) != '\n') {
+ if (ch == 'q')
+ quit();
+ cmdstack = ch;
+ }
+ lower_left();
+
+ if (strlen(s) + sizeof(return_to_continue) +
+ so_width + se_width + 1 > sc_width)
+ /*
+ * Printing the message has probably scrolled the screen.
+ * {{ Unless the terminal doesn't have auto margins,
+ * in which case we just hammered on the right margin. }}
+ */
+ repaint();
+ flush();
+}
+
+static char intr_to_abort[] = "... (interrupt to abort)";
+
+ierror(s)
+ char *s;
+{
+ lower_left();
+ clear_eol();
+ so_enter();
+ putstr(s);
+ putstr(intr_to_abort);
+ so_exit();
+ flush();
+}
diff --git a/usr.bin/more/pathnames.h b/usr.bin/more/pathnames.h
new file mode 100644
index 0000000..c564360
--- /dev/null
+++ b/usr.bin/more/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_HELPFILE "/usr/share/misc/more.help"
diff --git a/usr.bin/more/position.c b/usr.bin/more/position.c
new file mode 100644
index 0000000..8564be0
--- /dev/null
+++ b/usr.bin/more/position.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)position.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with the "position" table.
+ * This is a table which tells the position (in the input file) of the
+ * first char on each currently displayed line.
+ *
+ * {{ The position table is scrolled by moving all the entries.
+ * Would be better to have a circular table
+ * and just change a couple of pointers. }}
+ */
+
+#include <sys/types.h>
+#include <less.h>
+
+static off_t *table; /* The position table */
+static int tablesize;
+
+extern int sc_height;
+
+/*
+ * Return the starting file position of a line displayed on the screen.
+ * The line may be specified as a line number relative to the top
+ * of the screen, but is usually one of these special cases:
+ * the top (first) line on the screen
+ * the second line on the screen
+ * the bottom line on the screen
+ * the line after the bottom line on the screen
+ */
+off_t
+position(where)
+ int where;
+{
+ switch (where)
+ {
+ case BOTTOM:
+ where = sc_height - 2;
+ break;
+ case BOTTOM_PLUS_ONE:
+ where = sc_height - 1;
+ break;
+ case MIDDLE:
+ where = sc_height / 2;
+ }
+ return (table[where]);
+}
+
+/*
+ * Add a new file position to the bottom of the position table.
+ */
+add_forw_pos(pos)
+ off_t pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table up.
+ */
+ for (i = 1; i < sc_height; i++)
+ table[i-1] = table[i];
+ table[sc_height - 1] = pos;
+}
+
+/*
+ * Add a new file position to the top of the position table.
+ */
+add_back_pos(pos)
+ off_t pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table down.
+ */
+ for (i = sc_height - 1; i > 0; i--)
+ table[i] = table[i-1];
+ table[0] = pos;
+}
+
+copytable()
+{
+ register int a, b;
+
+ for (a = 0; a < sc_height && table[a] == NULL_POSITION; a++);
+ for (b = 0; a < sc_height; a++, b++) {
+ table[b] = table[a];
+ table[a] = NULL_POSITION;
+ }
+}
+
+/*
+ * Initialize the position table, done whenever we clear the screen.
+ */
+pos_clear()
+{
+ register int i;
+ extern char *malloc(), *realloc();
+
+ if (table == 0) {
+ tablesize = sc_height > 25 ? sc_height : 25;
+ table = (off_t *)malloc(tablesize * sizeof *table);
+ } else if (sc_height >= tablesize) {
+ tablesize = sc_height;
+ table = (off_t *)realloc(table, tablesize * sizeof *table);
+ }
+
+ for (i = 0; i < sc_height; i++)
+ table[i] = NULL_POSITION;
+}
+
+/*
+ * See if the byte at a specified position is currently on the screen.
+ * Check the position table to see if the position falls within its range.
+ * Return the position table entry if found, -1 if not.
+ */
+onscreen(pos)
+ off_t pos;
+{
+ register int i;
+
+ if (pos < table[0])
+ return (-1);
+ for (i = 1; i < sc_height; i++)
+ if (pos < table[i])
+ return (i-1);
+ return (-1);
+}
diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c
new file mode 100644
index 0000000..d5af8f3
--- /dev/null
+++ b/usr.bin/more/prim.c
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)prim.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Primitives for displaying the file on the screen.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <less.h>
+
+int back_scroll = -1;
+int hit_eof; /* keeps track of how many times we hit end of file */
+int screen_trashed;
+
+static int squished;
+
+extern int sigs;
+extern int top_scroll;
+extern int sc_width, sc_height;
+extern int caseless;
+extern int linenums;
+extern int tagoption;
+extern char *line;
+extern int retain_below;
+
+off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line();
+off_t ch_length(), ch_tell();
+
+/*
+ * Check to see if the end of file is currently "displayed".
+ */
+eof_check()
+{
+ off_t pos;
+
+ if (sigs)
+ return;
+ /*
+ * If the bottom line is empty, we are at EOF.
+ * If the bottom line ends at the file length,
+ * we must be just at EOF.
+ */
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION || pos == ch_length())
+ hit_eof++;
+}
+
+/*
+ * If the screen is "squished", repaint it.
+ * "Squished" means the first displayed line is not at the top
+ * of the screen; this can happen when we display a short file
+ * for the first time.
+ */
+squish_check()
+{
+ if (squished) {
+ squished = 0;
+ repaint();
+ }
+}
+
+/*
+ * Display n lines, scrolling forward, starting at position pos in the
+ * input file. "only_last" means display only the last screenful if
+ * n > screen size.
+ */
+forw(n, pos, only_last)
+ register int n;
+ off_t pos;
+ int only_last;
+{
+ extern int short_file;
+ static int first_time = 1;
+ int eof = 0, do_repaint;
+
+ squish_check();
+
+ /*
+ * do_repaint tells us not to display anything till the end,
+ * then just repaint the entire screen.
+ */
+ do_repaint = (only_last && n > sc_height-1);
+
+ if (!do_repaint) {
+ if (top_scroll && n >= sc_height - 1) {
+ /*
+ * Start a new screen.
+ * {{ This is not really desirable if we happen
+ * to hit eof in the middle of this screen,
+ * but we don't yet know if that will happen. }}
+ */
+ clear();
+ home();
+ } else {
+ lower_left();
+ clear_eol();
+ }
+
+ /*
+ * This is not contiguous with what is currently displayed.
+ * Clear the screen image (position table) and start a new
+ * screen.
+ */
+ if (pos != position(BOTTOM_PLUS_ONE)) {
+ pos_clear();
+ add_forw_pos(pos);
+ if (top_scroll) {
+ clear();
+ home();
+ } else if (!first_time)
+ putstr("...skipping...\n");
+ }
+ }
+
+ for (short_file = 0; --n >= 0;) {
+ /*
+ * Read the next line of input.
+ */
+ pos = forw_line(pos);
+ if (pos == NULL_POSITION) {
+ /*
+ * end of file; copy the table if the file was
+ * too small for an entire screen.
+ */
+ eof = 1;
+ if (position(TOP) == NULL_POSITION) {
+ copytable();
+ if (!position(TOP))
+ short_file = 1;
+ }
+ break;
+ }
+ /*
+ * Add the position of the next line to the position table.
+ * Display the current line on the screen.
+ */
+ add_forw_pos(pos);
+ if (do_repaint)
+ continue;
+ /*
+ * If this is the first screen displayed and we hit an early
+ * EOF (i.e. before the requested number of lines), we
+ * "squish" the display down at the bottom of the screen.
+ * But don't do this if a -t option was given; it can cause
+ * us to start the display after the beginning of the file,
+ * and it is not appropriate to squish in that case.
+ */
+ if (first_time && line == NULL && !top_scroll && !tagoption) {
+ squished = 1;
+ continue;
+ }
+ put_line();
+ }
+
+ if (eof && !sigs)
+ hit_eof++;
+ else
+ eof_check();
+ if (do_repaint)
+ repaint();
+ first_time = 0;
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n lines, scrolling backward.
+ */
+back(n, pos, only_last)
+ register int n;
+ off_t pos;
+ int only_last;
+{
+ int do_repaint;
+
+ squish_check();
+ do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
+ hit_eof = 0;
+ while (--n >= 0)
+ {
+ /*
+ * Get the previous line of input.
+ */
+ pos = back_line(pos);
+ if (pos == NULL_POSITION)
+ break;
+ /*
+ * Add the position of the previous line to the position table.
+ * Display the line on the screen.
+ */
+ add_back_pos(pos);
+ if (!do_repaint)
+ {
+ if (retain_below)
+ {
+ lower_left();
+ clear_eol();
+ }
+ home();
+ add_line();
+ put_line();
+ }
+ }
+
+ eof_check();
+ if (do_repaint)
+ repaint();
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n more lines, forward.
+ * Start just after the line currently displayed at the bottom of the screen.
+ */
+forward(n, only_last)
+ int n;
+ int only_last;
+{
+ off_t pos;
+
+ if (hit_eof) {
+ /*
+ * If we're trying to go forward from end-of-file,
+ * go on to the next file.
+ */
+ next_file(1);
+ return;
+ }
+
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION)
+ {
+ hit_eof++;
+ return;
+ }
+ forw(n, pos, only_last);
+}
+
+/*
+ * Display n more lines, backward.
+ * Start just before the line currently displayed at the top of the screen.
+ */
+backward(n, only_last)
+ int n;
+ int only_last;
+{
+ off_t pos;
+
+ pos = position(TOP);
+ /*
+ * This will almost never happen, because the top line is almost
+ * never empty.
+ */
+ if (pos == NULL_POSITION)
+ return;
+ back(n, pos, only_last);
+}
+
+/*
+ * Repaint the screen, starting from a specified position.
+ */
+prepaint(pos)
+ off_t pos;
+{
+ hit_eof = 0;
+ forw(sc_height-1, pos, 0);
+ screen_trashed = 0;
+}
+
+/*
+ * Repaint the screen.
+ */
+repaint()
+{
+ /*
+ * Start at the line currently at the top of the screen
+ * and redisplay the screen.
+ */
+ prepaint(position(TOP));
+}
+
+/*
+ * Jump to the end of the file.
+ * It is more convenient to paint the screen backward,
+ * from the end of the file toward the beginning.
+ */
+jump_forw()
+{
+ off_t pos;
+
+ if (ch_end_seek())
+ {
+ error("Cannot seek to end of file");
+ return;
+ }
+ lastmark();
+ pos = ch_tell();
+ clear();
+ pos_clear();
+ add_back_pos(pos);
+ back(sc_height - 1, pos, 0);
+}
+
+/*
+ * Jump to line n in the file.
+ */
+jump_back(n)
+ register int n;
+{
+ register int c, nlines;
+
+ /*
+ * This is done the slow way, by starting at the beginning
+ * of the file and counting newlines.
+ *
+ * {{ Now that we have line numbering (in linenum.c),
+ * we could improve on this by starting at the
+ * nearest known line rather than at the beginning. }}
+ */
+ if (ch_seek((off_t)0)) {
+ /*
+ * Probably a pipe with beginning of file no longer buffered.
+ * If he wants to go to line 1, we do the best we can,
+ * by going to the first line which is still buffered.
+ */
+ if (n <= 1 && ch_beg_seek() == 0)
+ jump_loc(ch_tell());
+ error("Cannot get to beginning of file");
+ return;
+ }
+
+ /*
+ * Start counting lines.
+ */
+ for (nlines = 1; nlines < n; nlines++)
+ while ((c = ch_forw_get()) != '\n')
+ if (c == EOI) {
+ char message[40];
+ (void)sprintf(message, "File has only %d lines",
+ nlines - 1);
+ error(message);
+ return;
+ }
+ jump_loc(ch_tell());
+}
+
+/*
+ * Jump to a specified percentage into the file.
+ * This is a poor compensation for not being able to
+ * quickly jump to a specific line number.
+ */
+jump_percent(percent)
+ int percent;
+{
+ off_t pos, len, ch_length();
+ register int c;
+
+ /*
+ * Determine the position in the file
+ * (the specified percentage of the file's length).
+ */
+ if ((len = ch_length()) == NULL_POSITION)
+ {
+ error("Don't know length of file");
+ return;
+ }
+ pos = (percent * len) / 100;
+
+ /*
+ * Back up to the beginning of the line.
+ */
+ if (ch_seek(pos) == 0)
+ {
+ while ((c = ch_back_get()) != '\n' && c != EOI)
+ ;
+ if (c == '\n')
+ (void) ch_forw_get();
+ pos = ch_tell();
+ }
+ jump_loc(pos);
+}
+
+/*
+ * Jump to a specified position in the file.
+ */
+jump_loc(pos)
+ off_t pos;
+{
+ register int nline;
+ off_t tpos;
+
+ if ((nline = onscreen(pos)) >= 0) {
+ /*
+ * The line is currently displayed.
+ * Just scroll there.
+ */
+ forw(nline, position(BOTTOM_PLUS_ONE), 0);
+ return;
+ }
+
+ /*
+ * Line is not on screen.
+ * Seek to the desired location.
+ */
+ if (ch_seek(pos)) {
+ error("Cannot seek to that position");
+ return;
+ }
+
+ /*
+ * See if the desired line is BEFORE the currently displayed screen.
+ * If so, then move forward far enough so the line we're on will be
+ * at the bottom of the screen, in order to be able to call back()
+ * to make the screen scroll backwards & put the line at the top of
+ * the screen.
+ * {{ This seems inefficient, but it's not so bad,
+ * since we can never move forward more than a
+ * screenful before we stop to redraw the screen. }}
+ */
+ tpos = position(TOP);
+ if (tpos != NULL_POSITION && pos < tpos) {
+ off_t npos = pos;
+ /*
+ * Note that we can't forw_line() past tpos here,
+ * so there should be no EOI at this stage.
+ */
+ for (nline = 0; npos < tpos && nline < sc_height - 1; nline++)
+ npos = forw_line(npos);
+
+ if (npos < tpos) {
+ /*
+ * More than a screenful back.
+ */
+ lastmark();
+ clear();
+ pos_clear();
+ add_back_pos(npos);
+ }
+
+ /*
+ * Note that back() will repaint() if nline > back_scroll.
+ */
+ back(nline, npos, 0);
+ return;
+ }
+ /*
+ * Remember where we were; clear and paint the screen.
+ */
+ lastmark();
+ prepaint(pos);
+}
+
+/*
+ * The table of marks.
+ * A mark is simply a position in the file.
+ */
+#define NMARKS (27) /* 26 for a-z plus one for quote */
+#define LASTMARK (NMARKS-1) /* For quote */
+static off_t marks[NMARKS];
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+init_mark()
+{
+ int i;
+
+ for (i = 0; i < NMARKS; i++)
+ marks[i] = NULL_POSITION;
+}
+
+/*
+ * See if a mark letter is valid (between a and z).
+ */
+ static int
+badmark(c)
+ int c;
+{
+ if (c < 'a' || c > 'z')
+ {
+ error("Choose a letter between 'a' and 'z'");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Set a mark.
+ */
+setmark(c)
+ int c;
+{
+ if (badmark(c))
+ return;
+ marks[c-'a'] = position(TOP);
+}
+
+lastmark()
+{
+ marks[LASTMARK] = position(TOP);
+}
+
+/*
+ * Go to a previously set mark.
+ */
+gomark(c)
+ int c;
+{
+ off_t pos;
+
+ if (c == '\'') {
+ pos = marks[LASTMARK];
+ if (pos == NULL_POSITION)
+ pos = 0;
+ }
+ else {
+ if (badmark(c))
+ return;
+ pos = marks[c-'a'];
+ if (pos == NULL_POSITION) {
+ error("mark not set");
+ return;
+ }
+ }
+ jump_loc(pos);
+}
+
+/*
+ * Get the backwards scroll limit.
+ * Must call this function instead of just using the value of
+ * back_scroll, because the default case depends on sc_height and
+ * top_scroll, as well as back_scroll.
+ */
+get_back_scroll()
+{
+ if (back_scroll >= 0)
+ return (back_scroll);
+ if (top_scroll)
+ return (sc_height - 2);
+ return (sc_height - 1);
+}
+
+/*
+ * Search for the n-th occurence of a specified pattern,
+ * either forward or backward.
+ */
+search(search_forward, pattern, n, wantmatch)
+ register int search_forward;
+ register char *pattern;
+ register int n;
+ int wantmatch;
+{
+ off_t pos, linepos;
+ register char *p;
+ register char *q;
+ int linenum;
+ int linematch;
+#ifdef RECOMP
+ char *re_comp();
+ char *errmsg;
+#else
+#ifdef REGCMP
+ char *regcmp();
+ static char *cpattern = NULL;
+#else
+ static char lpbuf[100];
+ static char *last_pattern = NULL;
+ char *strcpy();
+#endif
+#endif
+
+ /*
+ * For a caseless search, convert any uppercase in the pattern to
+ * lowercase.
+ */
+ if (caseless && pattern != NULL)
+ for (p = pattern; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+#ifdef RECOMP
+
+ /*
+ * (re_comp handles a null pattern internally,
+ * so there is no need to check for a null pattern here.)
+ */
+ if ((errmsg = re_comp(pattern)) != NULL)
+ {
+ error(errmsg);
+ return(0);
+ }
+#else
+#ifdef REGCMP
+ if (pattern == NULL || *pattern == '\0')
+ {
+ /*
+ * A null pattern means use the previous pattern.
+ * The compiled previous pattern is in cpattern, so just use it.
+ */
+ if (cpattern == NULL)
+ {
+ error("No previous regular expression");
+ return(0);
+ }
+ } else
+ {
+ /*
+ * Otherwise compile the given pattern.
+ */
+ char *s;
+ if ((s = regcmp(pattern, 0)) == NULL)
+ {
+ error("Invalid pattern");
+ return(0);
+ }
+ if (cpattern != NULL)
+ free(cpattern);
+ cpattern = s;
+ }
+#else
+ if (pattern == NULL || *pattern == '\0')
+ {
+ /*
+ * Null pattern means use the previous pattern.
+ */
+ if (last_pattern == NULL)
+ {
+ error("No previous regular expression");
+ return(0);
+ }
+ pattern = last_pattern;
+ } else
+ {
+ (void)strcpy(lpbuf, pattern);
+ last_pattern = lpbuf;
+ }
+#endif
+#endif
+
+ /*
+ * Figure out where to start the search.
+ */
+
+ if (position(TOP) == NULL_POSITION) {
+ /*
+ * Nothing is currently displayed. Start at the beginning
+ * of the file. (This case is mainly for searches from the
+ * command line.
+ */
+ pos = (off_t)0;
+ } else if (!search_forward) {
+ /*
+ * Backward search: start just before the top line
+ * displayed on the screen.
+ */
+ pos = position(TOP);
+ } else {
+ /*
+ * Start at the second screen line displayed on the screen.
+ */
+ pos = position(TOP_PLUS_ONE);
+ }
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Can't find anyplace to start searching from.
+ */
+ error("Nothing to search");
+ return(0);
+ }
+
+ linenum = find_linenum(pos);
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or
+ * until we hit end-of-file (or beginning-of-file
+ * if we're going backwards).
+ */
+ if (sigs)
+ /*
+ * A signal aborts the search.
+ */
+ return(0);
+
+ if (search_forward)
+ {
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos);
+ if (linenum != 0)
+ linenum++;
+ } else
+ {
+ /*
+ * Read the previous line and save the
+ * starting position of that line in linepos.
+ */
+ pos = back_raw_line(pos);
+ linepos = pos;
+ if (linenum != 0)
+ linenum--;
+ }
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * We hit EOF/BOF without a match.
+ */
+ error("Pattern not found");
+ return(0);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ */
+ if (linenums)
+ add_lnum(linenum, pos);
+
+ /*
+ * If this is a caseless search, convert uppercase in the
+ * input line to lowercase.
+ */
+ if (caseless)
+ for (p = q = line; *p; p++, q++)
+ *q = isupper(*p) ? tolower(*p) : *p;
+
+ /*
+ * Remove any backspaces along with the preceeding char.
+ * This allows us to match text which is underlined or
+ * overstruck.
+ */
+ for (p = q = line; *p; p++, q++)
+ if (q > line && *p == '\b')
+ /* Delete BS and preceeding char. */
+ q -= 2;
+ else
+ /* Otherwise, just copy. */
+ *q = *p;
+
+ /*
+ * Test the next line to see if we have a match.
+ * This is done in a variety of ways, depending
+ * on what pattern matching functions are available.
+ */
+#ifdef REGCMP
+ linematch = (regex(cpattern, line) != NULL);
+#else
+#ifdef RECOMP
+ linematch = (re_exec(line) == 1);
+#else
+ linematch = match(pattern, line);
+#endif
+#endif
+ /*
+ * We are successful if wantmatch and linematch are
+ * both true (want a match and got it),
+ * or both false (want a non-match and got it).
+ */
+ if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
+ --n <= 0)
+ /*
+ * Found the line.
+ */
+ break;
+ }
+ jump_loc(linepos);
+ return(1);
+}
+
+#if !defined(REGCMP) && !defined(RECOMP)
+/*
+ * We have neither regcmp() nor re_comp().
+ * We use this function to do simple pattern matching.
+ * It supports no metacharacters like *, etc.
+ */
+static
+match(pattern, buf)
+ char *pattern, *buf;
+{
+ register char *pp, *lp;
+
+ for ( ; *buf != '\0'; buf++)
+ {
+ for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
+ if (*pp == '\0' || *lp == '\0')
+ break;
+ if (*pp == '\0')
+ return (1);
+ }
+ return (0);
+}
+#endif
diff --git a/usr.bin/more/screen.c b/usr.bin/more/screen.c
new file mode 100644
index 0000000..edfae1e
--- /dev/null
+++ b/usr.bin/more/screen.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)screen.c 8.2 (Berkeley) 4/20/94";
+#endif /* not lint */
+
+/*
+ * Routines which deal with the characteristics of the terminal.
+ * Uses termcap to be as terminal-independent as possible.
+ *
+ * {{ Someday this should be rewritten to use curses. }}
+ */
+
+#include <stdio.h>
+#include <less.h>
+
+#define TERMIOS 1
+
+#if TERMIO
+#include <termio.h>
+#else
+#if TERMIOS
+#include <termios.h>
+#define TAB3 0
+#include <sys/ioctl.h>
+#else
+#include <sgtty.h>
+#endif
+#endif
+
+#ifdef TIOCGWINSZ
+#include <sys/ioctl.h>
+#else
+/*
+ * For the Unix PC (ATT 7300 & 3B1):
+ * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
+ * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
+ */
+#include <sys/signal.h>
+#ifdef SIGPHONE
+#include <sys/window.h>
+#endif
+#endif
+
+/*
+ * Strings passed to tputs() to do various terminal functions.
+ */
+static char
+ *sc_pad, /* Pad string */
+ *sc_home, /* Cursor home */
+ *sc_addline, /* Add line, scroll down following lines */
+ *sc_lower_left, /* Cursor to last line, first column */
+ *sc_move, /* General cursor positioning */
+ *sc_clear, /* Clear screen */
+ *sc_eol_clear, /* Clear to end of line */
+ *sc_s_in, /* Enter standout (highlighted) mode */
+ *sc_s_out, /* Exit standout mode */
+ *sc_u_in, /* Enter underline mode */
+ *sc_u_out, /* Exit underline mode */
+ *sc_b_in, /* Enter bold mode */
+ *sc_b_out, /* Exit bold mode */
+ *sc_backspace, /* Backspace cursor */
+ *sc_init, /* Startup terminal initialization */
+ *sc_deinit; /* Exit terminal de-intialization */
+
+int auto_wrap; /* Terminal does \r\n when write past margin */
+int ignaw; /* Terminal ignores \n immediately after wrap */
+ /* The user's erase and line-kill chars */
+int retain_below; /* Terminal retains text below the screen */
+int erase_char, kill_char, werase_char;
+int sc_width, sc_height = -1; /* Height & width of screen */
+int sc_window = -1; /* window size for forward and backward */
+int bo_width, be_width; /* Printing width of boldface sequences */
+int ul_width, ue_width; /* Printing width of underline sequences */
+int so_width, se_width; /* Printing width of standout sequences */
+
+/*
+ * These two variables are sometimes defined in,
+ * and needed by, the termcap library.
+ * It may be necessary on some systems to declare them extern here.
+ */
+/*extern*/ short ospeed; /* Terminal output baud rate */
+/*extern*/ char PC; /* Pad character */
+
+extern int back_scroll;
+char *tgetstr();
+char *tgoto();
+
+/*
+ * Change terminal to "raw mode", or restore to "normal" mode.
+ * "Raw mode" means
+ * 1. An outstanding read will complete on receipt of a single keystroke.
+ * 2. Input is not echoed.
+ * 3. On output, \n is mapped to \r\n.
+ * 4. \t is NOT expanded into spaces.
+ * 5. Signal-causing characters such as ctrl-C (interrupt),
+ * etc. are NOT disabled.
+ * It doesn't matter whether an input \n is mapped to \r, or vice versa.
+ */
+raw_mode(on)
+ int on;
+{
+#if TERMIO || TERMIOS
+
+#if TERMIO
+ struct termio s;
+ static struct termio save_term;
+#else
+ struct termios s;
+ static struct termios save_term;
+#endif
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+#if TERMIO
+ (void)ioctl(2, TCGETA, &s);
+#else
+ tcgetattr(2, &s);
+#endif
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+#if TERMIO
+ ospeed = s.c_cflag & CBAUD;
+#else
+ ospeed = cfgetospeed(&s);
+#endif
+ erase_char = s.c_cc[VERASE];
+ kill_char = s.c_cc[VKILL];
+ werase_char = s.c_cc[VWERASE];
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+ s.c_oflag |= (OPOST|ONLCR|TAB3);
+#if TERMIO
+ s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
+#endif
+ s.c_cc[VMIN] = 1;
+ s.c_cc[VTIME] = 0;
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+#if TERMIO
+ (void)ioctl(2, TCSETAW, &s);
+#else
+ tcsetattr(2, TCSADRAIN, &s);
+#endif
+#else
+ struct sgttyb s;
+ struct ltchars l;
+ static struct sgttyb save_term;
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+ (void)ioctl(2, TIOCGETP, &s);
+ (void)ioctl(2, TIOCGLTC, &l);
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+ ospeed = s.sg_ospeed;
+ erase_char = s.sg_erase;
+ kill_char = s.sg_kill;
+ werase_char = l.t_werasc;
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.sg_flags |= CBREAK;
+ s.sg_flags &= ~(ECHO|XTABS);
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+ (void)ioctl(2, TIOCSETN, &s);
+#endif
+}
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+get_term()
+{
+ char termbuf[2048];
+ char *sp;
+ char *term;
+ int hard;
+#ifdef TIOCGWINSZ
+ struct winsize w;
+#else
+#ifdef WIOCGETD
+ struct uwdata w;
+#endif
+#endif
+ static char sbuf[1024];
+
+ char *getenv(), *strcpy();
+
+ /*
+ * Find out what kind of terminal this is.
+ */
+ if ((term = getenv("TERM")) == NULL)
+ term = "unknown";
+ if (tgetent(termbuf, term) <= 0)
+ (void)strcpy(termbuf, "dumb:co#80:hc:");
+
+ /*
+ * Get size of the screen.
+ */
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
+ sc_height = w.ws_row;
+#else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
+ sc_height = w.uw_height/w.uw_vs;
+#endif
+#endif
+ else
+ sc_height = tgetnum("li");
+ hard = (sc_height < 0 || tgetflag("hc"));
+ if (hard) {
+ /* Oh no, this is a hardcopy terminal. */
+ sc_height = 24;
+ }
+
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
+ sc_width = w.ws_col;
+ else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
+ sc_width = w.uw_width/w.uw_hs;
+ else
+#endif
+#endif
+ sc_width = tgetnum("co");
+ if (sc_width < 0)
+ sc_width = 80;
+
+ auto_wrap = tgetflag("am");
+ ignaw = tgetflag("xn");
+ retain_below = tgetflag("db");
+
+ /*
+ * Assumes termcap variable "sg" is the printing width of
+ * the standout sequence, the end standout sequence,
+ * the underline sequence, the end underline sequence,
+ * the boldface sequence, and the end boldface sequence.
+ */
+ if ((so_width = tgetnum("sg")) < 0)
+ so_width = 0;
+ be_width = bo_width = ue_width = ul_width = se_width = so_width;
+
+ /*
+ * Get various string-valued capabilities.
+ */
+ sp = sbuf;
+
+ sc_pad = tgetstr("pc", &sp);
+ if (sc_pad != NULL)
+ PC = *sc_pad;
+
+ sc_init = tgetstr("ti", &sp);
+ if (sc_init == NULL)
+ sc_init = "";
+
+ sc_deinit= tgetstr("te", &sp);
+ if (sc_deinit == NULL)
+ sc_deinit = "";
+
+ sc_eol_clear = tgetstr("ce", &sp);
+ if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
+ {
+ sc_eol_clear = "";
+ }
+
+ sc_clear = tgetstr("cl", &sp);
+ if (hard || sc_clear == NULL || *sc_clear == '\0')
+ {
+ sc_clear = "\n\n";
+ }
+
+ sc_move = tgetstr("cm", &sp);
+ if (hard || sc_move == NULL || *sc_move == '\0')
+ {
+ /*
+ * This is not an error here, because we don't
+ * always need sc_move.
+ * We need it only if we don't have home or lower-left.
+ */
+ sc_move = "";
+ }
+
+ sc_s_in = tgetstr("so", &sp);
+ if (hard || sc_s_in == NULL)
+ sc_s_in = "";
+
+ sc_s_out = tgetstr("se", &sp);
+ if (hard || sc_s_out == NULL)
+ sc_s_out = "";
+
+ sc_u_in = tgetstr("us", &sp);
+ if (hard || sc_u_in == NULL)
+ sc_u_in = sc_s_in;
+
+ sc_u_out = tgetstr("ue", &sp);
+ if (hard || sc_u_out == NULL)
+ sc_u_out = sc_s_out;
+
+ sc_b_in = tgetstr("md", &sp);
+ if (hard || sc_b_in == NULL)
+ {
+ sc_b_in = sc_s_in;
+ sc_b_out = sc_s_out;
+ } else
+ {
+ sc_b_out = tgetstr("me", &sp);
+ if (hard || sc_b_out == NULL)
+ sc_b_out = "";
+ }
+
+ sc_home = tgetstr("ho", &sp);
+ if (hard || sc_home == NULL || *sc_home == '\0')
+ {
+ if (*sc_move == '\0')
+ {
+ /*
+ * This last resort for sc_home is supposed to
+ * be an up-arrow suggesting moving to the
+ * top of the "virtual screen". (The one in
+ * your imagination as you try to use this on
+ * a hard copy terminal.)
+ */
+ sc_home = "|\b^";
+ } else
+ {
+ /*
+ * No "home" string,
+ * but we can use "move(0,0)".
+ */
+ (void)strcpy(sp, tgoto(sc_move, 0, 0));
+ sc_home = sp;
+ sp += strlen(sp) + 1;
+ }
+ }
+
+ sc_lower_left = tgetstr("ll", &sp);
+ if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
+ {
+ if (*sc_move == '\0')
+ {
+ sc_lower_left = "\r";
+ } else
+ {
+ /*
+ * No "lower-left" string,
+ * but we can use "move(0,last-line)".
+ */
+ (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1));
+ sc_lower_left = sp;
+ sp += strlen(sp) + 1;
+ }
+ }
+
+ /*
+ * To add a line at top of screen and scroll the display down,
+ * we use "al" (add line) or "sr" (scroll reverse).
+ */
+ if ((sc_addline = tgetstr("al", &sp)) == NULL ||
+ *sc_addline == '\0')
+ sc_addline = tgetstr("sr", &sp);
+
+ if (hard || sc_addline == NULL || *sc_addline == '\0')
+ {
+ sc_addline = "";
+ /* Force repaint on any backward movement */
+ back_scroll = 0;
+ }
+
+ if (tgetflag("bs"))
+ sc_backspace = "\b";
+ else
+ {
+ sc_backspace = tgetstr("bc", &sp);
+ if (sc_backspace == NULL || *sc_backspace == '\0')
+ sc_backspace = "\b";
+ }
+}
+
+
+/*
+ * Below are the functions which perform all the
+ * terminal-specific screen manipulation.
+ */
+
+int putchr();
+
+/*
+ * Initialize terminal
+ */
+init()
+{
+ tputs(sc_init, sc_height, putchr);
+}
+
+/*
+ * Deinitialize terminal
+ */
+deinit()
+{
+ tputs(sc_deinit, sc_height, putchr);
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+home()
+{
+ tputs(sc_home, 1, putchr);
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+add_line()
+{
+ tputs(sc_addline, sc_height, putchr);
+}
+
+int short_file; /* if file less than a screen */
+lower_left()
+{
+ if (short_file) {
+ putchr('\r');
+ flush();
+ }
+ else
+ tputs(sc_lower_left, 1, putchr);
+}
+
+/*
+ * Ring the terminal bell.
+ */
+bell()
+{
+ putchr('\7');
+}
+
+/*
+ * Clear the screen.
+ */
+clear()
+{
+ tputs(sc_clear, sc_height, putchr);
+}
+
+/*
+ * Clear from the cursor to the end of the cursor's line.
+ * {{ This must not move the cursor. }}
+ */
+clear_eol()
+{
+ tputs(sc_eol_clear, 1, putchr);
+}
+
+/*
+ * Begin "standout" (bold, underline, or whatever).
+ */
+so_enter()
+{
+ tputs(sc_s_in, 1, putchr);
+}
+
+/*
+ * End "standout".
+ */
+so_exit()
+{
+ tputs(sc_s_out, 1, putchr);
+}
+
+/*
+ * Begin "underline" (hopefully real underlining,
+ * otherwise whatever the terminal provides).
+ */
+ul_enter()
+{
+ tputs(sc_u_in, 1, putchr);
+}
+
+/*
+ * End "underline".
+ */
+ul_exit()
+{
+ tputs(sc_u_out, 1, putchr);
+}
+
+/*
+ * Begin "bold"
+ */
+bo_enter()
+{
+ tputs(sc_b_in, 1, putchr);
+}
+
+/*
+ * End "bold".
+ */
+bo_exit()
+{
+ tputs(sc_b_out, 1, putchr);
+}
+
+/*
+ * Erase the character to the left of the cursor
+ * and move the cursor left.
+ */
+backspace()
+{
+ /*
+ * Try to erase the previous character by overstriking with a space.
+ */
+ tputs(sc_backspace, 1, putchr);
+ putchr(' ');
+ tputs(sc_backspace, 1, putchr);
+}
+
+/*
+ * Output a plain backspace, without erasing the previous char.
+ */
+putbs()
+{
+ tputs(sc_backspace, 1, putchr);
+}
diff --git a/usr.bin/more/signal.c b/usr.bin/more/signal.c
new file mode 100644
index 0000000..67d7e51
--- /dev/null
+++ b/usr.bin/more/signal.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)signal.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with signals.
+ *
+ * A signal usually merely causes a bit to be set in the "signals" word.
+ * At some convenient time, the mainline code checks to see if any
+ * signals need processing by calling psignal().
+ * If we happen to be reading from a file [in iread()] at the time
+ * the signal is received, we call intread to interrupt the iread.
+ */
+
+#include <less.h>
+#include <signal.h>
+
+/*
+ * "sigs" contains bits indicating signals which need to be processed.
+ */
+int sigs;
+
+#ifdef SIGTSTP
+#define S_STOP 02
+#endif
+#if defined(SIGWINCH) || defined(SIGWIND)
+#define S_WINCH 04
+#endif
+
+extern int sc_width, sc_height;
+extern int screen_trashed;
+extern int lnloop;
+extern int linenums;
+extern int scroll;
+extern int reading;
+
+#ifdef SIGTSTP
+/*
+ * "Stop" (^Z) signal handler.
+ */
+static void
+stop()
+{
+ (void)signal(SIGTSTP, stop);
+ sigs |= S_STOP;
+ if (reading)
+ intread();
+}
+#endif
+
+#ifdef SIGWINCH
+/*
+ * "Window" change handler
+ */
+void
+winch()
+{
+ (void)signal(SIGWINCH, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#else
+#ifdef SIGWIND
+/*
+ * "Window" change handler
+ */
+winch()
+{
+ (void)signal(SIGWIND, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#endif
+#endif
+
+static void
+purgeandquit()
+{
+
+ purge(); /* purge buffered output */
+ quit();
+}
+
+/*
+ * Set up the signal handlers.
+ */
+init_signals(on)
+ int on;
+{
+ if (on)
+ {
+ /*
+ * Set signal handlers.
+ */
+ (void)signal(SIGINT, purgeandquit);
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, stop);
+#endif
+#ifdef SIGWINCH
+ (void)signal(SIGWINCH, winch);
+#else
+#ifdef SIGWIND
+ (void)signal(SIGWIND, winch);
+#endif
+#endif
+ } else
+ {
+ /*
+ * Restore signals to defaults.
+ */
+ (void)signal(SIGINT, SIG_DFL);
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, SIG_DFL);
+#endif
+#ifdef SIGWINCH
+ (void)signal(SIGWINCH, SIG_IGN);
+#endif
+#ifdef SIGWIND
+ (void)signal(SIGWIND, SIG_IGN);
+#endif
+ }
+}
+
+/*
+ * Process any signals we have received.
+ * A received signal cause a bit to be set in "sigs".
+ */
+psignals()
+{
+ register int tsignals;
+
+ if ((tsignals = sigs) == 0)
+ return;
+ sigs = 0;
+
+#ifdef S_WINCH
+ if (tsignals & S_WINCH)
+ {
+ int old_width, old_height;
+ /*
+ * Re-execute get_term() to read the new window size.
+ */
+ old_width = sc_width;
+ old_height = sc_height;
+ get_term();
+ if (sc_width != old_width || sc_height != old_height)
+ {
+ scroll = (sc_height + 1) / 2;
+ screen_trashed = 1;
+ }
+ }
+#endif
+#ifdef SIGTSTP
+ if (tsignals & S_STOP)
+ {
+ /*
+ * Clean up the terminal.
+ */
+#ifdef SIGTTOU
+ (void)signal(SIGTTOU, SIG_IGN);
+#endif
+ lower_left();
+ clear_eol();
+ deinit();
+ (void)flush();
+ raw_mode(0);
+#ifdef SIGTTOU
+ (void)signal(SIGTTOU, SIG_DFL);
+#endif
+ (void)signal(SIGTSTP, SIG_DFL);
+ (void)kill(getpid(), SIGTSTP);
+ /*
+ * ... Bye bye. ...
+ * Hopefully we'll be back later and resume here...
+ * Reset the terminal and arrange to repaint the
+ * screen when we get back to the main command loop.
+ */
+ (void)signal(SIGTSTP, stop);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+ }
+#endif
+}
diff --git a/usr.bin/more/tags.c b/usr.bin/more/tags.c
new file mode 100644
index 0000000..029557e
--- /dev/null
+++ b/usr.bin/more/tags.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tags.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <less.h>
+
+#define WHITESP(c) ((c)==' ' || (c)=='\t')
+
+char *tagfile;
+char *tagpattern;
+
+static char *tags = "tags";
+
+extern int linenums;
+extern int sigs;
+extern char *line;
+
+/*
+ * Find a tag in the "tags" file.
+ * Sets "tagfile" to the name of the file containing the tag,
+ * and "tagpattern" to the search pattern which should be used
+ * to find the tag.
+ */
+findtag(tag)
+ register char *tag;
+{
+ register char *p;
+ register FILE *f;
+ register int taglen;
+ int search_char;
+ static char tline[200];
+
+ if ((f = fopen(tags, "r")) == NULL)
+ {
+ error("No tags file");
+ tagfile = NULL;
+ return;
+ }
+
+ taglen = strlen(tag);
+
+ /*
+ * Search the tags file for the desired tag.
+ */
+ while (fgets(tline, sizeof(tline), f) != NULL)
+ {
+ if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
+ continue;
+
+ /*
+ * Found it.
+ * The line contains the tag, the filename and the
+ * pattern, separated by white space.
+ * The pattern is surrounded by a pair of identical
+ * search characters.
+ * Parse the line and extract these parts.
+ */
+ tagfile = tagpattern = NULL;
+
+ /*
+ * Skip over the whitespace after the tag name.
+ */
+ for (p = tline; !WHITESP(*p) && *p != '\0'; p++)
+ continue;
+ while (WHITESP(*p))
+ p++;
+ if (*p == '\0')
+ /* File name is missing! */
+ continue;
+
+ /*
+ * Save the file name.
+ * Skip over the whitespace after the file name.
+ */
+ tagfile = p;
+ while (!WHITESP(*p) && *p != '\0')
+ p++;
+ *p++ = '\0';
+ while (WHITESP(*p))
+ p++;
+ if (*p == '\0')
+ /* Pattern is missing! */
+ continue;
+
+ /*
+ * Save the pattern.
+ * Skip to the end of the pattern.
+ * Delete the initial "^" and the final "$" from the pattern.
+ */
+ search_char = *p++;
+ if (*p == '^')
+ p++;
+ tagpattern = p;
+ while (*p != search_char && *p != '\0')
+ p++;
+ if (p[-1] == '$')
+ p--;
+ *p = '\0';
+
+ (void)fclose(f);
+ return;
+ }
+ (void)fclose(f);
+ error("No such tag in tags file");
+ tagfile = NULL;
+}
+
+/*
+ * Search for a tag.
+ * This is a stripped-down version of search().
+ * We don't use search() for several reasons:
+ * - We don't want to blow away any search string we may have saved.
+ * - The various regular-expression functions (from different systems:
+ * regcmp vs. re_comp) behave differently in the presence of
+ * parentheses (which are almost always found in a tag).
+ */
+tagsearch()
+{
+ off_t pos, linepos, forw_raw_line();
+ int linenum;
+
+ pos = (off_t)0;
+ linenum = find_linenum(pos);
+
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or
+ * until we hit end-of-file.
+ */
+ if (sigs)
+ return (1);
+
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos);
+ if (linenum != 0)
+ linenum++;
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * We hit EOF without a match.
+ */
+ error("Tag not found");
+ return (1);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ */
+ if (linenums)
+ add_lnum(linenum, pos);
+
+ /*
+ * Test the line to see if we have a match.
+ */
+ if (strcmp(tagpattern, line) == 0)
+ break;
+ }
+
+ jump_loc(linepos);
+ return (0);
+}
diff --git a/usr.bin/more/ttyin.c b/usr.bin/more/ttyin.c
new file mode 100644
index 0000000..52ff92e
--- /dev/null
+++ b/usr.bin/more/ttyin.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttyin.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with getting input from the keyboard (i.e. from the user).
+ */
+
+#include <less.h>
+
+static int tty;
+
+/*
+ * Open keyboard for input.
+ * (Just use file descriptor 2.)
+ */
+open_getchr()
+{
+ tty = 2;
+}
+
+/*
+ * Get a character from the keyboard.
+ */
+getchr()
+{
+ char c;
+ int result;
+
+ do
+ {
+ result = iread(tty, &c, 1);
+ if (result == READ_INTR)
+ return (READ_INTR);
+ if (result < 0)
+ {
+ /*
+ * Don't call error() here,
+ * because error calls getchr!
+ */
+ quit();
+ }
+ } while (result != 1);
+ return (c & 0177);
+}
OpenPOWER on IntegriCloud