Add 4.4BSD more(1) from UUNET. Patched to support regex(3)

and implement historical more commands.
This commit is contained in:
alm 1993-11-09 02:59:05 +00:00
parent 0605e44a71
commit bc24dd374e
23 changed files with 6155 additions and 357 deletions

View File

@ -1,14 +1,17 @@
# from: @(#)Makefile 5.11 (Berkeley) 6/25/90
# $Id: Makefile,v 1.2 1993/07/31 15:18:48 mycroft Exp $
# @(#)Makefile 5.6 (Berkeley) 3/12/91
PROG= more
DPADD= ${LIBTERM}
LDADD= -ltermcap
CFLAGS+=-I${.CURDIR} -DREGEX
SRCS= ch.c command.c decode.c filename.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
LDADD+= -ltermcap -lgnuregex
DPADD+= ${LIBTERM} /usr/lib/libgnuregex.a
MLINKS= more.1 page.1
LINKS= ${BINDIR}/more ${BINDIR}/page
beforeinstall:
install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/more.help \
${DESTDIR}/usr/share/misc/omore.help
${DESTDIR}/usr/share/misc
.include <bsd.prog.mk>

454
usr.bin/more/ch.c Normal file
View File

@ -0,0 +1,454 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.11 (Berkeley) 6/21/92";
#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);
}

675
usr.bin/more/command.c Normal file
View File

@ -0,0 +1,675 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.22 (Berkeley) 6/21/92";
#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 char *shellcmd = NULL; /* Pointer to a shell command */
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 %ld", pos);
putstr(pbuf);
if (!ispipe && (len = ch_length())) {
(void)sprintf(pbuf, "/%ld pct %ld%%",
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, " (%ld%%)", ((100 * pos) / len));
putstr(pbuf);
}
so_exit();
}
return(1);
}
/* get command character. */
static
getcc()
{
extern int cmdstack;
int ch;
/* 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(), *fexpand();
*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_SHELL:
/*
* Copy cmdbuf to shellcmd,
* expanding any special characters ("%" or "#"
* or an initial !).
*/
if ((p = fexpand(cmdbuf, shellcmd)) == NULL)
break;
else if (shellcmd != NULL)
free(shellcmd);
lsystem(shellcmd = p);
error("!done");
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_SHELL:
/*
* Shell escape.
*/
start_mca(A_SHELL, "!");
c = getcc();
goto again;
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);
}

207
usr.bin/more/decode.c Normal file
View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.9 (Berkeley) 3/1/91";
#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,
's',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,
'f',0, A_F_SCROLL,
'u',0, A_B_SCROLL,
CONTROL('U'),0, A_B_SCROLL,
' ',0, A_F_SCREEN,
'z',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,
':','f',0, A_STAT,
'/',0, A_F_SEARCH,
'?',0, A_B_SEARCH,
'!',0, A_SHELL,
':','!',0, A_SHELL,
'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,
':','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);
}

100
usr.bin/more/filename.c Normal file
View File

@ -0,0 +1,100 @@
/*
* Expand a string, substituting any "%" with the current filename,
* and any "#" with the previous filename and an initial "!" with
* the second string.
*/
char *
fexpand(s, t)
char *s;
char *t;
{
extern char *current_file, *previous_file;
register char *fr, *to;
register int n;
register char *e;
if (*s == '\0')
return ((char *) 0);
/*
* Make one pass to see how big a buffer we
* need to allocate for the expanded string.
*/
n = 0;
for (fr = s; *fr != '\0'; fr++)
{
switch (*fr)
{
case '!':
if (s == fr && t == (char *) 0) {
error("no previous command");
return ((char *) 0);
}
n += (s == fr) ? strlen(t) : 1;
break;
case '%':
n += (current_file != (char *) 0) ?
strlen(current_file) : 1;
break;
case '#':
n += (previous_file != (char *) 0) ?
strlen(previous_file) : 1;
break;
default:
n++;
if (*fr == '\\') {
n++;
if (*++fr == '\0') {
error("syntax error");
return ((char *) 0);
}
}
break;
}
}
if ((e = (char *) calloc(n+1, sizeof(char))) == (char *) 0) {
error("cannot allocate memory");
quit();
}
/*
* Now copy the string, expanding any "%" or "#".
*/
to = e;
for (fr = s; *fr != '\0'; fr++)
{
switch (*fr)
{
case '!':
if (s == fr) {
strcpy(to, t);
to += strlen(to);
} else
*to++ = *fr;
break;
case '%':
if (current_file == (char *) 0)
*to++ = *fr;
else {
strcpy(to, current_file);
to += strlen(to);
}
break;
case '#':
if (previous_file == (char *) 0)
*to++ = *fr;
else {
strcpy(to, previous_file);
to += strlen(to);
}
break;
default:
*to++ = *fr;
if (*fr == '\\')
*to++ = *++fr;
break;
}
}
*to = '\0';
return (e);
}

