diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile index 725e665802cc..e61848714e40 100644 --- a/usr.bin/more/Makefile +++ b/usr.bin/more/Makefile @@ -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 diff --git a/usr.bin/more/ch.c b/usr.bin/more/ch.c new file mode 100644 index 000000000000..4924d2cc65fc --- /dev/null +++ b/usr.bin/more/ch.c @@ -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 +#include +#include +#include +#include + +int file = -1; /* File descriptor of the input file */ + +/* + * Pool of buffers holding the most recently used blocks of the input file. + */ +struct buf { + struct buf *next, *prev; + long block; + int datasize; + char data[BUFSIZ]; +}; +int nbufs; + +/* + * The buffer pool is kept as a doubly-linked circular list, in order from + * most- to least-recently used. The circular list is anchored by buf_anchor. + */ +#define END_OF_CHAIN ((struct buf *)&buf_anchor) +#define buf_head buf_anchor.next +#define buf_tail buf_anchor.prev + +static struct { + struct buf *next, *prev; +} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN }; + +extern int ispipe, cbufs, sigs; + +/* + * Current position in file. + * Stored as a block number and an offset into the block. + */ +static long ch_block; +static int ch_offset; + +/* Length of file, needed if input is a pipe. */ +static off_t ch_fsize; + +/* Number of bytes read, if input is standard input (a pipe). */ +static off_t last_piped_pos; + +/* + * Get the character pointed to by the read pointer. ch_get() is a macro + * which is more efficient to call than fch_get (the function), in the usual + * case that the block desired is at the head of the chain. + */ +#define ch_get() \ + ((buf_head->block == ch_block && \ + ch_offset < buf_head->datasize) ? \ + buf_head->data[ch_offset] : fch_get()) + +static +fch_get() +{ + extern int bs_mode; + register struct buf *bp; + register int n, ch; + register char *p, *t; + off_t pos, lseek(); + + /* look for a buffer holding the desired block. */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == ch_block) { + if (ch_offset >= bp->datasize) + /* + * Need more data in this buffer. + */ + goto read_more; + /* + * On a pipe, we don't sort the buffers LRU + * because this can cause gaps in the buffers. + * For example, suppose we've got 12 1K buffers, + * and a 15K input stream. If we read the first 12K + * sequentially, then jump to line 1, then jump to + * the end, the buffers have blocks 0,4,5,6,..,14. + * If we then jump to line 1 again and try to + * read sequentially, we're out of luck when we + * get to block 1 (we'd get the "pipe error" below). + * To avoid this, we only sort buffers on a pipe + * when we actually READ the data, not when we + * find it already buffered. + */ + if (ispipe) + return(bp->data[ch_offset]); + goto found; + } + /* + * Block is not in a buffer. Take the least recently used buffer + * and read the desired block into it. If the LRU buffer has data + * in it, and input is a pipe, then try to allocate a new buffer first. + */ + if (ispipe && buf_tail->block != (long)(-1)) + (void)ch_addbuf(1); + bp = buf_tail; + bp->block = ch_block; + bp->datasize = 0; + +read_more: + pos = (ch_block * BUFSIZ) + bp->datasize; + if (ispipe) { + /* + * The data requested should be immediately after + * the last data read from the pipe. + */ + if (pos != last_piped_pos) { + error("pipe error"); + quit(); + } + } else + (void)lseek(file, pos, L_SET); + + /* + * Read the block. + * If we read less than a full block, we just return the + * partial block and pick up the rest next time. + */ + n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize); + if (n == READ_INTR) + return (EOI); + if (n < 0) { + error("read error"); + quit(); + } + if (ispipe) + last_piped_pos += n; + + p = &bp->data[bp->datasize]; + bp->datasize += n; + + /* + * Set an EOI marker in the buffered data itself. Then ensure the + * data is "clean": there are no extra EOI chars in the data and + * that the "meta" bit (the 0200 bit) is reset in each char; + * also translate \r\n sequences to \n if -u flag not set. + */ + if (n == 0) { + ch_fsize = pos; + bp->data[bp->datasize++] = EOI; + } + + if (bs_mode) { + for (p = &bp->data[bp->datasize]; --n >= 0;) { + *--p &= 0177; + if (*p == EOI) + *p = 0200; + } + } + else { + for (t = p; --n >= 0; ++p) { + ch = *p & 0177; + if (ch == '\r' && n && (p[1] & 0177) == '\n') { + ++p; + *t++ = '\n'; + } + else + *t++ = (ch == EOI) ? 0200 : ch; + } + if (p != t) { + bp->datasize -= p - t; + if (ispipe) + last_piped_pos -= p - t; + } + } + +found: + if (buf_head != bp) { + /* + * Move the buffer to the head of the buffer chain. + * This orders the buffer chain, most- to least-recently used. + */ + bp->next->prev = bp->prev; + bp->prev->next = bp->next; + + bp->next = buf_head; + bp->prev = END_OF_CHAIN; + buf_head->prev = bp; + buf_head = bp; + } + + if (ch_offset >= bp->datasize) + /* + * After all that, we still don't have enough data. + * Go back and try again. + */ + goto read_more; + + return(bp->data[ch_offset]); +} + +/* + * Determine if a specific block is currently in one of the buffers. + */ +static +buffered(block) + long block; +{ + register struct buf *bp; + + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + if (bp->block == block) + return(1); + return(0); +} + +/* + * Seek to a specified position in the file. + * Return 0 if successful, non-zero if can't seek there. + */ +ch_seek(pos) + register off_t pos; +{ + long new_block; + + new_block = pos / BUFSIZ; + if (!ispipe || pos == last_piped_pos || buffered(new_block)) { + /* + * Set read pointer. + */ + ch_block = new_block; + ch_offset = pos % BUFSIZ; + return(0); + } + return(1); +} + +/* + * Seek to the end of the file. + */ +ch_end_seek() +{ + off_t ch_length(); + + if (!ispipe) + return(ch_seek(ch_length())); + + /* + * Do it the slow way: read till end of data. + */ + while (ch_forw_get() != EOI) + if (sigs) + return(1); + return(0); +} + +/* + * Seek to the beginning of the file, or as close to it as we can get. + * We may not be able to seek there if input is a pipe and the + * beginning of the pipe is no longer buffered. + */ +ch_beg_seek() +{ + register struct buf *bp, *firstbp; + + /* + * Try a plain ch_seek first. + */ + if (ch_seek((off_t)0) == 0) + return(0); + + /* + * Can't get to position 0. + * Look thru the buffers for the one closest to position 0. + */ + firstbp = bp = buf_head; + if (bp == END_OF_CHAIN) + return(1); + while ((bp = bp->next) != END_OF_CHAIN) + if (bp->block < firstbp->block) + firstbp = bp; + ch_block = firstbp->block; + ch_offset = 0; + return(0); +} + +/* + * Return the length of the file, if known. + */ +off_t +ch_length() +{ + off_t lseek(); + + if (ispipe) + return(ch_fsize); + return((off_t)(lseek(file, (off_t)0, L_XTND))); +} + +/* + * Return the current position in the file. + */ +off_t +ch_tell() +{ + return(ch_block * BUFSIZ + ch_offset); +} + +/* + * Get the current char and post-increment the read pointer. + */ +ch_forw_get() +{ + register int c; + + c = ch_get(); + if (c != EOI && ++ch_offset >= BUFSIZ) { + ch_offset = 0; + ++ch_block; + } + return(c); +} + +/* + * Pre-decrement the read pointer and get the new current char. + */ +ch_back_get() +{ + if (--ch_offset < 0) { + if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) { + ch_offset = 0; + return(EOI); + } + ch_offset = BUFSIZ - 1; + ch_block--; + } + return(ch_get()); +} + +/* + * Allocate buffers. + * Caller wants us to have a total of at least want_nbufs buffers. + * keep==1 means keep the data in the current buffers; + * otherwise discard the old data. + */ +ch_init(want_nbufs, keep) + int want_nbufs; + int keep; +{ + register struct buf *bp; + char message[80]; + + cbufs = nbufs; + if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs)) { + /* + * Cannot allocate enough buffers. + * If we don't have ANY, then quit. + * Otherwise, just report the error and return. + */ + (void)sprintf(message, "cannot allocate %d buffers", + want_nbufs - nbufs); + error(message); + if (nbufs == 0) + quit(); + return; + } + + if (keep) + return; + + /* + * We don't want to keep the old data, + * so initialize all the buffers now. + */ + for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) + bp->block = (long)(-1); + last_piped_pos = (off_t)0; + ch_fsize = NULL_POSITION; + (void)ch_seek((off_t)0); +} + +/* + * Allocate some new buffers. + * The buffers are added to the tail of the buffer chain. + */ +ch_addbuf(nnew) + int nnew; +{ + register struct buf *bp; + register struct buf *newbufs; + char *calloc(); + + /* + * We don't have enough buffers. + * Allocate some new ones. + */ + newbufs = (struct buf *)calloc((u_int)nnew, sizeof(struct buf)); + if (newbufs == NULL) + return(1); + + /* + * Initialize the new buffers and link them together. + * Link them all onto the tail of the buffer list. + */ + nbufs += nnew; + cbufs = nbufs; + for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) { + bp->next = bp + 1; + bp->prev = bp - 1; + bp->block = (long)(-1); + } + newbufs[nnew-1].next = END_OF_CHAIN; + newbufs[0].prev = buf_tail; + buf_tail->next = &newbufs[0]; + buf_tail = &newbufs[nnew-1]; + return(0); +} diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c new file mode 100644 index 000000000000..8421303e177a --- /dev/null +++ b/usr.bin/more/command.c @@ -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 +#include +#include +#include +#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); +} diff --git a/usr.bin/more/decode.c b/usr.bin/more/decode.c new file mode 100644 index 000000000000..655e004f1ef7 --- /dev/null +++ b/usr.bin/more/decode.c @@ -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: + * ...<0> + * 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 +#include +#include +#include + +/* + * 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); +} diff --git a/usr.bin/more/filename.c b/usr.bin/more/filename.c new file mode 100644 index 000000000000..f6ee76955950 --- /dev/null +++ b/usr.bin/more/filename.c @@ -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); +} diff --git a/usr.bin/more/help.c b/usr.bin/more/help.c new file mode 100644 index 000000000000..4513c3211136 --- /dev/null +++ b/usr.bin/more/help.c @@ -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 +#include +#include "pathnames.h" + +help() +{ + char cmd[MAXPATHLEN + 20]; + + (void)sprintf(cmd, "-more -ce %s", _PATH_HELPFILE); + lsystem(cmd); +} diff --git a/usr.bin/more/input.c b/usr.bin/more/input.c new file mode 100644 index 000000000000..54c05e948b7f --- /dev/null +++ b/usr.bin/more/input.c @@ -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 +#include + +extern int squeeze; +extern int sigs; +extern char *line; + +off_t ch_tell(); + +/* + * Get the next line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the NEXT line. The line obtained is the line starting at curr_pos. + */ +off_t +forw_line(curr_pos) + off_t curr_pos; +{ + off_t new_pos; + register int c; + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) + return (NULL_POSITION); + + c = ch_forw_get(); + if (c == EOI) + return (NULL_POSITION); + + prewind(); + for (;;) + { + if (sigs) + return (NULL_POSITION); + if (c == '\n' || c == EOI) + { + /* + * End of the line. + */ + new_pos = ch_tell(); + break; + } + + /* + * Append the char to the line and get the next char. + */ + if (pappend(c)) + { + /* + * The char won't fit in the line; the line + * is too long to print in the screen width. + * End the line here. + */ + new_pos = ch_tell() - 1; + break; + } + c = ch_forw_get(); + } + (void) pappend('\0'); + + if (squeeze && *line == '\0') + { + /* + * This line is blank. + * Skip down to the last contiguous blank line + * and pretend it is the one which we are returning. + */ + while ((c = ch_forw_get()) == '\n') + if (sigs) + return (NULL_POSITION); + if (c != EOI) + (void) ch_back_get(); + new_pos = ch_tell(); + } + + return (new_pos); +} + +/* + * Get the previous line. + * A "current" position is passed and a "new" position is returned. + * The current position is the position of the first character of + * a line. The new position is the position of the first character + * of the PREVIOUS line. The line obtained is the one starting at new_pos. + */ +off_t +back_line(curr_pos) + off_t curr_pos; +{ + off_t new_pos, begin_new_pos; + int c; + + if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + if (squeeze) + { + /* + * Find out if the "current" line was blank. + */ + (void) ch_forw_get(); /* Skip the newline */ + c = ch_forw_get(); /* First char of "current" line */ + (void) ch_back_get(); /* Restore our position */ + (void) ch_back_get(); + + if (c == '\n') + { + /* + * The "current" line was blank. + * Skip over any preceeding blank lines, + * since we skipped them in forw_line(). + */ + while ((c = ch_back_get()) == '\n') + if (sigs) + return (NULL_POSITION); + if (c == EOI) + return (NULL_POSITION); + (void) ch_forw_get(); + } + } + + /* + * Scan backwards until we hit the beginning of the line. + */ + for (;;) + { + if (sigs) + return (NULL_POSITION); + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = ch_tell(); + break; + } + } + + /* + * Now scan forwards from the beginning of this line. + * We keep discarding "printable lines" (based on screen width) + * until we reach the curr_pos. + * + * {{ This algorithm is pretty inefficient if the lines + * are much longer than the screen width, + * but I don't know of any better way. }} + */ + if (ch_seek(new_pos)) + return (NULL_POSITION); + loop: + begin_new_pos = new_pos; + prewind(); + + do + { + c = ch_forw_get(); + if (c == EOI || sigs) + return (NULL_POSITION); + new_pos++; + if (c == '\n') + break; + if (pappend(c)) + { + /* + * Got a full printable line, but we haven't + * reached our curr_pos yet. Discard the line + * and start a new one. + */ + (void) pappend('\0'); + (void) ch_back_get(); + new_pos--; + goto loop; + } + } while (new_pos < curr_pos); + + (void) pappend('\0'); + + return (begin_new_pos); +} diff --git a/usr.bin/more/less.h b/usr.bin/more/less.h new file mode 100644 index 000000000000..75571f2e72db --- /dev/null +++ b/usr.bin/more/less.h @@ -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 diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c new file mode 100644 index 000000000000..15e77be1a383 --- /dev/null +++ b/usr.bin/more/line.c @@ -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 +#include +#include + +static char linebuf[1024]; /* Buffer which holds the current output line */ +static char *curr; /* Pointer into linebuf */ +static int column; /* Printable length, accounting for + backspaces, etc. */ +/* + * A ridiculously complex state machine takes care of backspaces. The + * complexity arises from the attempt to deal with all cases, especially + * involving long lines with underlining, boldfacing or whatever. There + * are still some cases which will break it. + * + * There are four states: + * LN_NORMAL is the normal state (not in underline mode). + * LN_UNDERLINE means we are in underline mode. We expect to get + * either a sequence like "_\bX" or "X\b_" to continue + * underline mode, or anything else to end underline mode. + * LN_BOLDFACE means we are in boldface mode. We expect to get sequences + * like "X\bX\b...X\bX" to continue boldface mode, or anything + * else to end boldface mode. + * LN_UL_X means we are one character after LN_UNDERLINE + * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). + * LN_UL_XB means we are one character after LN_UL_X + * (we have gotten the backspace in "_\bX" or "X\b_"; + * we expect one more ordinary character, + * which will put us back in state LN_UNDERLINE). + * LN_BO_X means we are one character after LN_BOLDFACE + * (we have gotten the 'X' in "X\bX"). + * LN_BO_XB means we are one character after LN_BO_X + * (we have gotten the backspace in "X\bX"; + * we expect one more 'X' which will put us back + * in LN_BOLDFACE). + */ +static int ln_state; /* Currently in normal/underline/bold/etc mode? */ +#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ +#define LN_UNDERLINE 1 /* In underline, need next char */ +#define LN_UL_X 2 /* In underline, got char, need \b */ +#define LN_UL_XB 3 /* In underline, got char & \b, need one more */ +#define LN_BOLDFACE 4 /* In boldface, need next char */ +#define LN_BO_X 5 /* In boldface, got char, need \b */ +#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ + +char *line; /* Pointer to the current line. + Usually points to linebuf. */ + +extern int bs_mode; +extern int tabstop; +extern int bo_width, be_width; +extern int ul_width, ue_width; +extern int sc_width, sc_height; + +/* + * Rewind the line buffer. + */ +prewind() +{ + line = curr = linebuf; + ln_state = LN_NORMAL; + column = 0; +} + +/* + * Append a character to the line buffer. + * Expand tabs into spaces, handle underlining, boldfacing, etc. + * Returns 0 if ok, 1 if couldn't fit in buffer. + */ +#define NEW_COLUMN(addon) \ + if (column + addon + (ln_state ? ue_width : 0) > sc_width) \ + return(1); \ + else \ + column += addon + +pappend(c) + int c; +{ + if (c == '\0') { + /* + * Terminate any special modes, if necessary. + * Append a '\0' to the end of the line. + */ + switch (ln_state) { + case LN_UL_X: + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + curr++; + break; + case LN_BO_X: + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + curr++; + break; + case LN_UL_XB: + case LN_UNDERLINE: + *curr++ = UE_CHAR; + break; + case LN_BO_XB: + case LN_BOLDFACE: + *curr++ = BE_CHAR; + break; + } + ln_state = LN_NORMAL; + *curr = '\0'; + return(0); + } + + if (curr > linebuf + sizeof(linebuf) - 12) + /* + * Almost out of room in the line buffer. + * Don't take any chances. + * {{ Linebuf is supposed to be big enough that this + * will never happen, but may need to be made + * bigger for wide screens or lots of backspaces. }} + */ + return(1); + + if (!bs_mode) { + /* + * Advance the state machine. + */ + switch (ln_state) { + case LN_NORMAL: + if (curr <= linebuf + 1 + || curr[-1] != (char)('H' | 0200)) + break; + column -= 2; + if (c == curr[-2]) + goto enter_boldface; + if (c == '_' || curr[-2] == '_') + goto enter_underline; + curr -= 2; + break; + +enter_boldface: + /* + * We have "X\bX" (including the current char). + * Switch into boldface mode. + */ + column--; + if (column + bo_width + be_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit boldface mode. + */ + return (1); + + if (bo_width > 0 && curr > linebuf + 2 + && curr[-3] == ' ') { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter boldface" sequence. + */ + curr[-3] = BO_CHAR; + column += bo_width-1; + } else { + curr[-1] = curr[-2]; + curr[-2] = BO_CHAR; + column += bo_width; + curr++; + } + goto ln_bo_xb_case; + +enter_underline: + /* + * We have either "_\bX" or "X\b_" (including + * the current char). Switch into underline mode. + */ + column--; + if (column + ul_width + ue_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit underline mode. + */ + return (1); + + if (ul_width > 0 && + curr > linebuf + 2 && curr[-3] == ' ') + { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter underline" sequence. + */ + curr[-3] = UL_CHAR; + column += ul_width-1; + } else + { + curr[-1] = curr[-2]; + curr[-2] = UL_CHAR; + column += ul_width; + curr++; + } + goto ln_ul_xb_case; + /*NOTREACHED*/ + case LN_UL_XB: + /* + * Termination of a sequence "_\bX" or "X\b_". + */ + if (c != '_' && curr[-2] != '_' && c == curr[-2]) + { + /* + * We seem to have run on from underlining + * into boldfacing - this is a nasty fix, but + * until this whole routine is rewritten as a + * real DFA, ... well ... + */ + curr[0] = curr[-2]; + curr[-2] = UE_CHAR; + curr[-1] = BO_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_BO_XB; + goto ln_bo_xb_case; + } +ln_ul_xb_case: + if (c == '_') + c = curr[-2]; + curr -= 2; + ln_state = LN_UNDERLINE; + break; + case LN_BO_XB: + /* + * Termination of a sequnce "X\bX". + */ + if (c != curr[-2] && (c == '_' || curr[-2] == '_')) + { + /* + * We seem to have run on from + * boldfacing into underlining. + */ + curr[0] = curr[-2]; + curr[-2] = BE_CHAR; + curr[-1] = UL_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_UL_XB; + goto ln_ul_xb_case; + } +ln_bo_xb_case: + curr -= 2; + ln_state = LN_BOLDFACE; + break; + case LN_UNDERLINE: + if (column + ue_width + bo_width + 1 + be_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_UL_X; + break; + case LN_BOLDFACE: + if (c == '\b') + { + ln_state = LN_BO_XB; + break; + } + if (column + be_width + ul_width + 1 + ue_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_BO_X; + break; + case LN_UL_X: + if (c == '\b') + ln_state = LN_UL_XB; + else + { + /* + * Exit underline mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + column += ue_width; + if (ue_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit underline" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + case LN_BO_X: + if (c == '\b') + ln_state = LN_BO_XB; + else + { + /* + * Exit boldface mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + column += be_width; + if (be_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit boldface" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + } + } + + if (c == '\t') { + /* + * Expand a tab into spaces. + */ + do { + NEW_COLUMN(1); + } while ((column % tabstop) != 0); + *curr++ = '\t'; + return (0); + } + + if (c == '\b') { + if (ln_state == LN_NORMAL) + NEW_COLUMN(2); + else + column--; + *curr++ = ('H' | 0200); + return(0); + } + + if (CONTROL_CHAR(c)) { + /* + * Put a "^X" into the buffer. The 0200 bit is used to tell + * put_line() to prefix the char with a ^. We don't actually + * put the ^ in the buffer because we sometimes need to move + * chars around, and such movement might separate the ^ from + * its following character. + */ + NEW_COLUMN(2); + *curr++ = (CARAT_CHAR(c) | 0200); + return(0); + } + + /* + * Ordinary character. Just put it in the buffer. + */ + NEW_COLUMN(1); + *curr++ = c; + return (0); +} + +/* + * Analogous to forw_line(), but deals with "raw lines": + * lines which are not split for screen width. + * {{ This is supposed to be more efficient than forw_line(). }} + */ +off_t +forw_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || + (c = ch_forw_get()) == EOI) + return (NULL_POSITION); + + p = linebuf; + + for (;;) + { + if (c == '\n' || c == EOI) + { + new_pos = ch_tell(); + break; + } + if (p >= &linebuf[sizeof(linebuf)-1]) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + * {{ The line buffer is supposed to be big + * enough that this never happens. }} + */ + new_pos = ch_tell() - 1; + break; + } + *p++ = c; + c = ch_forw_get(); + } + *p = '\0'; + line = linebuf; + return (new_pos); +} + +/* + * Analogous to back_line(), but deals with "raw lines". + * {{ This is supposed to be more efficient than back_line(). }} + */ +off_t +back_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + p = &linebuf[sizeof(linebuf)]; + *--p = '\0'; + + for (;;) + { + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = (off_t)0; + break; + } + if (p <= linebuf) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() + 1; + break; + } + *--p = c; + } + line = p; + return (new_pos); +} diff --git a/usr.bin/more/linenum.c b/usr.bin/more/linenum.c new file mode 100644 index 000000000000..04637a302356 --- /dev/null +++ b/usr.bin/more/linenum.c @@ -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 +#include +#include + +/* + * Structure to keep track of a line number and the associated file position. + * A doubly-linked circular list of line numbers is kept ordered by line number. + */ +struct linenum +{ + struct linenum *next; /* Link to next in the list */ + struct linenum *prev; /* Line to previous in the list */ + off_t pos; /* File position */ + off_t gap; /* Gap between prev and next */ + int line; /* Line number */ +}; +/* + * "gap" needs some explanation: the gap of any particular line number + * is the distance between the previous one and the next one in the list. + * ("Distance" means difference in file position.) In other words, the + * gap of a line number is the gap which would be introduced if this + * line number were deleted. It is used to decide which one to replace + * when we have a new one to insert and the table is full. + */ + +#define NPOOL 50 /* Size of line number pool */ + +#define LONGTIME (2) /* In seconds */ + +int lnloop = 0; /* Are we in the line num loop? */ + +static struct linenum anchor; /* Anchor of the list */ +static struct linenum *freelist; /* Anchor of the unused entries */ +static struct linenum pool[NPOOL]; /* The pool itself */ +static struct linenum *spare; /* We always keep one spare entry */ + +extern int linenums; +extern int sigs; + +/* + * Initialize the line number structures. + */ +clr_linenum() +{ + register struct linenum *p; + + /* + * Put all the entries on the free list. + * Leave one for the "spare". + */ + for (p = pool; p < &pool[NPOOL-2]; p++) + p->next = p+1; + pool[NPOOL-2].next = NULL; + freelist = pool; + + spare = &pool[NPOOL-1]; + + /* + * Initialize the anchor. + */ + anchor.next = anchor.prev = &anchor; + anchor.gap = 0; + anchor.pos = (off_t)0; + anchor.line = 1; +} + +/* + * Calculate the gap for an entry. + */ +static +calcgap(p) + register struct linenum *p; +{ + /* + * Don't bother to compute a gap for the anchor. + * Also don't compute a gap for the last one in the list. + * The gap for that last one should be considered infinite, + * but we never look at it anyway. + */ + if (p == &anchor || p->next == &anchor) + return; + p->gap = p->next->pos - p->prev->pos; +} + +/* + * Add a new line number to the cache. + * The specified position (pos) should be the file position of the + * FIRST character in the specified line. + */ +add_lnum(line, pos) + int line; + off_t pos; +{ + register struct linenum *p; + register struct linenum *new; + register struct linenum *nextp; + register struct linenum *prevp; + register off_t mingap; + + /* + * Find the proper place in the list for the new one. + * The entries are sorted by position. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + if (p->line == line) + /* We already have this one. */ + return; + nextp = p; + prevp = p->prev; + + if (freelist != NULL) + { + /* + * We still have free (unused) entries. + * Use one of them. + */ + new = freelist; + freelist = freelist->next; + } else + { + /* + * No free entries. + * Use the "spare" entry. + */ + new = spare; + spare = NULL; + } + + /* + * Fill in the fields of the new entry, + * and insert it into the proper place in the list. + */ + new->next = nextp; + new->prev = prevp; + new->pos = pos; + new->line = line; + + nextp->prev = new; + prevp->next = new; + + /* + * Recalculate gaps for the new entry and the neighboring entries. + */ + calcgap(new); + calcgap(nextp); + calcgap(prevp); + + if (spare == NULL) + { + /* + * We have used the spare entry. + * Scan the list to find the one with the smallest + * gap, take it out and make it the spare. + * We should never remove the last one, so stop when + * we get to p->next == &anchor. This also avoids + * looking at the gap of the last one, which is + * not computed by calcgap. + */ + mingap = anchor.next->gap; + for (p = anchor.next; p->next != &anchor; p = p->next) + { + if (p->gap <= mingap) + { + spare = p; + mingap = p->gap; + } + } + spare->next->prev = spare->prev; + spare->prev->next = spare->next; + } +} + +/* + * If we get stuck in a long loop trying to figure out the + * line number, print a message to tell the user what we're doing. + */ +static +longloopmessage() +{ + ierror("Calculating line numbers"); + /* + * Set the lnloop flag here, so if the user interrupts while + * we are calculating line numbers, the signal handler will + * turn off line numbers (linenums=0). + */ + lnloop = 1; +} + +/* + * Find the line number associated with a given position. + * Return 0 if we can't figure it out. + */ +find_linenum(pos) + off_t pos; +{ + register struct linenum *p; + register int lno; + register int loopcount; + off_t cpos, back_raw_line(), forw_raw_line(); + time_t startime, time(); + + if (!linenums) + /* + * We're not using line numbers. + */ + return (0); + if (pos == NULL_POSITION) + /* + * Caller doesn't know what he's talking about. + */ + return (0); + if (pos == (off_t)0) + /* + * Beginning of file is always line number 1. + */ + return (1); + + /* + * Find the entry nearest to the position we want. + */ + for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next) + continue; + if (p->pos == pos) + /* Found it exactly. */ + return (p->line); + + /* + * This is the (possibly) time-consuming part. + * We start at the line we just found and start + * reading the file forward or backward till we + * get to the place we want. + * + * First decide whether we should go forward from the + * previous one or backwards from the next one. + * The decision is based on which way involves + * traversing fewer bytes in the file. + */ + flush(); + (void)time(&startime); + if (p == &anchor || pos - p->prev->pos < p->pos - pos) + { + /* + * Go forward. + */ + p = p->prev; + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos < pos; lno++) + { + /* + * Allow a signal to abort this loop. + */ + cpos = forw_raw_line(cpos); + if (sigs || cpos == NULL_POSITION) + return (0); + if (loopcount >= 0 && ++loopcount > 100) { + loopcount = 0; + if (time((time_t *)NULL) + >= startime + LONGTIME) { + longloopmessage(); + loopcount = -1; + } + } + } + lnloop = 0; + /* + * If the given position is not at the start of a line, + * make sure we return the correct line number. + */ + if (cpos > pos) + lno--; + } else + { + /* + * Go backward. + */ + if (ch_seek(p->pos)) + return (0); + loopcount = 0; + for (lno = p->line, cpos = p->pos; cpos > pos; lno--) + { + /* + * Allow a signal to abort this loop. + */ + cpos = back_raw_line(cpos); + if (sigs || cpos == NULL_POSITION) + return (0); + if (loopcount >= 0 && ++loopcount > 100) { + loopcount = 0; + if (time((time_t *)NULL) + >= startime + LONGTIME) { + longloopmessage(); + loopcount = -1; + } + } + } + lnloop = 0; + } + + /* + * We might as well cache it. + */ + add_lnum(lno, cpos); + return (lno); +} + +/* + * Return the line number of the "current" line. + * The argument "where" tells which line is to be considered + * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc). + */ +currline(where) + int where; +{ + off_t pos, ch_length(), position(); + + if ((pos = position(where)) == NULL_POSITION) + pos = ch_length(); + return(find_linenum(pos)); +} diff --git a/usr.bin/more/main.c b/usr.bin/more/main.c new file mode 100644 index 000000000000..f9d7130b12aa --- /dev/null +++ b/usr.bin/more/main.c @@ -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 +#include +#include +#include + +int ispipe; +int new_file; +int is_tty; +char *current_file, *previous_file, *current_name, *next_name; +off_t prev_pos; +int any_display; +int scroll; +int ac; +char **av; +int curr_ac; +int quitting; + +extern int file; +extern int cbufs; +extern int errmsgs; + +extern char *tagfile; +extern int tagoption; + +/* + * Edit a new file. + * Filename "-" means standard input. + * No filename means the "current" file, from the command line. + */ +edit(filename) + register char *filename; +{ + extern int errno; + register int f; + register char *m; + off_t initial_pos, position(); + static int didpipe; + char message[100], *p; + char *rindex(), *strerror(), *save(), *bad_file(); + + initial_pos = NULL_POSITION; + if (filename == NULL || *filename == '\0') { + if (curr_ac >= ac) { + error("No current file"); + return(0); + } + filename = save(av[curr_ac]); + } + else if (strcmp(filename, "#") == 0) { + if (*previous_file == '\0') { + error("no previous file"); + return(0); + } + filename = save(previous_file); + initial_pos = prev_pos; + } else + filename = save(filename); + + /* use standard input. */ + if (!strcmp(filename, "-")) { + if (didpipe) { + error("Can view standard input only once"); + return(0); + } + f = 0; + } + else if ((m = bad_file(filename, message, sizeof(message))) != NULL) { + error(m); + free(filename); + return(0); + } + else if ((f = open(filename, O_RDONLY, 0)) < 0) { + (void)sprintf(message, "%s: %s", filename, strerror(errno)); + error(message); + free(filename); + return(0); + } + + if (isatty(f)) { + /* + * Not really necessary to call this an error, + * but if the control terminal (for commands) + * and the input file (for data) are the same, + * we get weird results at best. + */ + error("Can't take input from a terminal"); + if (f > 0) + (void)close(f); + (void)free(filename); + return(0); + } + + /* + * We are now committed to using the new file. + * Close the current input file and set up to use the new one. + */ + if (file > 0) + (void)close(file); + new_file = 1; + if (previous_file != NULL) + free(previous_file); + previous_file = current_file; + current_file = filename; + pos_clear(); + prev_pos = position(TOP); + ispipe = (f == 0); + if (ispipe) { + didpipe = 1; + current_name = "stdin"; + } else + current_name = (p = rindex(filename, '/')) ? p + 1 : filename; + if (curr_ac >= ac) + next_name = NULL; + else + next_name = av[curr_ac + 1]; + file = f; + ch_init(cbufs, 0); + init_mark(); + + if (is_tty) { + int no_display = !any_display; + any_display = 1; + if (no_display && errmsgs > 0) { + /* + * We displayed some messages on error output + * (file descriptor 2; see error() function). + * Before erasing the screen contents, + * display the file name and wait for a keystroke. + */ + error(filename); + } + /* + * Indicate there is nothing displayed yet. + */ + if (initial_pos != NULL_POSITION) + jump_loc(initial_pos); + clr_linenum(); + } + return(1); +} + +/* + * Edit the next file in the command line list. + */ +next_file(n) + int n; +{ + extern int quit_at_eof; + off_t position(); + + if (curr_ac + n >= ac) { + if (quit_at_eof || position(TOP) == NULL_POSITION) + quit(); + error("No (N-th) next file"); + } + else + (void)edit(av[curr_ac += n]); +} + +/* + * Edit the previous file in the command line list. + */ +prev_file(n) + int n; +{ + if (curr_ac - n < 0) + error("No (N-th) previous file"); + else + (void)edit(av[curr_ac -= n]); +} + +/* + * copy a file directly to standard output; used if stdout is not a tty. + * the only processing is to squeeze multiple blank input lines. + */ +static +cat_file() +{ + extern int squeeze; + register int c, empty; + + if (squeeze) { + empty = 0; + while ((c = ch_forw_get()) != EOI) + if (c != '\n') { + putchr(c); + empty = 0; + } + else if (empty < 2) { + putchr(c); + ++empty; + } + } + else while ((c = ch_forw_get()) != EOI) + putchr(c); + flush(); +} + +main(argc, argv) + int argc; + char **argv; +{ + int envargc, argcnt; + char *envargv[2], *getenv(); + + /* + * Process command line arguments and MORE environment arguments. + * Command line arguments override environment arguments. + */ + if (envargv[1] = getenv("MORE")) { + envargc = 2; + envargv[0] = "more"; + envargv[2] = NULL; + (void)option(envargc, envargv); + } + argcnt = option(argc, argv); + argv += argcnt; + argc -= argcnt; + + /* + * Set up list of files to be examined. + */ + ac = argc; + av = argv; + curr_ac = 0; + + /* + * Set up terminal, etc. + */ + is_tty = isatty(1); + if (!is_tty) { + /* + * Output is not a tty. + * Just copy the input file(s) to output. + */ + if (ac < 1) { + (void)edit("-"); + cat_file(); + } else { + do { + (void)edit((char *)NULL); + if (file >= 0) + cat_file(); + } while (++curr_ac < ac); + } + exit(0); + } + + raw_mode(1); + get_term(); + open_getchr(); + init(); + init_signals(1); + + /* select the first file to examine. */ + if (tagoption) { + /* + * A -t option was given; edit the file selected by the + * "tags" search, and search for the proper line in the file. + */ + if (!tagfile || !edit(tagfile) || tagsearch()) + quit(); + } + else if (ac < 1) + (void)edit("-"); /* Standard input */ + else { + /* + * Try all the files named as command arguments. + * We are simply looking for one which can be + * opened without error. + */ + do { + (void)edit((char *)NULL); + } while (file < 0 && ++curr_ac < ac); + } + + if (file >= 0) + commands(); + quit(); + /*NOTREACHED*/ +} + +/* + * Copy a string to a "safe" place + * (that is, to a buffer allocated by malloc). + */ +char * +save(s) + char *s; +{ + char *p, *strcpy(), *malloc(); + + p = malloc((u_int)strlen(s)+1); + if (p == NULL) + { + error("cannot allocate memory"); + quit(); + } + return(strcpy(p, s)); +} + +/* + * Exit the program. + */ +quit() +{ + /* + * Put cursor at bottom left corner, clear the line, + * reset the terminal modes, and exit. + */ + quitting = 1; + lower_left(); + clear_eol(); + deinit(); + flush(); + raw_mode(0); + exit(0); +} diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1 index a5c8f325163f..20a1e021ce74 100644 --- a/usr.bin/more/more.1 +++ b/usr.bin/more/more.1 @@ -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 -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 . diff --git a/usr.bin/more/more.help b/usr.bin/more/more.help index 1ced5c9d688e..2c0f54c4bb5b 100644 --- a/usr.bin/more/more.help +++ b/usr.bin/more/more.help @@ -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. -------------------------------------------------------------------------------- - Display next k lines of text [current screen size] -z Display next k lines of text [current screen size]* - Display next k lines of text [1]* -d or ctrl-D Scroll k lines [current scroll size, initially 11]* -q or Q or 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 -/ Search for kth occurrence of regular expression [1] -n Search for kth occurrence of last r.e [1] -! or :! Execute 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 -------------------------------------------------------------------------------- diff --git a/usr.bin/more/option.c b/usr.bin/more/option.c new file mode 100644 index 000000000000..708df99265ce --- /dev/null +++ b/usr.bin/more/option.c @@ -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 +#include + +int top_scroll; /* Repaint screen from top */ +int bs_mode; /* How to process backspaces */ +int caseless; /* Do "caseless" searches */ +int cbufs = 10; /* Current number of buffers */ +int linenums = 1; /* Use line numbers */ +int quit_at_eof; +int squeeze; /* Squeeze multiple blank lines into one */ +int tabstop = 8; /* Tab settings */ +int tagoption; + +char *firstsearch; +extern int sc_height; + +option(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + static int sc_window_set = 0; + int ch; + char *p; + + /* backward compatible processing for "+/search" */ + char **a; + for (a = argv; *a; ++a) + if ((*a)[0] == '+' && (*a)[1] == '/') + (*a)[0] = '-'; + + optind = 1; /* called twice, re-init getopt. */ + while ((ch = getopt(argc, argv, "0123456789/:ceinst:ux:f")) != EOF) + switch((char)ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * kludge: more was originally designed to take + * a number after a dash. + */ + if (!sc_window_set) { + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + sc_height = atoi(++p); + else + sc_height = atoi(argv[optind] + 1); + sc_window_set = 1; + } + break; + case '/': + firstsearch = optarg; + break; + case 'c': + top_scroll = 1; + break; + case 'e': + quit_at_eof = 1; + break; + case 'i': + caseless = 1; + break; + case 'n': + linenums = 0; + break; + case 's': + squeeze = 1; + break; + case 't': + tagoption = 1; + findtag(optarg); + break; + case 'u': + bs_mode = 1; + break; + case 'x': + tabstop = atoi(optarg); + if (tabstop <= 0) + tabstop = 8; + break; + case 'f': /* ignore -f, compatability with old more */ + break; + case '?': + default: + fprintf(stderr, + "usage: more [-ceinus] [-t tag] [-x tabs] [-/ pattern] [-#] [file ...]\n"); + exit(1); + } + return(optind); +} diff --git a/usr.bin/more/os.c b/usr.bin/more/os.c new file mode 100644 index 000000000000..10be985f0d20 --- /dev/null +++ b/usr.bin/more/os.c @@ -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 +#include +#include +#include +#include +#include +#include +#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 . + * 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 . + */ + cmd = malloc((u_int)(strlen(filename)+8)); + if (cmd == NULL) + return (filename); + (void)sprintf(cmd, "echo \"%s\"", filename); + } else + { + /* + * Read the output of <$SHELL -c "echo filename">. + */ + cmd = malloc((u_int)(strlen(p)+12)); + if (cmd == NULL) + return (filename); + (void)sprintf(cmd, "%s -c \"echo %s\"", p, filename); + } + + if ((f = popen(cmd, "r")) == NULL) + return (filename); + free(cmd); + + for (p = buffer; p < &buffer[sizeof(buffer)-1]; p++) + { + if ((ch = getc(f)) == '\n' || ch == EOF) + break; + *p = ch; + } + *p = '\0'; + (void)pclose(f); + return(buffer); +} + +char * +bad_file(filename, message, len) + char *filename, *message; + u_int len; +{ + extern int errno; + struct stat statbuf; + char *strcat(), *strerror(); + + if (stat(filename, &statbuf) < 0) { + (void)sprintf(message, "%s: %s", filename, strerror(errno)); + return(message); + } + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { + static char is_dir[] = " is a directory"; + + strtcpy(message, filename, (int)(len-sizeof(is_dir)-1)); + (void)strcat(message, is_dir); + return(message); + } + return((char *)NULL); +} + +/* + * Copy a string, truncating to the specified length if necessary. + * Unlike strncpy(), the resulting string is guaranteed to be null-terminated. + */ +strtcpy(to, from, len) + char *to, *from; + int len; +{ + char *strncpy(); + + (void)strncpy(to, from, (int)len); + to[len-1] = '\0'; +} + diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c new file mode 100644 index 000000000000..0f805036cfb3 --- /dev/null +++ b/usr.bin/more/output.c @@ -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 +#include + +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(); +} diff --git a/usr.bin/more/pathnames.h b/usr.bin/more/pathnames.h index d62c9e8515cf..6402b411b36c 100644 --- a/usr.bin/more/pathnames.h +++ b/usr.bin/more/pathnames.h @@ -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 -#define HELPFILE "/usr/share/misc/omore.help" +#define _PATH_HELPFILE "/usr/share/misc/more.help" diff --git a/usr.bin/more/position.c b/usr.bin/more/position.c new file mode 100644 index 000000000000..46ed345e83ef --- /dev/null +++ b/usr.bin/more/position.c @@ -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 +#include + +static off_t *table; /* The position table */ +static int tablesize; + +extern int sc_height; + +/* + * Return the starting file position of a line displayed on the screen. + * The line may be specified as a line number relative to the top + * of the screen, but is usually one of these special cases: + * the top (first) line on the screen + * the second line on the screen + * the bottom line on the screen + * the line after the bottom line on the screen + */ +off_t +position(where) + int where; +{ + switch (where) + { + case BOTTOM: + where = sc_height - 2; + break; + case BOTTOM_PLUS_ONE: + where = sc_height - 1; + break; + case MIDDLE: + where = sc_height / 2; + } + return (table[where]); +} + +/* + * Add a new file position to the bottom of the position table. + */ +add_forw_pos(pos) + off_t pos; +{ + register int i; + + /* + * Scroll the position table up. + */ + for (i = 1; i < sc_height; i++) + table[i-1] = table[i]; + table[sc_height - 1] = pos; +} + +/* + * Add a new file position to the top of the position table. + */ +add_back_pos(pos) + off_t pos; +{ + register int i; + + /* + * Scroll the position table down. + */ + for (i = sc_height - 1; i > 0; i--) + table[i] = table[i-1]; + table[0] = pos; +} + +copytable() +{ + register int a, b; + + for (a = 0; a < sc_height && table[a] == NULL_POSITION; a++); + for (b = 0; a < sc_height; a++, b++) { + table[b] = table[a]; + table[a] = NULL_POSITION; + } +} + +/* + * Initialize the position table, done whenever we clear the screen. + */ +pos_clear() +{ + register int i; + extern char *malloc(), *realloc(); + + if (table == 0) { + tablesize = sc_height > 25 ? sc_height : 25; + table = (off_t *)malloc(tablesize * sizeof *table); + } else if (sc_height >= tablesize) { + tablesize = sc_height; + table = (off_t *)realloc(table, tablesize * sizeof *table); + } + + for (i = 0; i < sc_height; i++) + table[i] = NULL_POSITION; +} + +/* + * See if the byte at a specified position is currently on the screen. + * Check the position table to see if the position falls within its range. + * Return the position table entry if found, -1 if not. + */ +onscreen(pos) + off_t pos; +{ + register int i; + + if (pos < table[0]) + return (-1); + for (i = 1; i < sc_height; i++) + if (pos < table[i]) + return (i-1); + return (-1); +} diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c new file mode 100644 index 000000000000..e0410304cf1f --- /dev/null +++ b/usr.bin/more/prim.c @@ -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 +#include +#include +#include + +#ifdef REGEX +#include +#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 diff --git a/usr.bin/more/screen.c b/usr.bin/more/screen.c new file mode 100644 index 000000000000..64d350084089 --- /dev/null +++ b/usr.bin/more/screen.c @@ -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 +#include + +#if TERMIO +#include +#else +#include +#endif + +#ifdef TIOCGWINSZ +#include +#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 +#ifdef SIGPHONE +#include +#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); +} diff --git a/usr.bin/more/signal.c b/usr.bin/more/signal.c new file mode 100644 index 000000000000..aa97f0741891 --- /dev/null +++ b/usr.bin/more/signal.c @@ -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 +#include + +/* + * "sigs" contains bits indicating signals which need to be processed. + */ +int sigs; + +#ifdef SIGTSTP +#define S_STOP 02 +#endif +#if defined(SIGWINCH) || defined(SIGWIND) +#define S_WINCH 04 +#endif + +extern int sc_width, sc_height; +extern int screen_trashed; +extern int lnloop; +extern int linenums; +extern int scroll; +extern int reading; + +#ifdef SIGTSTP +/* + * "Stop" (^Z) signal handler. + */ +static void +stop() +{ + (void)signal(SIGTSTP, stop); + sigs |= S_STOP; + if (reading) + intread(); +} +#endif + +#ifdef SIGWINCH +/* + * "Window" change handler + */ +void +winch() +{ + (void)signal(SIGWINCH, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#else +#ifdef SIGWIND +/* + * "Window" change handler + */ +winch() +{ + (void)signal(SIGWIND, winch); + sigs |= S_WINCH; + if (reading) + intread(); +} +#endif +#endif + +static void +purgeandquit() +{ + + purge(); /* purge buffered output */ + quit(); +} + +/* + * Set up the signal handlers. + */ +init_signals(on) + int on; +{ + if (on) + { + /* + * Set signal handlers. + */ + (void)signal(SIGINT, purgeandquit); +#ifdef SIGTSTP + (void)signal(SIGTSTP, stop); +#endif +#ifdef SIGWINCH + (void)signal(SIGWINCH, winch); +#else +#ifdef SIGWIND + (void)signal(SIGWIND, winch); +#endif +#endif + } else + { + /* + * Restore signals to defaults. + */ + (void)signal(SIGINT, SIG_DFL); +#ifdef SIGTSTP + (void)signal(SIGTSTP, SIG_DFL); +#endif +#ifdef SIGWINCH + (void)signal(SIGWINCH, SIG_IGN); +#endif +#ifdef SIGWIND + (void)signal(SIGWIND, SIG_IGN); +#endif + } +} + +/* + * Process any signals we have received. + * A received signal cause a bit to be set in "sigs". + */ +psignals() +{ + register int tsignals; + + if ((tsignals = sigs) == 0) + return; + sigs = 0; + +#ifdef S_WINCH + if (tsignals & S_WINCH) + { + int old_width, old_height; + /* + * Re-execute get_term() to read the new window size. + */ + old_width = sc_width; + old_height = sc_height; + get_term(); + if (sc_width != old_width || sc_height != old_height) + { + scroll = (sc_height + 1) / 2; + screen_trashed = 1; + } + } +#endif +#ifdef SIGTSTP + if (tsignals & S_STOP) + { + /* + * Clean up the terminal. + */ +#ifdef SIGTTOU + (void)signal(SIGTTOU, SIG_IGN); +#endif + lower_left(); + clear_eol(); + deinit(); + (void)flush(); + raw_mode(0); +#ifdef SIGTTOU + (void)signal(SIGTTOU, SIG_DFL); +#endif + (void)signal(SIGTSTP, SIG_DFL); + (void)kill(getpid(), SIGTSTP); + /* + * ... Bye bye. ... + * Hopefully we'll be back later and resume here... + * Reset the terminal and arrange to repaint the + * screen when we get back to the main command loop. + */ + (void)signal(SIGTSTP, stop); + raw_mode(1); + init(); + screen_trashed = 1; + } +#endif +} diff --git a/usr.bin/more/tags.c b/usr.bin/more/tags.c new file mode 100644 index 000000000000..af186ec31863 --- /dev/null +++ b/usr.bin/more/tags.c @@ -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 +#include +#include + +#define WHITESP(c) ((c)==' ' || (c)=='\t') + +char *tagfile; +char *tagpattern; + +static char *tags = "tags"; + +extern int linenums; +extern int sigs; +extern char *line; + +/* + * Find a tag in the "tags" file. + * Sets "tagfile" to the name of the file containing the tag, + * and "tagpattern" to the search pattern which should be used + * to find the tag. + */ +findtag(tag) + register char *tag; +{ + register char *p; + register FILE *f; + register int taglen; + int search_char; + static char tline[200]; + + if ((f = fopen(tags, "r")) == NULL) + { + error("No tags file"); + tagfile = NULL; + return; + } + + taglen = strlen(tag); + + /* + * Search the tags file for the desired tag. + */ + while (fgets(tline, sizeof(tline), f) != NULL) + { + if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) + continue; + + /* + * Found it. + * The line contains the tag, the filename and the + * pattern, separated by white space. + * The pattern is surrounded by a pair of identical + * search characters. + * Parse the line and extract these parts. + */ + tagfile = tagpattern = NULL; + + /* + * Skip over the whitespace after the tag name. + */ + for (p = tline; !WHITESP(*p) && *p != '\0'; p++) + continue; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* File name is missing! */ + continue; + + /* + * Save the file name. + * Skip over the whitespace after the file name. + */ + tagfile = p; + while (!WHITESP(*p) && *p != '\0') + p++; + *p++ = '\0'; + while (WHITESP(*p)) + p++; + if (*p == '\0') + /* Pattern is missing! */ + continue; + + /* + * Save the pattern. + * Skip to the end of the pattern. + * Delete the initial "^" and the final "$" from the pattern. + */ + search_char = *p++; + if (*p == '^') + p++; + tagpattern = p; + while (*p != search_char && *p != '\0') + p++; + if (p[-1] == '$') + p--; + *p = '\0'; + + (void)fclose(f); + return; + } + (void)fclose(f); + error("No such tag in tags file"); + tagfile = NULL; +} + +/* + * Search for a tag. + * This is a stripped-down version of search(). + * We don't use search() for several reasons: + * - We don't want to blow away any search string we may have saved. + * - The various regular-expression functions (from different systems: + * regcmp vs. re_comp) behave differently in the presence of + * parentheses (which are almost always found in a tag). + */ +tagsearch() +{ + off_t pos, linepos, forw_raw_line(); + int linenum; + + pos = (off_t)0; + linenum = find_linenum(pos); + + for (;;) + { + /* + * Get lines until we find a matching one or + * until we hit end-of-file. + */ + if (sigs) + return (1); + + /* + * Read the next line, and save the + * starting position of that line in linepos. + */ + linepos = pos; + pos = forw_raw_line(pos); + if (linenum != 0) + linenum++; + + if (pos == NULL_POSITION) + { + /* + * We hit EOF without a match. + */ + error("Tag not found"); + return (1); + } + + /* + * If we're using line numbers, we might as well + * remember the information we have now (the position + * and line number of the current line). + */ + if (linenums) + add_lnum(linenum, pos); + + /* + * Test the line to see if we have a match. + */ + if (strcmp(tagpattern, line) == 0) + break; + } + + jump_loc(linepos); + return (0); +} diff --git a/usr.bin/more/ttyin.c b/usr.bin/more/ttyin.c new file mode 100644 index 000000000000..44607751d2b5 --- /dev/null +++ b/usr.bin/more/ttyin.c @@ -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 + +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); +}