49
usr.bin/more/help.c Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.7 (Berkeley) 6/1/90";
#endif /* not lint */
#include <sys/param.h>
#include <less.h>
#include "pathnames.h"
help()
{
char cmd[MAXPATHLEN + 20];
(void)sprintf(cmd, "-more -ce %s", _PATH_HELPFILE);
lsystem(cmd);
}

241
usr.bin/more/input.c Normal file
View File

@ -0,0 +1,241 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.4 (Berkeley) 6/1/90";
#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);
}

88
usr.bin/more/less.h Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.9 (Berkeley) 6/1/90
*/
#undef 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
#define A_SHELL 28

508
usr.bin/more/line.c Normal file
View File

@ -0,0 +1,508 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.5 (Berkeley) 7/24/91";
#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);
}

383
usr.bin/more/linenum.c Normal file
View File

@ -0,0 +1,383 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.6 (Berkeley) 6/1/90";
#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));
}

367
usr.bin/more/main.c Normal file
View File

@ -0,0 +1,367 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)main.c 5.13 (Berkeley) 6/1/90";
#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);
}

View File

@ -1,4 +1,5 @@
.\" Copyright (c) 1980 The Regents of the University of California.
.\" Copyright (c) 1988, 1990 The Regents of the University of California.
.\" Copyright (c) 1988 Mark Nudleman
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -29,329 +30,276 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" from: @(#)more.1 6.6 (Berkeley) 4/18/91
.\" $Id: more.1,v 1.2 1993/08/01 07:30:35 mycroft Exp $
.\" @(#)more.1 5.15 (Berkeley) 7/29/91
.\"
.TH MORE 1 "April 18, 1991"
.UC 4
.SH NAME
more, page \- file perusal filter for crt viewing
.SH SYNOPSIS
.B more
[
.B \-cdflsu
]
[
.B \-\fIn\fP
]
[
.B +\fIlinenumber\fP
]
[
.B +/\fIpattern\fP
] [ name ... ]
.LP
.B page
.I "more options"
.SH DESCRIPTION
.I More
is a filter which allows examination of a continuous text
one screenful at a time on a soft-copy terminal.
It normally pauses after each screenful, printing --More--
at the bottom of the screen.
If the user then types a carriage return, one more line is displayed.
If the user hits a space,
another screenful is displayed. Other possibilities are enumerated later.
.PP
The command line options are:
.TP
.I \-n
An integer which is the size (in lines) of the window which
.I more
will use instead of the default.
.TP
.B \-c
.I More
will draw each page by beginning at the top of the screen and erasing
each line just before it draws on it.
This avoids scrolling the screen, making it easier to read while
.I more
is writing.
This option will be ignored if the terminal does not have the ability
to clear to the end of a line.
.TP
.B \-d
.I More
will prompt the user with the message "Press
space to continue, \'q\' to quit." at the end of each screenful,
and will respond to subsequent illegal user input by
printing "Press \'h\' for instructions." instead of ringing the bell.
This is useful if
.I more
is being used as a filter in some setting,
such as a class,
where many users may be unsophisticated.
.TP
.B \-f
This causes
.I more
to count logical, rather than screen lines.
That is, long lines are not folded.
This option is recommended if
.I nroff
output is being piped through
.I ul,
since the latter may generate escape sequences.
These escape sequences contain characters which would ordinarily occupy
screen positions, but which do not print when they are sent to the
terminal as part of an escape sequence.
Thus
.I more
may think that lines are longer than they actually are, and fold
lines erroneously.
.TP
.B \-l
Do
not treat ^\&L (form feed) specially.
If this option is not given,
.I more
will pause after any line that contains a ^\&L, as if the end of a
screenful had been reached.
Also, if a file begins with a form feed, the screen will be cleared
before the file is printed.
.TP
.B \-s
Squeeze multiple blank lines from the output, producing only one blank
line. Especially helpful when viewing
.I nroff
output, this option maximizes the useful information present on the screen.
.TP
.B \-u
.Dd July 29, 1991
.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,
.I more
will handle underlining such as produced by
.I nroff
in a manner appropriate to the particular terminal: if the terminal can
perform underlining or has a stand-out mode,
.I more
will output appropriate escape sequences to enable underlining or stand-out
mode for underlined information in the source file. The
.I \-u
option suppresses this processing.
.TP
.B +\fIlinenumber\fP
Start up at \fIlinenumber\fP.
.TP
.B +/\fIpattern\fP
Start up two lines before the line containing the
regular expression \fIpattern\fP.
.PP
If the program is invoked as
.I page,
then the screen is cleared before each screenful is printed (but only
if a full screenful is being printed), and
.I k
\- 1 rather
than
.I k
\- 2 lines are printed in each screenful, where
.I k
is the number of lines the terminal can display.
.PP
.I More
looks in the file
.I /etc/termcap
to determine terminal characteristics,
and to determine the default window size.
On a terminal capable of displaying 24 lines,
the default window size is 22 lines.
.PP
.I More
looks in the environment variable
.I MORE
to pre-set any flags desired. For example, if you prefer to view files using
the
.I \-c
mode of operation, the
.I csh
command
.I "setenv MORE -c"
or the
.I sh
command sequence
.I "MORE='-c' ; export MORE"
would cause all invocations of
.I more ,
including invocations by programs such as
.I man
and
.I msgs ,
to use this mode.
Normally, the user will place the command sequence which sets up the
.I MORE
environment variable in the
.I .cshrc
or
.I .profile
file.
.PP
If
.I more
is reading from a file, rather than a pipe, then a percentage is displayed
along with the --More-- prompt.
This gives the fraction of the file (in characters, not lines) that has been
read so far.
.PP
Other sequences which may be typed when
.I more
pauses, and their effects, are as follows (\fIi\fP is an optional integer
argument, defaulting to 1) :
.PP
.IP \fIi\|\fP<space>
display
.I i
more lines, (or another screenful if no argument is given)
.PP
.IP ^D
display 11 more lines (a ``scroll'').
If
.I i
is given, then the scroll size is set to \fIi\|\fP.
.PP
.IP d
same as ^D (control-D)
.PP
.IP \fIi\|\fPz
same as typing a space except that \fIi\|\fP, if present, becomes the new
window size.
.PP
.IP \fIi\|\fPs
skip \fIi\|\fP lines and print a screenful of lines
.PP
.IP \fIi\|\fPf
skip \fIi\fP screenfuls and print a screenful of lines
.PP
.IP \fIi\|\fPb
skip back \fIi\fP screenfuls and print a screenful of lines
.PP
.IP \fIi\|\fP^B
same as b
.PP
.IP "q or Q"
Exit from
.I more.
.PP
.IP =
Display the current line number.
.PP
.IP v
Start up the editor
.I vi
at the current line.
.PP
.IP h
Help command; give a description of all the
.I more
commands.
.PP
.IP \fIi\|\fP/expr
search for the \fIi\|\fP-th occurrence of the regular expression \fIexpr.\fP
If there are less than \fIi\fP occurrences of \fIexpr\|\fP,
and the input is a file (rather than a pipe),
then the position in the file remains unchanged.
Otherwise, a screenful is displayed, starting two lines before the place
where the expression was found.
The user's erase and kill characters may be used to edit the regular
expression.
Erasing back past the first column cancels the search command.
.PP
.IP \fIi\|\fPn
search for the \fIi\|\fP-th occurrence of the last regular expression entered.
.PP
.IP \'
(single quote) Go to the point from which the last search started.
If no search has been performed in the current file, this command
goes back to the beginning of the file.
.PP
.IP !command
invoke a shell with \fIcommand\|\fP.
The characters `%' and `!' in "command" are replaced with the
current file name and the previous shell command respectively.
If there is no current file name, `%' is not expanded.
The sequences "\\%" and "\\!" are replaced by "%" and "!" respectively.
.PP
.IP \fIi\|\fP:n
skip to the \fIi\|\fP-th next file given in the command line
(skips to last file if n doesn't make sense)
.PP
.IP \fIi\|\fP:p
skip to the \fIi\|\fP-th previous file given in the command line.
If this command is given in the middle of printing out a
file, then
.I more
goes back to the beginning of the file. If \fIi\fP doesn't make sense,
.I more
skips back to the first file.
If
.I more
is not reading from a file, the bell is rung and nothing else happens.
.PP
.IP :f
display the current file name and line number.
.PP
.IP ":q or :Q"
exit from
.I more
(same as q or Q).
.PP
.IP .
(dot) repeat the previous command.
.PP
The commands take effect immediately, i.e., it is not necessary to
type a carriage return.
Up to the time when the command character itself is given,
the user may hit the line kill character to cancel the numerical
argument being formed.
In addition, the user may hit the erase character to redisplay the
--More--(xx%) message.
.PP
At any time when output is being sent to the terminal, the user can
hit the quit key (normally control\-\\).
.I More
will stop sending output, and will display the usual --More--
prompt.
The user may then enter one of the above commands in the normal manner.
Unfortunately, some output is lost when this is done, due to the
fact that any characters waiting in the terminal's output queue
are flushed when the quit signal occurs.
.PP
The terminal is set to
.I noecho
mode by this program so that the output can be continuous.
What you type will thus not show on your terminal, except for the / and !
commands.
.PP
If the standard output is not a teletype, then
.I more
acts just like
.I cat,
except that a header is printed before each file (if there is
more than one).
.PP
.DT
A sample usage of
.I more
in previewing
.I nroff
output would be
.PP
nroff \-ms +2 doc.n | more -s
.SH FILES
.DT
/etc/termcap Terminal data base
.br
/usr/lib/more.help Help file
.SH "SEE ALSO"
csh(1), man(1), msgs(1), script(1), sh(1), environ(7)
.SH BUGS
Skipping backwards is too slow on large files.
.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 preceeded 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 postion 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 command
Invoke a shell command. The characters `%', `#', and `!'
are replaced by the current file name, previous filename
and previous shell command, respectively. If there is no
current or previous filename, `%' and `#' are not expanded.
`\%' `\#' and `\!' are replaced by `%', `#' and `!',
respectively.
.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 .

View File

@ -1,24 +1,41 @@
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).
!command Execute command in a subshell.
:!command Execute command in a subshell.
: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.
Most commands optionally preceded by integer argument k. Defaults in brackets.
Star (*) indicates argument becomes new default.
-------------------------------------------------------------------------------
<space> Display next k lines of text [current screen size]
z Display next k lines of text [current screen size]*
<return> Display next k lines of text [1]*
d or ctrl-D Scroll k lines [current scroll size, initially 11]*
q or Q or <interrupt> Exit from more
s Skip forward k lines of text [1]
f Skip forward k screenfuls of text [1]
b or ctrl-B Skip backwards k screenfuls of text [1]
' Go to place where previous search started
= Display current line number
/<regular expression> Search for kth occurrence of regular expression [1]
n Search for kth occurrence of last r.e [1]
!<cmd> or :!<cmd> Execute <cmd> in a subshell
v Start up /usr/ucb/vi at current line
ctrl-L Redraw screen
:n Go to kth next file [1]
:p Go to kth previous file [1]
:f Display current file name and line number
. Repeat previous command
-------------------------------------------------------------------------------

128
usr.bin/more/option.c Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.11 (Berkeley) 6/1/90";
#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);
}

283
usr.bin/more/os.c Normal file
View File

@ -0,0 +1,283 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.12 (Berkeley) 3/1/91";
#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';
}

253
usr.bin/more/output.c Normal file
View File

@ -0,0 +1,253 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.10 (Berkeley) 7/24/91";
#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; XXX */
break;
case UE_CHAR:
ul_exit();
column += ue_width;
break;
case BO_CHAR:
bo_enter();
column += bo_width; /* +1; XXX */
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(); */
screen_trashed = 1; /* XXX */
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();
}

View File

@ -1,4 +1,4 @@
/*-
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
@ -30,10 +30,9 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)pathnames.h 5.2 (Berkeley) 4/18/91
* $Id: pathnames.h,v 1.2 1993/08/01 18:11:02 mycroft Exp $
* @(#)pathnames.h 5.2 (Berkeley) 6/1/90
*/
#include <paths.h>
#define HELPFILE "/usr/share/misc/omore.help"
#define _PATH_HELPFILE "/usr/share/misc/more.help"

163
usr.bin/more/position.c Normal file
View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.7 (Berkeley) 6/1/90";
#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);
}

871
usr.bin/more/prim.c Normal file
View File

@ -0,0 +1,871 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.8 (Berkeley) 6/1/90";
#endif /* not lint */
/*
* Primitives for displaying the file on the screen.
*/
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <less.h>
#ifdef REGEX
#include <regex.h>
#endif
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;
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)
{
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 REGEX
static regex_t *cpattern = NULL;
#else
#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
#endif /*REGEX */
/*
* 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 REGEX
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.
*/
if (cpattern == NULL
&& (cpattern = (regex_t *) malloc(sizeof(regex_t))) == NULL) {
error("cannot allocate memory");
quit();
}
else
regfree(cpattern);
if (regcomp(cpattern, pattern, 0))
{
error("Invalid pattern");
return(0);
}
}
#else
#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
#endif /* REGEX */
/*
* 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 REGEX
linematch = !regexec(cpattern, line, 0, NULL, 0);
#else
#ifdef REGCMP
linematch = (regex(cpattern, line) != NULL);
#else
#ifdef RECOMP
linematch = (re_exec(line) == 1);
#else
linematch = match(pattern, line);
#endif
#endif
#endif /* REGEX */
/*
* 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) && !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

557
usr.bin/more/screen.c Normal file
View File

@ -0,0 +1,557 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.8 (Berkeley) 6/28/92";
#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>
#if TERMIO
#include <termio.h>
#else
#include <sgtty.h>
#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 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
struct termio s;
static struct termio save_term;
if (on)
{
/*
* Get terminal modes.
*/
(void)ioctl(2, TCGETA, &s);
/*
* Save modes and set certain variables dependent on modes.
*/
save_term = s;
ospeed = s.c_cflag & CBAUD;
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);
s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
s.c_cc[VMIN] = 1;
s.c_cc[VTIME] = 0;
} else
{
/*
* Restore saved modes.
*/
s = save_term;
}
(void)ioctl(2, TCSETAW, &s);
#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");
/*
* 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);
}

220
usr.bin/more/signal.c Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.8 (Berkeley) 3/1/91";
#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
}

205
usr.bin/more/tags.c Normal file
View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.5 (Berkeley) 6/1/90";
#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);
}

79
usr.bin/more/ttyin.c Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 1988 Mark Nudleman
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (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 5.4 (Berkeley) 6/1/90";
#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);
}