distribution by Mark Nudleman
authorKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Fri, 22 Jul 1988 09:53:31 +0000 (01:53 -0800)
committerKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Fri, 22 Jul 1988 09:53:31 +0000 (01:53 -0800)
SCCS-vsn: usr.bin/more/README 5.1
SCCS-vsn: usr.bin/more/ch.c 5.1
SCCS-vsn: usr.bin/more/cmd.h 5.1
SCCS-vsn: usr.bin/more/command.c 5.1
SCCS-vsn: usr.bin/more/decode.c 5.1
SCCS-vsn: usr.bin/more/help.c 5.1
SCCS-vsn: usr.bin/more/input.c 5.1
SCCS-vsn: usr.bin/more/less.h 5.1
SCCS-vsn: usr.bin/more/more.help 5.1
SCCS-vsn: usr.bin/more/more.1 5.1
SCCS-vsn: usr.bin/more/lesskey.c 5.1
SCCS-vsn: usr.bin/more/lesskey.1 5.1
SCCS-vsn: usr.bin/more/line.c 5.1
SCCS-vsn: usr.bin/more/linenum.c 5.1
SCCS-vsn: usr.bin/more/linstall 5.1
SCCS-vsn: usr.bin/more/main.c 5.1
SCCS-vsn: usr.bin/more/mkfuncs.awk 5.1
SCCS-vsn: usr.bin/more/option.c 5.1
SCCS-vsn: usr.bin/more/os.c 5.1
SCCS-vsn: usr.bin/more/output.c 5.1
SCCS-vsn: usr.bin/more/position.c 5.1
SCCS-vsn: usr.bin/more/position.h 5.1
SCCS-vsn: usr.bin/more/prim.c 5.1
SCCS-vsn: usr.bin/more/prompt.c 5.1
SCCS-vsn: usr.bin/more/screen.c 5.1
SCCS-vsn: usr.bin/more/signal.c 5.1
SCCS-vsn: usr.bin/more/tags.c 5.1
SCCS-vsn: usr.bin/more/ttyin.c 5.1
SCCS-vsn: usr.bin/more/vecho.c 5.1
SCCS-vsn: usr.bin/more/version.c 5.1

30 files changed:
usr/src/usr.bin/more/README [new file with mode: 0644]
usr/src/usr.bin/more/ch.c [new file with mode: 0644]
usr/src/usr.bin/more/cmd.h [new file with mode: 0644]
usr/src/usr.bin/more/command.c [new file with mode: 0644]
usr/src/usr.bin/more/decode.c [new file with mode: 0644]
usr/src/usr.bin/more/help.c [new file with mode: 0644]
usr/src/usr.bin/more/input.c [new file with mode: 0644]
usr/src/usr.bin/more/less.h [new file with mode: 0644]
usr/src/usr.bin/more/lesskey.1 [new file with mode: 0644]
usr/src/usr.bin/more/lesskey.c [new file with mode: 0644]
usr/src/usr.bin/more/line.c [new file with mode: 0644]
usr/src/usr.bin/more/linenum.c [new file with mode: 0644]
usr/src/usr.bin/more/linstall [new file with mode: 0644]
usr/src/usr.bin/more/main.c [new file with mode: 0644]
usr/src/usr.bin/more/mkfuncs.awk [new file with mode: 0644]
usr/src/usr.bin/more/more.1 [new file with mode: 0644]
usr/src/usr.bin/more/more.help [new file with mode: 0644]
usr/src/usr.bin/more/option.c [new file with mode: 0644]
usr/src/usr.bin/more/os.c [new file with mode: 0644]
usr/src/usr.bin/more/output.c [new file with mode: 0644]
usr/src/usr.bin/more/position.c [new file with mode: 0644]
usr/src/usr.bin/more/position.h [new file with mode: 0644]
usr/src/usr.bin/more/prim.c [new file with mode: 0644]
usr/src/usr.bin/more/prompt.c [new file with mode: 0644]
usr/src/usr.bin/more/screen.c [new file with mode: 0644]
usr/src/usr.bin/more/signal.c [new file with mode: 0644]
usr/src/usr.bin/more/tags.c [new file with mode: 0644]
usr/src/usr.bin/more/ttyin.c [new file with mode: 0644]
usr/src/usr.bin/more/vecho.c [new file with mode: 0644]
usr/src/usr.bin/more/version.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/more/README b/usr/src/usr.bin/more/README
new file mode 100644 (file)
index 0000000..b7cb1db
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)README      5.1 (Berkeley) %G%
+ */
+
+This is the distribution of "less", a paginator similar to "more" or "pg".
+The manual page is in less.man (nroff source in less.nro).
+
+INSTALLATION:
+
+1. Move the distributed source to its own directory and 
+   unpack it by running "sh" on the distribution files,
+   if you have not already done so.
+
+2. Type "sh linstall" and answer the questions it asks.
+   This will generate a makefile and a defines.h.
+
+   If you choose not to include some features in your version,
+   you may wish to edit the manual page "less.nro" and the help
+   page "less.help" to remove the references to the appropriate 
+   commands or options.
+
+3. It is a good idea to look over the generated makefile 
+   and make sure it looks ok.
+
+4. Type "make" and watch the fun.
+
+5. If the make succeeds, it will generate a program "less"
+   in your current directory.  Test the generated program.
+
+6. When satisfied that it works, if you wish to install it
+   in a public place, type "make install".
+
+If you have any problems building or running "less", 
+suggestions, complaints, etc., you may mail to the 
+author via USENET at:
+       sun          \
+       decwrl        } !pyramid!ctnews!mitisft!markn
+       ihnp4!hplabs /
+       
+
+Note to hackers: comments noting possible improvements are enclosed
+in double curly brackets {{ like this }}.
diff --git a/usr/src/usr.bin/more/ch.c b/usr/src/usr.bin/more/ch.c
new file mode 100644 (file)
index 0000000..6760531
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ch.c       5.1 (Berkeley) %G%";
+#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 "less.h"
+
+public int file = -1;          /* File descriptor of the input file */
+
+/*
+ * Pool of buffers holding the most recently used blocks of the input file.
+ */
+#define BUFSIZ 1024
+struct buf {
+       struct buf *next, *prev;
+       long block;
+       int datasize;
+       char data[BUFSIZ];
+};
+public 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 clean_data;
+extern int ispipe;
+extern int autobuf;
+extern int cbufs;
+extern int sigs;
+#if LOGFILE
+extern int logfile;
+#endif
+
+/*
+ * 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 POSITION ch_fsize;
+
+/*
+ * Number of bytes read, if input is standard input (a pipe).
+ */
+static POSITION 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 int
+fch_get()
+{
+       register struct buf *bp;
+       register int n;
+       register char *p;
+       POSITION pos;
+
+       /*
+        * 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 autobuf is true, and input is a pipe, 
+        * then try to allocate a new buffer first.
+        */
+       if (autobuf && 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
+               lseek(file, pos, 0);
+
+       /*
+        * 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;
+
+#if LOGFILE
+       /*
+        * If we have a log file, write the new data to it.
+        */
+       if (logfile >= 0 && n > 0)
+               write(logfile, &bp->data[bp->datasize], n);
+#endif
+
+       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.
+        */
+       if (n == 0)
+       {
+               ch_fsize = pos;
+               bp->data[bp->datasize++] = EOI;
+       }
+
+       if (!clean_data)
+       {
+               p = &bp->data[bp->datasize];
+               while (--n >= 0)
+               {
+                       *--p &= 0177;
+                       if (*p == EOI)
+                               *p = '@';
+               }
+       }
+
+    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]);
+}
+
+#if LOGFILE
+/*
+ * Close the logfile.
+ * If we haven't read all of standard input into it, do that now.
+ */
+       public void
+end_logfile()
+{
+       static int tried = 0;
+
+       if (logfile < 0)
+               return;
+       if (!tried && ch_fsize == NULL_POSITION)
+       {
+               tried = 1;
+               ierror("finishing logfile");
+               while (ch_forw_get() != EOI)
+                       if (sigs)
+                               break;
+       }
+       close(logfile);
+       logfile = -1;
+}
+
+/*
+ * Start a log file AFTER less has already been running.
+ * Invoked from the - command; see toggle_option().
+ * Write all the existing buffered data to the log file.
+ */
+       public void
+sync_logfile()
+{
+       register struct buf *bp;
+       register int n;
+       long block;
+       long last_block;
+
+       last_block = (last_piped_pos + BUFSIZ - 1) / BUFSIZ;
+       for (block = 0;  block <= last_block;  block++)
+               for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
+                       if (bp->block == block)
+                       {
+                               n = bp->datasize;
+                               if (bp->data[n-1] == EOI)
+                                       n--;
+                               write(logfile, bp->data, n);
+                               break;
+                       }
+}
+
+#endif
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+       static int
+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.
+ */
+       public int
+ch_seek(pos)
+       register POSITION 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.
+ */
+       public int
+ch_end_seek()
+{
+       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.
+ */
+       public int
+ch_beg_seek()
+{
+       register struct buf *bp, *firstbp;
+
+       /*
+        * Try a plain ch_seek first.
+        */
+       if (ch_seek((POSITION)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.
+ */
+       public POSITION
+ch_length()
+{
+       if (ispipe)
+               return (ch_fsize);
+       return ((POSITION)(lseek(file, (offset_t)0, 2)));
+}
+
+/*
+ * Return the current position in the file.
+ */
+       public POSITION
+ch_tell()
+{
+       return (ch_block * BUFSIZ + ch_offset);
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+       public int
+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.
+ */
+       public int
+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.
+ */
+       public void
+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.
+                */
+               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 = (POSITION)0;
+       ch_fsize = NULL_POSITION;
+       (void) ch_seek((POSITION)0);
+}
+
+/*
+ * Allocate some new buffers.
+ * The buffers are added to the tail of the buffer chain.
+ */
+       static int
+ch_addbuf(nnew)
+       int nnew;
+{
+       register struct buf *bp;
+       register struct buf *newbufs;
+
+       /*
+        * We don't have enough buffers.  
+        * Allocate some new ones.
+        */
+       newbufs = (struct buf *) calloc(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/src/usr.bin/more/cmd.h b/usr/src/usr.bin/more/cmd.h
new file mode 100644 (file)
index 0000000..da15744
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)cmd.h       5.1 (Berkeley) %G%
+ */
+
+#define        MAX_USERCMD             200
+#define        MAX_CMDLEN              16
+
+#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_DISP_OPTION           7
+#define        A_DEBUG                 8
+#define        A_EXAMINE               9
+#define        A_FIRSTCMD              10
+#define        A_FREPAINT              11
+#define        A_F_LINE                12
+#define        A_F_SCREEN              13
+#define        A_F_SCROLL              14
+#define        A_F_SEARCH              15
+#define        A_GOEND                 16
+#define        A_GOLINE                17
+#define        A_GOMARK                18
+#define        A_HELP                  19
+#define        A_NEXT_FILE             20
+#define        A_PERCENT               21
+#define        A_PREFIX                22
+#define        A_PREV_FILE             23
+#define        A_QUIT                  24
+#define        A_REPAINT               25
+#define        A_SETMARK               26
+#define        A_SHELL                 27
+#define        A_STAT                  28
+
+#define        A_TOGGLE_OPTION         30
+#define        A_VERSION               31
+#define        A_VISUAL                32
+
+#define        A_INVALID               100
+#define        A_NOACTION              101
diff --git a/usr/src/usr.bin/more/command.c b/usr/src/usr.bin/more/command.c
new file mode 100644 (file)
index 0000000..8e3efc1
--- /dev/null
@@ -0,0 +1,884 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)command.c  5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * User-level command processor.
+ */
+
+#include "less.h"
+#include "position.h"
+#include "cmd.h"
+
+#define        NO_MCA          0
+#define        MCA_DONE        1
+#define        MCA_MORE        2
+
+extern int erase_char, kill_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 char *first_cmd;
+extern char *every_first_cmd;
+extern char version[];
+extern char *current_file;
+#if EDITOR
+extern char *editor;
+#endif
+extern int screen_trashed;     /* The screen has been overwritten */
+
+static char cmdbuf[120];       /* Buffer for holding a multi-char command */
+#if SHELL_ESCAPE
+static char *shellcmd = NULL;  /* For holding last shell command for "!!" */
+#endif
+static char *cp;               /* Pointer into cmdbuf */
+static int cmd_col;            /* Current column of the multi-char command */
+static int mca;                        /* The multicharacter command (action) */
+static int last_mca;           /* The previous mca */
+static int number;             /* The number typed by the user */
+static int wsearch;            /* Search for matches (1) or non-matches (0) */
+
+/*
+ * Reset command buffer (to empty).
+ */
+cmd_reset()
+{
+       cp = cmdbuf;
+}
+
+/*
+ * Backspace in command buffer.
+ */
+       static int
+cmd_erase()
+{
+       if (cp == cmdbuf)
+               /*
+                * Backspace past beginning of the string:
+                * this usually means abort the command.
+                */
+               return (1);
+
+       if (control_char(*--cp))
+       {
+               /*
+                * Erase an extra character, for the carat.
+                */
+               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 int
+cmd_char(c)
+       int c;
+{
+       if (c == erase_char)
+       {
+               if (cmd_erase())
+                       return (1);
+       } else if (c == kill_char)
+       {
+               /* {{ Could do this faster, but who cares? }} */
+               while (cmd_erase() == 0)
+                       ;
+       } else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
+       {
+               /*
+                * No room in the command buffer.
+                */
+               bell();
+       } else if (cmd_col >= sc_width-3)
+       {
+               /*
+                * No room on the screen.
+                * {{ Could get fancy here; maybe shift the displayed
+                *    line and make room for more chars, like ksh. }}
+                */
+               bell();
+       } else
+       {
+               /*
+                * Append the character to the string.
+                */
+               *cp++ = c;
+               if (control_char(c))
+               {
+                       putchr('^');
+                       cmd_col++;
+                       c = carat_char(c);
+               }
+               putchr(c);
+               cmd_col++;
+       }
+       return (0);
+}
+
+/*
+ * Return the number currently in the command buffer.
+ */
+       static int
+cmd_int()
+{
+       *cp = '\0';
+       cp = cmdbuf;
+       return (atoi(cmdbuf));
+}
+
+/*
+ * Move the cursor to lower left before executing a command.
+ * This looks nicer if the command takes a long time before
+ * updating the screen.
+ */
+       static void
+cmd_exec()
+{
+       lower_left();
+       flush();
+}
+
+/*
+ * Display the appropriate prompt.
+ */
+       static void
+prompt()
+{
+       register char *p;
+
+       if (first_cmd != NULL && *first_cmd != '\0')
+       {
+               /*
+                * No prompt necessary if commands are from first_cmd
+                * rather than from the user.
+                */
+               return;
+       }
+
+       /*
+        * If nothing is displayed yet, display starting from line 1.
+        */
+       if (position(TOP) == NULL_POSITION)
+               jump_back(1);
+       else if (screen_trashed)
+               repaint();
+
+       /*
+        * If the -E flag is set and we've hit EOF on the last file, quit.
+        */
+       if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
+               quit();
+
+       /*
+        * Select the proper prompt and display it.
+        */
+       lower_left();
+       clear_eol();
+       p = pr_string();
+       if (p == NULL)
+               putchr(':');
+       else
+       {
+               so_enter();
+               putstr(p);
+               so_exit();
+       }
+}
+
+/*
+ * Get command character.
+ * The character normally comes from the keyboard,
+ * but may come from the "first_cmd" string.
+ */
+       static int
+getcc()
+{
+       if (first_cmd == NULL)
+               return (getchr());
+
+       if (*first_cmd == '\0')
+       {
+               /*
+                * Reached end of first_cmd input.
+                */
+               first_cmd = NULL;
+               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());
+       }
+       return (*first_cmd++);
+}
+
+/*
+ * Execute a multicharacter command.
+ */
+       static void
+exec_mca()
+{
+       register char *p;
+       register int n;
+
+       *cp = '\0';
+       cmd_exec();
+       switch (mca)
+       {
+       case A_F_SEARCH:
+               search(1, cmdbuf, number, wsearch);
+               break;
+       case A_B_SEARCH:
+               search(0, cmdbuf, number, wsearch);
+               break;
+       case A_FIRSTCMD:
+               /*
+                * Skip leading spaces or + signs in the string.
+                */
+               for (p = cmdbuf;  *p == '+' || *p == ' ';  p++)
+                       ;
+               if (every_first_cmd != NULL)
+                       free(every_first_cmd);
+               if (*p == '\0')
+                       every_first_cmd = NULL;
+               else
+                       every_first_cmd = save(p);
+               break;
+       case A_TOGGLE_OPTION:
+               toggle_option(cmdbuf, 1);
+               break;
+       case A_EXAMINE:
+               /*
+                * Ignore leading spaces in the filename.
+                */
+               for (p = cmdbuf;  *p == ' ';  p++)
+                       ;
+               edit(glob(p));
+               break;
+#if SHELL_ESCAPE
+       case A_SHELL:
+               /*
+                * !! just uses whatever is in shellcmd.
+                * Otherwise, copy cmdbuf to shellcmd,
+                * replacing any '%' with the current
+                * file name.
+                */
+               if (*cmdbuf != '!')
+               {
+                       register char *fr, *to;
+
+                       /*
+                        * Make one pass to see how big a buffer we 
+                        * need to allocate for the expanded shell cmd.
+                        */
+                       for (fr = cmdbuf;  *fr != '\0';  fr++)
+                               if (*fr == '%')
+                                       n += strlen(current_file);
+                               else
+                                       n++;
+
+                       if (shellcmd != NULL)
+                               free(shellcmd);
+                       shellcmd = calloc(n+1, sizeof(char));
+                       if (shellcmd == NULL)
+                       {
+                               error("cannot allocate memory");
+                               break;
+                       }
+
+                       /*
+                        * Now copy the shell cmd, expanding any "%"
+                        * into the current filename.
+                        */
+                       to = shellcmd;
+                       for (fr = cmdbuf;  *fr != '\0';  fr++)
+                       {
+                               if (*fr != '%')
+                                       *to++ = *fr;
+                               else
+                               {
+                                       strcpy(to, current_file);
+                                       to += strlen(to);
+                               }
+                       }
+                       *to = '\0';
+               }
+
+               if (shellcmd == NULL)
+                       lsystem("");
+               else
+                       lsystem(shellcmd);
+               error("!done");
+               break;
+#endif
+       }
+}
+
+/*
+ * Add a character to a multi-character command.
+ */
+       static int
+mca_char(c)
+       int c;
+{
+       switch (mca)
+       {
+       case 0:
+               /*
+                * Not in a multicharacter command.
+                */
+               return (NO_MCA);
+
+       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 ((c < '0' || c > '9') &&
+                       c != erase_char && c != kill_char)
+               {
+                       /*
+                        * Not part of the number.
+                        * Treat as a normal command character.
+                        */
+                       number = cmd_int();
+                       mca = 0;
+                       return (NO_MCA);
+               }
+               break;
+
+       case A_TOGGLE_OPTION:
+               /*
+                * Special case for the TOGGLE_OPTION command.
+                * if the option letter which was entered is a
+                * single-char option, execute the command immediately,
+                * so he doesn't have to hit RETURN.
+                */
+               if (cp == cmdbuf && c != erase_char && c != kill_char &&
+                   single_char_option(c))
+               {
+                       cmdbuf[0] = c;
+                       cmdbuf[1] = '\0';
+                       toggle_option(cmdbuf, 1);
+                       return (MCA_DONE);
+               }
+               break;
+       }
+
+       /*
+        * Any other multicharacter command
+        * is terminated by a newline.
+        */
+       if (c == '\n' || c == '\r')
+       {
+               /*
+                * Execute the command.
+                */
+               exec_mca();
+               return (MCA_DONE);
+       }
+       /*
+        * Append the char to the command buffer.
+        */
+       if (cmd_char(c))
+               /*
+                * Abort the multi-char command.
+                */
+               return (MCA_DONE);
+       /*
+        * Need another character.
+        */
+       return (MCA_MORE);
+}
+
+/*
+ * Main command processor.
+ * Accept and execute commands until a quit command, then return.
+ */
+       public void
+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();
+               prompt();
+               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.
+                        */
+                       if (number <= 0)
+                               number = sc_window;
+                       if (number <= 0)
+                               number = sc_height - 1;
+                       cmd_exec();
+                       forward(number, 1);
+                       break;
+
+               case A_B_SCREEN:
+                       /*
+                        * Backward one screen.
+                        */
+                       if (number <= 0)
+                               number = sc_window;
+                       if (number <= 0)
+                               number = sc_height - 1;
+                       cmd_exec();
+                       backward(number, 1);
+                       break;
+
+               case A_F_LINE:
+                       /*
+                        * Forward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       forward(number, 0);
+                       break;
+
+               case A_B_LINE:
+                       /*
+                        * Backward N (default 1) line.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       backward(number, 0);
+                       break;
+
+               case A_F_SCROLL:
+                       /*
+                        * Forward N lines 
+                        * (default same as last 'd' or 'u' command).
+                        */
+                       if (number > 0)
+                               scroll = number;
+                       cmd_exec();
+                       forward(scroll, 0);
+                       break;
+
+               case A_B_SCROLL:
+                       /*
+                        * Forward N lines 
+                        * (default same as last 'd' or 'u' command).
+                        */
+                       if (number > 0)
+                               scroll = number;
+                       cmd_exec();
+                       backward(scroll, 0);
+                       break;
+
+               case A_FREPAINT:
+                       /*
+                        * Flush buffers, then repaint screen.
+                        * Don't flush the buffers on a pipe!
+                        */
+                       if (!ispipe)
+                       {
+                               ch_init(0, 0);
+                               clr_linenum();
+                       }
+                       /* FALLTHRU */
+               case A_REPAINT:
+                       /*
+                        * Repaint screen.
+                        */
+                       cmd_exec();
+                       repaint();
+                       break;
+
+               case A_GOLINE:
+                       /*
+                        * Go to line N, default beginning of file.
+                        */
+                       if (number <= 0)
+                               number = 1;
+                       cmd_exec();
+                       jump_back(number);
+                       break;
+
+               case A_PERCENT:
+                       /*
+                        * Go to a specified percentage into the file.
+                        */
+                       if (number < 0)
+                               number = 0;
+                       if (number > 100)
+                               number = 100;
+                       cmd_exec();
+                       jump_percent(number);
+                       break;
+
+               case A_GOEND:
+                       /*
+                        * Go to line N, default end of file.
+                        */
+                       cmd_exec();
+                       if (number <= 0)
+                               jump_forw();
+                       else
+                               jump_back(number);
+                       break;
+
+               case A_STAT:
+                       /*
+                        * Print file name, etc.
+                        */
+                       cmd_exec();
+                       error(eq_message());
+                       break;
+                       
+               case A_VERSION:
+                       /*
+                        * Print version number, without the "@(#)".
+                        */
+                       cmd_exec();
+                       error(version+4);
+                       break;
+
+               case A_QUIT:
+                       /*
+                        * Exit.
+                        */
+                       quit();
+
+               case A_F_SEARCH:
+               case A_B_SEARCH:
+                       /*
+                        * Search for a pattern.
+                        * Accept chars of the pattern until \n.
+                        */
+                       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();
+                       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_EXAMINE:
+                       /*
+                        * Edit a new file.  Get the filename.
+                        */
+                       cmd_reset();
+                       start_mca(A_EXAMINE, "Examine: ");
+                       c = getcc();
+                       goto again;
+                       
+               case A_VISUAL:
+                       /*
+                        * Invoke an editor on the input file.
+                        */
+#if EDITOR
+                       if (ispipe)
+                       {
+                               error("Cannot edit standard input");
+                               break;
+                       }
+                       /*
+                        * Try to pass the line number to the editor.
+                        */
+                       cmd_exec();
+                       c = currline(MIDDLE);
+                       if (c == 0)
+                               sprintf(cmdbuf, "%s %s",
+                                       editor, current_file);
+                       else
+                               sprintf(cmdbuf, "%s +%d %s",
+                                       editor, c, current_file);
+                       lsystem(cmdbuf);
+                       ch_init(0, 0);
+                       clr_linenum();
+                       break;
+#else
+                       error("Command not available");
+                       break;
+#endif
+
+               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_TOGGLE_OPTION:
+                       /*
+                        * Toggle a flag setting.
+                        */
+                       cmd_reset();
+                       start_mca(A_TOGGLE_OPTION, "-");
+                       c = getcc();
+                       goto again;
+
+               case A_DISP_OPTION:
+                       /*
+                        * Report a flag setting.
+                        */
+                       cmd_reset();
+                       start_mca(A_DISP_OPTION, "_");
+                       c = getcc();
+                       if (c == erase_char || c == kill_char)
+                               break;
+                       cmdbuf[0] = c;
+                       cmdbuf[1] = '\0';
+                       toggle_option(cmdbuf, 0);
+                       break;
+
+               case A_FIRSTCMD:
+                       /*
+                        * Set an initial command for new files.
+                        */
+                       cmd_reset();
+                       start_mca(A_FIRSTCMD, "+");
+                       c = getcc();
+                       goto again;
+
+               case A_SHELL:
+                       /*
+                        * Shell escape.
+                        */
+#if SHELL_ESCAPE
+                       cmd_reset();
+                       start_mca(A_SHELL, "!");
+                       c = getcc();
+                       goto again;
+#else
+                       error("Command not available");
+                       break;
+#endif
+
+               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 a mark.
+                        */
+                       lower_left();
+                       clear_eol();
+                       start_mca(A_GOMARK, "goto mark: ");
+                       c = getcc();
+                       if (c == erase_char || c == kill_char)
+                               break;
+                       gomark(c);
+                       break;
+
+               case A_PREFIX:
+                       /*
+                        * The command is incomplete (more chars are needed).
+                        * Display the current char so the user knows
+                        * what's going on and get another character.
+                        */
+                       if (mca != A_PREFIX)
+                               start_mca(A_PREFIX, "& ");
+                       if (control_char(c))
+                       {
+                               putchr('^');
+                               c = carat_char(c);
+                       }
+                       putchr(c);
+                       c = getcc();
+                       goto again;
+
+               default:
+                       bell();
+                       break;
+               }
+       }
+}
diff --git a/usr/src/usr.bin/more/decode.c b/usr/src/usr.bin/more/decode.c
new file mode 100644 (file)
index 0000000..2b5eb25
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)decode.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Routines to decode user commands.
+ *
+ * This is all table driven.
+ * A command table is a sequence of command descriptors.
+ * Each command descriptor is a sequence of bytes with the following format:
+ *     <c1><c2>...<cN><0><action>
+ * The characters c1,c2,...,cN are the command string; that is,
+ * the characters which the user must type.
+ * It is terminated by a null <0> byte.
+ * The byte after the null byte is the action code associated
+ * with the command string.
+ *
+ * The default commands are described by cmdtable.
+ * User-defined commands are read into usertable.
+ */
+
+#include "less.h"
+#include "cmd.h"
+
+/*
+ * Command table is ordered roughly according to expected
+ * frequency of use, so the common commands are near the beginning.
+ */
+static char cmdtable[] =
+{
+       '\r',0,                         A_F_LINE,
+       '\n',0,                         A_F_LINE,
+       'e',0,                          A_F_LINE,
+       'j',0,                          A_F_LINE,
+       CONTROL('E'),0,                 A_F_LINE,
+       CONTROL('N'),0,                 A_F_LINE,
+       'k',0,                          A_B_LINE,
+       'y',0,                          A_B_LINE,
+       CONTROL('Y'),0,                 A_B_LINE,
+       CONTROL('K'),0,                 A_B_LINE,
+       CONTROL('P'),0,                 A_B_LINE,
+       'd',0,                          A_F_SCROLL,
+       CONTROL('D'),0,                 A_F_SCROLL,
+       'u',0,                          A_B_SCROLL,
+       CONTROL('U'),0,                 A_B_SCROLL,
+       ' ',0,                          A_F_SCREEN,
+       'f',0,                          A_F_SCREEN,
+       CONTROL('F'),0,                 A_F_SCREEN,
+       CONTROL('V'),0,                 A_F_SCREEN,
+       'b',0,                          A_B_SCREEN,
+       CONTROL('B'),0,                 A_B_SCREEN,
+       CONTROL('['),'v',0,             A_B_SCREEN,
+       'R',0,                          A_FREPAINT,
+       'r',0,                          A_REPAINT,
+       CONTROL('R'),0,                 A_REPAINT,
+       CONTROL('L'),0,                 A_REPAINT,
+       'g',0,                          A_GOLINE,
+       '<',0,                          A_GOLINE,
+       CONTROL('['),'<',0,             A_GOLINE,
+       'p',0,                          A_PERCENT,
+       '%',0,                          A_PERCENT,
+       'G',0,                          A_GOEND,
+       CONTROL('['),'>',0,             A_GOEND,
+       '>',0,                          A_GOEND,
+
+       '0',0,                          A_DIGIT,
+       '1',0,                          A_DIGIT,
+       '2',0,                          A_DIGIT,
+       '3',0,                          A_DIGIT,
+       '4',0,                          A_DIGIT,
+       '5',0,                          A_DIGIT,
+       '6',0,                          A_DIGIT,
+       '7',0,                          A_DIGIT,
+       '8',0,                          A_DIGIT,
+       '9',0,                          A_DIGIT,
+
+       '=',0,                          A_STAT,
+       CONTROL('G'),0,                 A_STAT,
+       '/',0,                          A_F_SEARCH,
+       '?',0,                          A_B_SEARCH,
+       'n',0,                          A_AGAIN_SEARCH,
+       'm',0,                          A_SETMARK,
+       '\'',0,                         A_GOMARK,
+       CONTROL('X'),CONTROL('X'),0,    A_GOMARK,
+       'E',0,                          A_EXAMINE,
+       ':','e',0,                      A_EXAMINE,
+       CONTROL('X'),CONTROL('V'),0,    A_EXAMINE,
+       'N',0,                          A_NEXT_FILE,
+       'P',0,                          A_PREV_FILE,
+       ':','n',0,                      A_NEXT_FILE,
+       ':','p',0,                      A_PREV_FILE,
+       '-',0,                          A_TOGGLE_OPTION,
+       '_',0,                          A_DISP_OPTION,
+       'v',0,                          A_VISUAL,
+       '!',0,                          A_SHELL,
+       '+',0,                          A_FIRSTCMD,
+
+       'H',0,                          A_HELP,
+       'h',0,                          A_HELP,
+       'V',0,                          A_VERSION,
+       'q',0,                          A_QUIT,
+       ':','q',0,                      A_QUIT,
+       'Z','Z',0,                      A_QUIT
+};
+
+char *cmdendtable = cmdtable + sizeof(cmdtable);
+
+static char usertable[MAX_USERCMD];
+char *userendtable = usertable;
+
+static char kbuf[MAX_CMDLEN+1];
+static char *kp = kbuf;
+
+/*
+ * Decode a command character and return the associated action.
+ */
+       public int
+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';
+
+#if USERFILE
+       /*
+        * Look first for any user-defined commands.
+        */
+       action = cmd_search(usertable, userendtable);
+#endif
+       /*
+        * If didn't find user-defined command,
+        * try the normal default commands.
+        */
+       if (action == A_INVALID)
+               action = cmd_search(cmdtable, cmdendtable);
+
+       if (action != A_PREFIX)
+               /*
+                * This is not a prefix character.
+                */
+               noprefix();
+
+       return (action);
+}
+
+/*
+ * Indicate that we're not in a prefix command
+ * by resetting the command buffer pointer.
+ */
+       public void
+noprefix()
+{
+       kp = kbuf;
+}
+
+/*
+ * Search a command table for the current command string (in kbuf).
+ */
+       static int
+cmd_search(table, endtable)
+       char *table;
+       char *endtable;
+{
+       register char *p;
+       register char *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);
+}
+
+/*
+ * Initialize the user command table.
+ */
+       public void
+init_cmd()
+{
+#if USERFILE
+       char *filename;
+       char *homedir;
+       int f;
+       int n;
+       extern char *getenv();
+
+       /*
+        * Try to open "$HOME/.less"
+        * If we can't, return without doing anything.
+        */
+       homedir = getenv("HOME");
+       if (homedir == NULL)
+               return;
+       filename = calloc(strlen(homedir)+7, sizeof(char));
+       if (filename == NULL)
+               return;
+       sprintf(filename, "%s/%s", homedir, ".less");
+       f = open(filename, 0);
+       free(filename);
+       if (f < 0)
+               return;
+
+       /*
+        * Read the file into the user table.
+        * {{ Minimal error checking is done here.
+        *    A garbage .less file will produce strange results.
+        *    To avoid a large amount of error checking code here, we
+        *    rely on the lesskey program to generate a good .less file. }}
+        */
+       n = read(f, (char *)usertable, MAX_USERCMD);
+       if (n < 3 || usertable[n-2] != '\0')
+       {
+               /*
+                * Several error cases are lumped together here:
+                * - Cannot read user file (n < 0).
+                * - User file is too short (a valid file must
+                *   have at least 3 chars: one char command string,
+                *   the terminating null byte, and the action byte).
+                * - The final entry in the user file is bad (it
+                *   doesn't have a null byte in the proper place).
+                * Many other error cases are not caught, such as
+                * invalid format in any except the last entry,
+                * invalid action codes, command strings too long, etc.
+                */
+               error("invalid user key file");
+               n = 0;
+       }
+       userendtable = usertable + n;
+       close(f);
+#endif
+}
diff --git a/usr/src/usr.bin/more/help.c b/usr/src/usr.bin/more/help.c
new file mode 100644 (file)
index 0000000..7e758a2
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)help.c     5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include  "less.h"
+
+/*
+ * Display some help.
+ * Just invoke another "less" to display the help file.
+ *
+ * {{ This makes this function very simple, and makes changing the
+ *    help file very easy, but it may present difficulties on
+ *    (non-Unix) systems which do not supply the "system()" function. }}
+ */
+
+       public void
+help()
+{
+       char cmd[FILENAME+100];
+
+       sprintf(cmd, 
+        "-less -m '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
+        HELPFILE);
+       lsystem(cmd);
+       error("End of help");
+}
diff --git a/usr/src/usr.bin/more/input.c b/usr/src/usr.bin/more/input.c
new file mode 100644 (file)
index 0000000..d68314c
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c    5.1 (Berkeley) %G%";
+#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 "less.h"
+
+extern int squeeze;
+extern int sigs;
+extern char *line;
+
+/*
+ * 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.
+ */
+       public POSITION
+forw_line(curr_pos)
+       POSITION curr_pos;
+{
+       POSITION 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.
+ */
+       public POSITION
+back_line(curr_pos)
+       POSITION curr_pos;
+{
+       POSITION new_pos, begin_new_pos;
+       int c;
+
+       if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)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/src/usr.bin/more/less.h b/usr/src/usr.bin/more/less.h
new file mode 100644 (file)
index 0000000..765c108
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)less.h      5.1 (Berkeley) %G%
+ */
+
+/*
+ * Standard include file for "less".
+ */
+
+/*
+ * Include the file of compile-time options.
+ */
+#include "defines.h"
+
+/*
+ * Language details.
+ */
+#if !VOID
+#define        void  int
+#endif
+#define        public          /* PUBLIC FUNCTION */
+
+/*
+ * Special types and constants.
+ */
+typedef long           POSITION;
+/*
+ * {{ Warning: if POSITION is changed to other than "long",
+ *    you may have to change some of the printfs which use "%ld"
+ *    to print a variable of type POSITION. }}
+ */
+
+#define        NULL_POSITION   ((POSITION)(-1))
+
+/*
+ * The type of signal handler functions.
+ * Usually int, although it should be void.
+ */
+typedef        int             HANDLER;
+
+
+#define        FILENAME        128     /* Max size of a filename */
+
+#define        EOI             (0)
+#ifndef NULL
+#define        NULL            (0)
+#endif
+
+#define        READ_INTR       (-2)
+
+/* How quiet should we be? */
+#define        NOT_QUIET       0       /* Ring bell at eof and for errors */
+#define        LITTLE_QUIET    1       /* Ring bell only for errors */
+#define        VERY_QUIET      2       /* Never ring bell */
+
+/* How should we prompt? */
+#define        PR_SHORT        0       /* Prompt with colon */
+#define        PR_MEDIUM       1       /* Prompt with message */
+#define        PR_LONG         2       /* Prompt with longer message */
+
+/* How should we handle backspaces? */
+#define        BS_SPECIAL      0       /* Do special things for underlining and bold */
+#define        BS_NORMAL       1       /* \b treated as normal char; actually output */
+#define        BS_CONTROL      2       /* \b treated as control char; prints as ^H */
+
+/* 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(c)              ((c)&037)
+#define        SIGNAL(sig,func)        signal(sig,func)
+
+/* Library function declarations */
+offset_t lseek();
+char *calloc();
+
+#include "funcs.h"
diff --git a/usr/src/usr.bin/more/lesskey.1 b/usr/src/usr.bin/more/lesskey.1
new file mode 100644 (file)
index 0000000..69171d4
--- /dev/null
@@ -0,0 +1,124 @@
+.\"
+.\" Copyright (c) 1988 Mark Nudleman
+.\" Copyright (c) 1988 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Mark Nudleman.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley.  The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\"    @(#)lesskey.1   5.1 (Berkeley) %G%
+.\"
+.TH LESSKEY 1
+.SH NAME
+lesskey \- specify key bindings for less
+.SH SYNOPSIS
+.B "lesskey [-o output] [input]"
+.SH DESCRIPTION
+.I Lesskey
+is used to specify a set of key bindings to be used by 
+.I less.
+The input file is a text file which describes the key bindings,
+and the output file is a binary file which is used by 
+.I less.
+If no input file is specified, standard input is used.
+If no output file is specified, $HOME/.less is used.
+.PP
+The input file consists of lines of the form:
+.sp
+       string <whitespace> action <newline>
+.sp
+Whitespace is any sequence of one or more spaces and/or tabs.
+The "string" is the command key(s) which invoke the action.
+The string may be a single command key, or a sequence of up to 15 keys.
+The "action" is the name of the less action, from the list below.
+The characters in the "string" may appear literally, or be
+prefixed by a carat to indicate a control key.
+A backslash may be used to cause the following character
+to be taken literally.
+Characters which must be preceeded by backslash include
+carat, space, tab and the backslash itself.
+A backslash followed by one to three octal digits may be used to
+specify a character by its octal value.
+Blank lines and lines which start with a pound sign (#) are ignored.
+.PP
+As an example, the following input file describes the set of
+default command keys used by less:
+.sp
+.nf
+       k                       back-line
+       y                       back-line
+       ^K                      back-line
+       ^Y                      back-line
+       ^P                      back-line
+       b                       back-screen
+       ^B                      back-screen
+       \\33v                   back-screen
+       u                       back-scroll
+       ^U                      back-scroll
+       ?                       back-search
+       E                       examine
+       ^X^V                    examine
+       +                       first-cmd
+       e                       forw-line
+       j                       forw-line
+       ^E                      forw-line
+       ^J                      forw-line
+       ^M                      forw-line
+       ^N                      forw-line
+       f                       forw-screen
+       ^F                      forw-screen
+       \\40                    forw-screen
+       ^V                      forw-screen
+       d                       forw-scroll
+       ^D                      forw-scroll
+       /                       forw-search
+       G                       goto-end
+       >                       goto-end
+       \\33>                   goto-end
+       g                       goto-line
+       <                       goto-line
+       \\33<                   goto-line
+       '                       goto-mark
+       ^X^X                    goto-mark
+       H                       help
+       N                       next-file
+       %                       percent
+       p                       percent
+       P                       prev-file
+       q                       quit
+       ZZ                      quit
+       ^L                      repaint
+       ^R                      repaint
+       r                       repaint
+       R                       flush-repaint
+       n                       repeat-search
+       m                       set-mark
+       !                       shell
+       =                       status
+       ^G                      status
+       -                       toggle-option
+       _                       display-option
+       V                       version
+       v                       visual
+.fi
+.sp
+Commands specified by
+.I lesskey
+take precedence over the default commands.
+A default command key may be disabled by including it in the
+key file with the action "invalid".
+
+.SH "SEE ALSO"
+less(1)
diff --git a/usr/src/usr.bin/more/lesskey.c b/usr/src/usr.bin/more/lesskey.c
new file mode 100644 (file)
index 0000000..17577c6
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#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[] = "@(#)lesskey.c  5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ *     lesskey [-o output] [input]
+ *
+ *     Make a .less file.
+ *     If no input file is specified, standard input is used.
+ *     If no output file is specified, $HOME/.less is used.
+ *
+ *     The .less file is used to specify (to "less") user-defined
+ *     key bindings.  Basically any sequence of 1 to MAX_CMDLEN
+ *     keystrokes may be bound to an existing less function.
+ *
+ *     The input file is an ascii file consisting of a 
+ *     sequence of lines of the form:
+ *             string <whitespace> action <newline>
+ *
+ *     "string" is a sequence of command characters which form
+ *             the new user-defined command.  The command
+ *             characters may be:
+ *             1. The actual character itself.
+ *             2. A character preceeded by ^ to specify a
+ *                control character (e.g. ^X means control-X).
+ *             3. Any character (other than an octal digit) preceeded by
+ *                a \ to specify the character itself (characters which
+ *                must be preceeded by \ include ^, \, and whitespace.
+ *             4. A backslash followed by one to three octal digits
+ *                to specify a character by its octal value.
+ *     "action" is the name of a "less" action, from the table below.
+ *
+ *     Blank lines and lines which start with # are ignored.
+ *
+ *
+ *     The output file is a non-ascii file, consisting of
+ *     zero or more byte sequences of the form:
+ *             string <0> <action>
+ *
+ *     "string" is the command string.
+ *     "<0>" is one null byte.
+ *     "<action>" is one byte containing the action code (the A_xxx value).
+ *
+ *
+ *             Revision history
+ *
+ *     v1: Initial version.                                    10/13/87  mark
+ */
+
+#include <stdio.h>
+#include "less.h"
+#include "cmd.h"
+
+char usertable[MAX_USERCMD];
+
+struct cmdname
+{
+       char *cn_name;
+       int cn_action;
+} cmdnames[] = 
+{
+       "back-line",            A_B_LINE,
+       "back-screen",          A_B_SCREEN,
+       "back-scroll",          A_B_SCROLL,
+       "back-search",          A_B_SEARCH,
+       "debug",                A_DEBUG,
+       "display-flag",         A_DISP_OPTION,
+       "display-option",       A_DISP_OPTION,
+       "end",                  A_GOEND,
+       "examine",              A_EXAMINE,
+       "first-cmd",            A_FIRSTCMD,
+       "firstcmd",             A_FIRSTCMD,
+       "flush-repaint",        A_FREPAINT,
+       "forw-line",            A_F_LINE,
+       "forw-screen",          A_F_SCREEN,
+       "forw-scroll",          A_F_SCROLL,
+       "forw-search",          A_F_SEARCH,
+       "goto-end",             A_GOEND,
+       "goto-line",            A_GOLINE,
+       "goto-mark",            A_GOMARK,
+       "help",                 A_HELP,
+       "invalid",              A_NOACTION,
+       "next-file",            A_NEXT_FILE,
+       "noaction",             A_NOACTION,
+       "percent",              A_PERCENT,
+       "prev-file",            A_PREV_FILE,
+       "quit",                 A_QUIT,
+       "repaint",              A_REPAINT,
+       "repaint-flush",        A_FREPAINT,
+       "repeat-search",        A_AGAIN_SEARCH,
+       "set-mark",             A_SETMARK,
+       "shell",                A_SHELL,
+       "status",               A_STAT,
+       "toggle-flag",          A_TOGGLE_OPTION,
+       "toggle-option",        A_TOGGLE_OPTION,
+       "version",              A_VERSION,
+       "visual",               A_VISUAL,
+       NULL,                   0
+};
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *p;                /* {{ Can't be register since we use &p }} */
+       register char *up;      /* Pointer into usertable */
+       FILE *desc;             /* Description file (input) */
+       FILE *out;              /* Output file */
+       int linenum;            /* Line number in input file */
+       char *currcmd;          /* Start of current command string */
+       int errors;
+       int i;
+       char line[100];
+       char *outfile;
+
+       extern char *getenv();
+
+       /*
+        * Process command line arguments.
+        */
+       outfile = NULL;
+       while (--argc > 0 && **(++argv) == '-')
+       {
+               switch (argv[0][1])
+               {
+               case 'o':
+                       outfile = &argv[0][2];
+                       if (*outfile == '\0')
+                       {
+                               if (--argc <= 0)
+                                       usage();
+                               outfile = *(++argv);
+                       }
+                       break;
+               default:
+                       usage();
+               }
+       }
+       if (argc > 1)
+               usage();
+
+
+       /*
+        * Open the input file, or use standard input if none specified.
+        */
+       if (argc > 0)
+       {
+               if ((desc = fopen(*argv, "r")) == NULL)
+               {
+                       perror(*argv);
+                       exit(1);
+               }
+       } else
+               desc = stdin;
+
+       /*
+        * Read the input file, one line at a time.
+        * Each line consists of a command string,
+        * followed by white space, followed by an action name.
+        */
+       linenum = 0;
+       errors = 0;
+       up = usertable;
+       while (fgets(line, sizeof(line), desc) != NULL)
+       {
+               ++linenum;
+
+               /*
+                * Skip leading white space.
+                * Replace the final newline with a null byte.
+                * Ignore blank lines and comment lines.
+                */
+               p = line;
+               while (*p == ' ' || *p == '\t')
+                       ++p;
+               for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
+                       ;
+               p[i] = '\0';
+               if (*p == '#' || *p == '\0')
+                       continue;
+
+               /*
+                * Parse the command string and store it in the usertable.
+                */
+               currcmd = up;
+               do
+               {
+                       if (up >= usertable + MAX_USERCMD)
+                       {
+                               fprintf(stderr, "too many commands, line %d\n",
+                                       linenum);
+                               exit(1);
+                       }
+                       if (up >= currcmd + MAX_CMDLEN)
+                       {
+                               fprintf(stderr, "command too long on line %d\n",
+                                       linenum);
+                               errors++;
+                               break;
+                       }
+
+                       *up++ = tchar(&p);
+
+               } while (*p != ' ' && *p != '\t' && *p != '\0');
+
+               /*
+                * Terminate the command string with a null byte.
+                */
+               *up++ = '\0';
+
+               /*
+                * Skip white space between the command string
+                * and the action name.
+                * Terminate the action name if it is followed
+                * by whitespace or a # comment.
+                */
+               if (*p == '\0')
+               {
+                       fprintf(stderr, "missing whitespace on line %d\n",
+                               linenum);
+                       errors++;
+                       continue;
+               }
+               while (*p == ' ' || *p == '\t')
+                       ++p;
+               for (i = 0;  p[i] != ' ' && p[i] != '\t' && 
+                            p[i] != '#' && p[i] != '\0';  i++)
+                       ;
+               p[i] = '\0';
+
+               /*
+                * Parse the action name and store it in the usertable.
+                */
+               for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
+                       if (strcmp(cmdnames[i].cn_name, p) == 0)
+                               break;
+               if (cmdnames[i].cn_name == NULL)
+               {
+                       fprintf(stderr, "unknown action <%s> on line %d\n",
+                               p, linenum);
+                       errors++;
+                       continue;
+               }
+               *up++ = cmdnames[i].cn_action;
+       }
+
+       if (errors > 0)
+       {
+               fprintf(stderr, "%d errors; no output produced\n", errors);
+               exit(1);
+       }
+
+       /*
+        * Write the output file.
+        * If no output file was specified, use "$HOME/.less"
+        */
+       if (outfile == NULL)
+       {
+               p = getenv("HOME");
+               if (p == NULL)
+               {
+                       fprintf(stderr, "cannot find $HOME\n");
+                       exit(1);
+               }
+               strcpy(line, p);
+               strcat(line, "/.less");
+               outfile = line;
+       }
+       if ((out = fopen(outfile, "w")) == NULL)
+               perror(outfile);
+       else
+               fwrite((char *)usertable, 1, up-usertable, out);
+}
+
+/*
+ * Parse one character of the command string.
+ */
+tchar(pp)
+       char **pp;
+{
+       register char *p;
+       register char ch;
+       register int i;
+
+       p = *pp;
+       switch (*p)
+       {
+       case '\\':
+               if (*++p >= '0' && *p <= '7')
+               {
+                       /*
+                        * Parse an octal number.
+                        */
+                       ch = 0;
+                       i = 0;
+                       do
+                               ch = 8*ch + (*p - '0');
+                       while (*++p >= '0' && *p <= '7' && ++i < 3);
+                       *pp = p;
+                       return (ch);
+               }
+               /*
+                * Backslash followed by a char just means that char.
+                */
+               *pp = p+1;
+               return (*p);
+       case '^':
+               /*
+                * Carat means CONTROL.
+                */
+               *pp = p+2;
+               return (CONTROL(p[1]));
+       }
+       *pp = p+1;
+       return (*p);
+}
+
+usage()
+{
+       fprintf(stderr, "usage: lesskey [-o output] [input]\n");
+       exit(1);
+}
diff --git a/usr/src/usr.bin/more/line.c b/usr/src/usr.bin/more/line.c
new file mode 100644 (file)
index 0000000..f1afbc5
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)line.c     5.1 (Berkeley) %G%";
+#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 "less.h"
+
+static char linebuf[1024];     /* Buffer which holds the current output line */
+static char *curr;             /* Pointer into linebuf */
+static int column;             /* Printable length, accounting for
+                                  backspaces, etc. */
+/*
+ * A ridiculously complex state machine takes care of backspaces 
+ * when in BS_SPECIAL mode.  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 */
+
+public 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.
+ */
+       public void
+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(newcol)      if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
+                                       return (1); else column = (newcol)
+
+       public int
+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 == BS_SPECIAL)
+       {
+               /*
+                * Advance the state machine.
+                */
+               switch (ln_state)
+               {
+               case LN_NORMAL:
+                       if (curr <= linebuf + 1 || curr[-1] != '\b')
+                               break;
+
+                       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.
+                        */
+                       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.
+                        */
+                       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(column+1);
+               } while ((column % tabstop) != 0);
+               *curr++ = '\t';
+               return (0);
+       }
+
+       if (c == '\b')
+       {
+               if (bs_mode == BS_CONTROL)
+               {
+                       /*
+                        * Treat backspace as a control char: output "^H".
+                        */
+                       NEW_COLUMN(column+2);
+                       *curr++ = ('H' | 0200);
+               } else
+               {
+                       /*
+                        * Output a real backspace.
+                        */
+                       column--;
+                       *curr++ = '\b';
+               }
+               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.
+                * {{ This should be redone so that we can use an
+                *    8 bit (e.g. international) character set. }}
+                */
+               NEW_COLUMN(column+2);
+               *curr++ = (carat_char(c) | 0200);
+               return (0);
+       }
+
+       /*
+        * Ordinary character.  Just put it in the buffer.
+        */
+       NEW_COLUMN(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(). }}
+ */
+       public POSITION
+forw_raw_line(curr_pos)
+       POSITION curr_pos;
+{
+       register char *p;
+       register int c;
+       POSITION new_pos;
+
+       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(). }}
+ */
+       public POSITION
+back_raw_line(curr_pos)
+       POSITION curr_pos;
+{
+       register char *p;
+       register int c;
+       POSITION new_pos;
+
+       if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)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 = (POSITION)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/src/usr.bin/more/linenum.c b/usr/src/usr.bin/more/linenum.c
new file mode 100644 (file)
index 0000000..909adc3
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)linenum.c  5.1 (Berkeley) %G%";
+#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 "less.h"
+#include "position.h"
+
+/*
+ * Structure to keep track of a line number and the associated file position.
+ * A doubly-linked circular list of line numbers is kept ordered by line number.
+ */
+struct linenum
+{
+       struct linenum *next;           /* Link to next in the list */
+       struct linenum *prev;           /* Line to previous in the list */
+       POSITION pos;                   /* File position */
+       POSITION 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 */
+
+public 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.
+ */
+       public void
+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 = (POSITION)0;
+       anchor.line = 1;
+}
+
+/*
+ * Calculate the gap for an entry.
+ */
+       static void
+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.
+ */
+       public void
+add_lnum(line, pos)
+       int line;
+       POSITION pos;
+{
+       register struct linenum *p;
+       register struct linenum *new;
+       register struct linenum *nextp;
+       register struct linenum *prevp;
+       register POSITION 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 void
+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.
+ */
+       public int
+find_linenum(pos)
+       POSITION pos;
+{
+       register struct linenum *p;
+       register int lno;
+       register int loopcount;
+       POSITION cpos;
+#if GET_TIME
+       long startime;
+#endif
+
+       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 == (POSITION)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();
+#if GET_TIME
+       startime = get_time();
+#endif
+       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 GET_TIME
+                       if (loopcount >= 0 && ++loopcount > 100)
+                       {
+                               loopcount = 0;
+                               if (get_time() >= startime + LONGTIME)
+                               {
+                                       longloopmessage();
+                                       loopcount = -1;
+                               }
+                       }
+#else
+                       if (loopcount >= 0 && ++loopcount > LONGLOOP)
+                       {
+                               longloopmessage();
+                               loopcount = -1;
+                       }
+#endif
+               }
+               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 GET_TIME
+                       if (loopcount >= 0 && ++loopcount > 100)
+                       {
+                               loopcount = 0;
+                               if (get_time() >= startime + LONGTIME)
+                               {
+                                       longloopmessage();
+                                       loopcount = -1;
+                               }
+                       }
+#else
+                       if (loopcount >= 0 && ++loopcount > LONGLOOP)
+                       {
+                               longloopmessage();
+                               loopcount = -1;
+                       }
+#endif
+               }
+               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).
+ */
+       public int
+currline(where)
+       int where;
+{
+       POSITION pos;
+
+       pos = position(where);
+       if (pos == NULL_POSITION)
+               pos = ch_length();
+       return (find_linenum(pos));
+}
+
+#if DEBUG_STUFF
+debug()
+{
+       register struct linenum *p;
+       char buf[20];
+
+       lower_left();
+       clear_eol();
+       for (p = anchor.next;  p != &anchor;  p = p->next)
+       {
+               sprintf(buf, "%d-%d ", p->line, p->pos);
+               putstr(buf);
+       }
+       putstr("\n");
+       error("DEBUG");
+}
+#endif /*DEBUG_STUFF*/
diff --git a/usr/src/usr.bin/more/linstall b/usr/src/usr.bin/more/linstall
new file mode 100644 (file)
index 0000000..d6932be
--- /dev/null
@@ -0,0 +1,755 @@
+#! /bin/sh
+# Installation script for less.
+# This script prompts the operator for various information
+# and constructs a makefile.
+
+echo "This script will build a makefile for less."
+echo "If you already have a file called \"makefile\" it will be overwritten."
+echo "Press RETURN to continue."
+read ans
+
+echo "I will ask you some questions about your system."
+echo "If you do not know the answer to any question,"
+echo "just press RETURN and I will choose a default for you."
+echo "Press RETURN now."
+read ans
+
+ECHO=./vecho
+if [ ! -x $ECHO ]
+then
+       echo "One moment..."
+       cc -o $ECHO vecho.c
+       echo ""
+fi
+
+$ECHO "Most Unix systems are derived from either System V"
+$ECHO "or Berkeley BSD 4.1, 4.2, 4.3, etc."
+$ECHO ""
+$ECHO "Is your system closest to:"
+$ECHO "  1. System V"
+$ECHO "  2. BSD 4.1"
+$ECHO "  3. BSD 4.2 or later"
+$ECHO "  4. Xenix"
+$ECHO "Enter a number, or just RETURN if you don't know: \c"
+read ans
+xenix=0
+case "X$ans" in
+X1) sys=sys5; sysname="System V" ;;
+X2) sys=bsd; bsd41=1; sysname="BSD 4.1" ;;
+X3) sys=bsd; bsd41=0; sysname="BSD 4.2" ;;
+X4) sys=sys5; xenix=1; sysname="Xenix" ;;
+*) sys=unknown ;;
+esac
+$ECHO ""
+
+DATE=`date`
+cat >makefile <<EOF
+# Makefile for "less"
+# Generated $DATE by $0.
+EOF
+
+cat >>makefile <<"EOF"
+#
+# Invoked as:
+#      make all
+#   or make install
+# Plain "make" is equivalent to "make all".
+#
+# If you add or delete functions, remake funcs.h by doing:
+#      make newfuncs
+# This depends on the coding convention of function headers looking like:
+#      " \t public <function-type> \n <function-name> ( ... ) "
+#
+# Also provided:
+#      make lint       # Runs "lint" on all the sources.
+#      make clean      # Removes "less" and the .o files.
+#      make clobber    # Pretty much the same as make "clean".
+
+SHELL = /bin/sh
+
+EOF
+
+cat >defines.h <<EOF
+/* Definition file for less */
+/* Generated $DATE by $0. */
+
+EOF
+
+cat >>defines.h <<EOF
+/*
+ * Define XENIX if running under XENIX 3.0.
+ */
+#define        XENIX           $xenix
+
+EOF
+$ECHO ""
+
+
+
+if [ "X$sys" = "Xunknown" ]
+then
+       alldefault=0
+else
+       def=yes
+       alldefault=1
+       $ECHO "Do you want to use ALL the defaults for $sysname?"
+       $ECHO "  Enter \"yes\" if you have a STANDARD $sysname."
+       $ECHO "  Enter \"no\" if you want to change any of the defaults. [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) alldefault=1 ;;
+       X[nN]*) alldefault=0 ;;
+       esac
+       $ECHO ""
+fi
+
+if [ $alldefault = 0 ]
+then
+       alloptional=0
+else
+       def=yes
+       alloptional=1
+       $ECHO "Do you want to use all the optional features of less?"
+       $ECHO "  Less has several features which you may or may not"
+       $ECHO "  wish to include, such as shell escapes."
+       $ECHO "  Enter \"yes\" if you want to include ALL the optional features."
+       $ECHO "  Enter \"no\" if you want to select individual features. [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) alloptional=1 ;;
+       X[nN]*) alloptional=0 ;;
+       esac
+       $ECHO ""
+fi
+
+
+
+def=yes
+x=1
+if [ $alldefault = 0 ]
+then
+       $ECHO "Does your C compiler support the \"void\" type? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * VOID is 1 if your C compiler supports the "void" type,
+ * 0 if it does not.
+ */
+#define        VOID            $x
+
+EOF
+
+
+
+def=long
+if [ $alldefault = 0 ]
+then
+       $ECHO "What type is the \"offset\" argument to lseek? [$def] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               def=$ans
+       fi
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * offset_t is the type which lseek() returns.
+ * It is also the type of lseek()'s second argument.
+ */
+#define        offset_t        $def
+
+EOF
+
+
+
+
+def=yes; x=1
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most Unix systems provide the stat() function."
+       $ECHO "Does your system have stat()? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * STAT is 1 if your system has the stat() call.
+ */
+#define        STAT            $x
+
+EOF
+
+
+
+
+def=yes; x=1
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most Unix systems provide the perror() function."
+       $ECHO "Does your system have perror()? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * PERROR is 1 if your system has the perror() call.
+ * (Actually, if it has sys_errlist, sys_nerr and errno.)
+ */
+#define        PERROR          $x
+
+EOF
+
+
+
+
+def=yes; x=1
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most Unix systems provide the time() function."
+       $ECHO "Does your system have time()? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * GET_TIME is 1 if your system has the time() call.
+ */
+#define        GET_TIME        $x
+
+EOF
+
+if [ $x = 0 ]
+then
+       $ECHO "What is the APPROXIMATE performance of your"
+       $ECHO "machine, as a percentage of a Vax 11/750?"
+       $ECHO "(Enter 100 if your machine is as fast as a Vax,"
+       $ECHO " 50 if it is half as fast, 200 if it is twice as fast, etc.)"
+       $ECHO "The accuracy of this information is not critical."
+       while :
+       do
+               $ECHO "Percent of Vax 11/750 [100]: \c"
+               read ans
+               if [ "X$ans" = "X" ]
+               then
+                       ans=100
+               fi
+               longloop=`expr "$ans" "*" 3`
+               if [ $? = 0 ]
+               then
+                       break
+               fi
+               $ECHO "Enter a number please!"
+       done
+       $ECHO ""
+
+       cat >>defines.h <<EOF
+/*
+ * LONGLOOP is the number of lines we should process in the line number
+ * scan before displaying a warning that it will take a while.
+ */
+#define        LONGLOOP        ($longloop)
+EOF
+fi
+
+
+
+
+if [ "$sys" = "bsd" ]
+then
+       def=no; x=0
+else
+       def=yes; x=1
+fi
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most System V systems have termio.h, while most"
+       $ECHO "Berkeley-derived systems have sgtty.h."
+       $ECHO "Does your system have termio.h? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * TERMIO is 1 if your system has /usr/include/termio.h.
+ * This is normally the case for System 5.
+ * If TERMIO is 0 your system must have /usr/include/sgtty.h.
+ * This is normally the case for BSD.
+ */
+#define        TERMIO          $x
+
+EOF
+
+
+
+
+if [ "$sys" = "bsd" -a "$bsd41" = "0" ]
+then
+       def=yes; x=1
+else
+       def=no; x=0
+fi
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most BSD 4.2 and 4.3 systems have the sigsetmask() call."
+       $ECHO "Most System V and BSD 4.1 systems do not."
+       $ECHO "Does your system have sigsetmask()? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * SIGSETMASK is 1 if your system has the sigsetmask() call.
+ * This is normally the case only for BSD 4.2,
+ * not for BSD 4.1 or System 5.
+ */
+#define        SIGSETMASK      $x
+
+EOF
+
+
+
+if [ "$sys" = "bsd" ]
+then
+       def=2; REGCMP=0;RECOMP=1
+else
+       def=1; REGCMP=1;RECOMP=0
+fi
+if [ $alldefault = 0 ]
+then
+       $ECHO "Most System V systems have the regcmp() function."
+       $ECHO "Most Berkeley-derived systems have the re_comp() function."
+       $ECHO "Does your system have:"
+       $ECHO "  1. regcmp"
+       $ECHO "  2. re_comp"
+       $ECHO "  3. neither   [$def] \c"
+       read ans
+       case "X$ans" in
+       X1) REGCMP=1;RECOMP=0 ;;
+       X2) REGCMP=0;RECOMP=1 ;;
+       X3) REGCMP=0;RECOMP=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * REGCMP is 1 if your system has the regcmp() function.
+ * This is normally the case for System 5.
+ * RECOMP is 1 if your system has the re_comp() function.
+ * This is normally the case for BSD.
+ * If neither is 1, pattern matching is supported, but without metacharacters.
+ */
+#define        REGCMP          $REGCMP
+#define        RECOMP          $RECOMP
+
+EOF
+
+
+
+
+def=yes
+x=1
+if [ $alloptional = 0 ]
+then
+       $ECHO "Do you wish to allow shell escapes? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
+ * (This is possible only if your system supplies the system() function.)
+ */
+#define        SHELL_ESCAPE    $x
+
+EOF
+
+
+
+def=yes
+x=1
+edname="vi"
+if [ $alloptional = 0 ]
+then
+       $ECHO "Do you wish to allow editor escapes? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[nN]*) x=0; edname="" ;;
+       X[yY]*) x=1
+               $ECHO "What is the pathname of the default editor? [$edname] \c"
+               read ans 
+               if [ "x$ans" != "x" ]
+               then
+                       edname=$ans
+               fi
+               ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
+ * (This is possible only if your system supplies the system() function.)
+ * EDIT_PGM is the name of the (default) editor to be invoked.
+ */
+#define        EDITOR          $x
+#define        EDIT_PGM        "$edname"
+
+EOF
+
+
+
+
+def=yes
+x=1
+if [ $alloptional = 0 ]
+then
+       $ECHO "Do you wish to support \"tag\" files? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * TAGS is 1 if you wish to support tag files.
+ */
+#define        TAGS            $x
+
+EOF
+
+
+
+def=yes
+x=1
+if [ $alloptional = 0 ]
+then
+       $ECHO "Do you wish to allow user-defined key definitions? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+USERFILE=$x
+cat >>defines.h <<EOF
+/*
+ * USERFILE is 1 if you wish to allow a .less file to specify 
+ * user-defined key bindings.
+ */
+#define        USERFILE        $x
+
+EOF
+
+
+
+def=yes
+x=1
+if [ $alldefault = 0 ]
+then
+       $ECHO "If your system provides the popen() function and"
+       $ECHO "the \"$ECHO\" shell command, you may allow shell metacharacters" 
+       $ECHO "to be expanded in filenames."
+       $ECHO "Do you wish to allow shell metacharacters in filenames? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
+ * This will generally work if your system provides the "popen" function
+ * and the "$ECHO" shell command.
+ */
+#define        GLOB            $x
+
+EOF
+
+
+
+def=yes
+x=1
+if [ $alloptional = 0 ]
+then
+       $ECHO "Do you wish to allow log files (-l option)? [$def] \c"
+       read ans
+       case "X$ans" in
+       X[yY]*) x=1 ;;
+       X[nN]*) x=0 ;;
+       esac
+       $ECHO ""
+fi
+cat >>defines.h <<EOF
+/*
+ * LOGFILE is 1 if you wish to allow the -l option (to create log files).
+ */
+#define        LOGFILE         $x
+
+EOF
+
+cat >>defines.h <<EOF
+/*
+ * ONLY_RETURN is 1 if you want RETURN to be the only input which
+ * will continue past an error message.
+ * Otherwise, any key will continue past an error message.
+ */
+#define        ONLY_RETURN     0
+
+EOF
+
+cat >>makefile <<EOF
+
+##########################################################################
+# Compilation environment.
+##########################################################################
+
+EOF
+
+
+
+if [ "$xenix" = "1" ]
+then
+       LIBS="-ltermlib"
+elif [ "$sys" = "bsd" ]
+then
+       LIBS="-ltermcap"
+else
+       LIBS="-lcurses -ltermcap -lPW"
+fi
+if [ $alldefault = 0 ]
+then
+       $ECHO "To build \"less\", you must link with libraries supplied by your system."
+       $ECHO "(If this needs to be changed later, edit the makefile"
+       $ECHO "and change the definition of LIBS.)"
+       $ECHO "What libraries should be used [$LIBS] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               LIBS="$ans"
+       fi
+       $ECHO ""
+fi
+cat >>makefile <<EOF
+# LIBS is the list of libraries needed.
+LIBS = $LIBS
+
+EOF
+
+
+
+INSTALL_LESS="/usr/local/bin/less"
+INSTALL_KEY="/usr/local/bin/lesskey"
+INSTALL_HELP="/usr/local/bin/less.help"
+INSTALL_LESSMAN="/usr/man/man1/less.1"
+INSTALL_KEYMAN="/usr/man/man1/lesskey.1"
+LESS_MANUAL="less.nro"
+KEY_MANUAL="lesskey.nro"
+if [ $alldefault = 0 ]
+then
+       $ECHO "What is the name of the \"public\" (installed) version of less?"
+       $ECHO " [$INSTALL_LESS] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               INSTALL_LESS="$ans"
+       fi
+       $ECHO "What is the name of the \"public\" (installed) version of lesskey?"
+       $ECHO " [$INSTALL_KEY] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               INSTALL_KEY="$ans"
+       fi
+       $ECHO "What is the name of the \"public\" (installed) version of the help file?"
+       $ECHO " [$INSTALL_HELP] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               INSTALL_HELP="$ans"
+       fi
+       $ECHO "What is the name of the \"public\" (installed) version of the less manual page?"
+       $ECHO " [$INSTALL_LESSMAN] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               INSTALL_LESSMAN="$ans"
+       fi
+       $ECHO "What is the name of the \"public\" (installed) version of the lesskey manual page?"
+       $ECHO " [$INSTALL_KEYMAN] \c"
+       read ans
+       if [ "X$ans" != "X" ]
+       then
+               INSTALL_KEYMAN="$ans"
+       fi
+       $ECHO ""
+fi
+cat >>makefile <<EOF
+# INSTALL_LESS is a list of the public versions of less.
+# INSTALL_KEY is a list of the public versions of lesskey.
+# INSTALL_HELP is a list of the public version of the help file.
+# INSTALL_LESSMAN is a list of the public versions of the less manual page.
+# INSTALL_KEYMAN is a list of the public versions of the lesskey manual page.
+INSTALL_LESS =         \$(ROOT)$INSTALL_LESS
+INSTALL_KEY =          \$(ROOT)$INSTALL_KEY
+INSTALL_HELP =         \$(ROOT)$INSTALL_HELP
+INSTALL_LESSMAN =      \$(ROOT)$INSTALL_LESSMAN
+INSTALL_KEYMAN =       \$(ROOT)$INSTALL_KEYMAN
+LESS_MANUAL =          $LESS_MANUAL
+KEY_MANUAL =           $KEY_MANUAL
+HELPFILE =             $INSTALL_HELP
+
+
+EOF
+
+
+
+cat >>makefile <<"EOF"
+# OPTIM is passed to the compiler and the loader.
+# It is normally "-O" but may be, for example, "-g".
+OPTIM = -O
+
+CFLAGS = $(OPTIM)
+
+
+
+##########################################################################
+# Files
+##########################################################################
+
+SRC1 = main.c option.c prim.c ch.c position.c input.c linenum.c
+SRC2 = screen.c prompt.c line.c signal.c os.c help.c ttyin.c command.c
+SRC3 = output.c decode.c tags.c version.c
+SRC =  $(SRC1) $(SRC2) $(SRC3)
+OBJ =  main.o option.o prim.o ch.o position.o input.o output.o \
+       screen.o prompt.o line.o signal.o os.o help.o ttyin.o \
+       decode.o command.o linenum.o tags.o version.o
+
+
+##########################################################################
+# Rules for building stuff
+##########################################################################
+
+EOF
+
+if [ "$USERFILE" = "1" ]
+then
+       cat >>makefile <<"EOF"
+all: less lesskey
+install: install_less install_help install_key install_lman install_kman
+EOF
+else
+       cat >>makefile <<"EOF"
+all: less
+install: install_less install_help install_lman
+EOF
+fi
+
+cat >>makefile <<"EOF"
+
+less: $(OBJ)
+       $(CC) $(LDFLAGS) $(OPTIM) -o less $(OBJ) $(LIBS) $(LDLIBS)
+
+lesskey: lesskey.o
+       $(CC) $(LDFLAGS) $(OPTIM) -o lesskey lesskey.o $(LDLIBS)
+
+# help.o depends on makefile for the definition of HELPFILE
+help.o: makefile
+       $(CC) $(CFLAGS) -c -DHELPFILE=\"$(HELPFILE)\" help.c
+
+install_less: less
+       for f in $(INSTALL_LESS); do  rm -f $$f; cp less $$f;  done
+       touch install_less
+
+install_key: lesskey
+       for f in $(INSTALL_KEY); do  rm -f $$f; cp lesskey $$f;  done
+       touch install_key
+
+install_help: less.help
+       for f in $(INSTALL_HELP); do  rm -f $$f; cp less.help $$f;  done
+       touch install_help
+
+install_lman: $(LESS_MANUAL) 
+       for f in $(INSTALL_LESSMAN); do  rm -f $$f; cp $(LESS_MANUAL) $$f;  done
+       touch install_lman
+
+install_kman: $(KEY_MANUAL)
+       for f in $(INSTALL_KEYMAN); do  rm -f $$f; cp $(KEY_MANUAL) $$f;  done
+       touch install_kman
+
+##########################################################################
+# Maintenance
+##########################################################################
+
+lint:
+       lint -hp $(SRC)
+
+newfuncs:
+       mv funcs.h funcs.h.OLD
+       awk -f mkfuncs.awk $(SRC) >funcs.h
+
+clean:
+       rm -f $(OBJ) lesskey.o less lesskey vecho
+
+clobber:
+       rm -f *.o less lesskey vecho install_less install_key \
+               install_help install_lman install_kman
+
+shar:
+       shar -v linstall less.h position.h funcs.h cmd.h \
+               vecho.c lesskey.c less.nro lesskey.nro lesskey.man > less.shar.a
+       shar -v $(SRC1) > less.shar.b
+       shar -v $(SRC2) > less.shar.c
+       shar -v $(SRC3) less.man README less.help *.awk >less.shar.d
+
+
+##########################################################################
+# Dependencies
+##########################################################################
+
+$(OBJ): less.h funcs.h defines.h position.h
+command.o decode.o: cmd.h
+lesskey.o: less.h funcs.h defines.h cmd.h
+
+EOF
+$ECHO ""
+
+$ECHO "The makefile has been built."
+$ECHO "You should check it to make sure everything is as you want it to be."
+$ECHO "When you are satisfied with the makefile, just type \"make\""
+$ECHO "and \"less\" will be built."
diff --git a/usr/src/usr.bin/more/main.c b/usr/src/usr.bin/more/main.c
new file mode 100644 (file)
index 0000000..9c0b247
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#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.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Entry point, initialization, miscellaneous routines.
+ */
+
+#include "less.h"
+#include "position.h"
+
+public int     ispipe;
+public char *  first_cmd;
+public char *  every_first_cmd;
+public int     new_file;
+public int     is_tty;
+public char    *current_file;
+public char    *previous_file;
+public POSITION        prev_pos;
+public int     any_display;
+public int     scroll;
+public int     ac;
+public char ** av;
+public int     curr_ac;
+public int     quitting;
+
+extern int     file;
+extern int     quit_at_eof;
+extern int     cbufs;
+extern int     errmsgs;
+
+#if LOGFILE
+public int     logfile = -1;
+public int     force_logfile = 0;
+public char *  namelogfile = NULL;
+#endif
+
+#if EDITOR
+public char *  editor;
+#endif
+
+#if TAGS
+extern char *  tagfile;
+extern char *  tagpattern;
+extern int     tagoption;
+#endif
+
+
+/*
+ * Edit a new file.
+ * Filename "-" means standard input.
+ * No filename means the "current" file, from the command line.
+ */
+       public void
+edit(filename)
+       register char *filename;
+{
+       register int f;
+       register char *m;
+       POSITION initial_pos;
+       char message[100];
+       static int didpipe;
+
+       initial_pos = NULL_POSITION;
+       if (filename == NULL || *filename == '\0')
+       {
+               if (curr_ac >= ac)
+               {
+                       error("No current file");
+                       return;
+               }
+               filename = save(av[curr_ac]);
+       } else if (strcmp(filename, "#") == 0)
+       {
+               if (*previous_file == '\0')
+               {
+                       error("no previous file");
+                       return;
+               }
+               filename = save(previous_file);
+               initial_pos = prev_pos;
+       } else
+               filename = save(filename);
+
+       if (strcmp(filename, "-") == 0)
+       {
+               /* 
+                * Use standard input.
+                */
+               if (didpipe)
+               {
+                       error("Can view standard input only once");
+                       return;
+               }
+               f = 0;
+       } else if ((m = bad_file(filename, message, sizeof(message))) != NULL)
+       {
+               error(m);
+               free(filename);
+               return;
+       } else if ((f = open(filename, 0)) < 0)
+       {
+               error(errno_message(filename, message, sizeof(message)));
+               free(filename);
+               return;
+       }
+
+       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)
+                       close(f);
+               free(filename);
+               return;
+       }
+
+#if LOGFILE
+       if (f == 0 && namelogfile != NULL && is_tty)
+               use_logfile();
+#endif
+
+       /*
+        * 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)
+               close(file);
+       new_file = 1;
+       if (previous_file != NULL)
+               free(previous_file);
+       previous_file = current_file;
+       current_file = filename;
+       prev_pos = position(TOP);
+       ispipe = (f == 0);
+       if (ispipe)
+               didpipe = 1;
+       file = f;
+       ch_init(cbufs, 0);
+       init_mark();
+
+       if (every_first_cmd != NULL)
+               first_cmd = every_first_cmd;
+
+       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.
+                */
+               pos_clear();
+               if (initial_pos != NULL_POSITION)
+                       jump_loc(initial_pos);
+               clr_linenum();
+       }
+}
+
+/*
+ * Edit the next file in the command line list.
+ */
+       public void
+next_file(n)
+       int n;
+{
+       if (curr_ac + n >= ac)
+       {
+               if (quit_at_eof)
+                       quit();
+               error("No (N-th) next file");
+       } else
+               edit(av[curr_ac += n]);
+}
+
+/*
+ * Edit the previous file in the command line list.
+ */
+       public void
+prev_file(n)
+       int n;
+{
+       if (curr_ac - n < 0)
+               error("No (N-th) previous file");
+       else
+               edit(av[curr_ac -= n]);
+}
+
+/*
+ * Copy a file directly to standard output.
+ * Used if standard output is not a tty.
+ */
+       static void
+cat_file()
+{
+       register int c;
+
+       while ((c = ch_forw_get()) != EOI)
+               putchr(c);
+       flush();
+}
+
+#if LOGFILE
+
+use_logfile()
+{
+       int exists;
+       int answer;
+       char message[100];
+
+       /*
+        * If he asked for a log file and we have opened standard input,
+        * create the log file.  
+        * We take care not to blindly overwrite an existing file.
+        */
+       end_logfile();
+
+       /*
+        * {{ We could use access() here. }}
+        */
+       exists = open(namelogfile, 0);
+       close(exists);
+       exists = (exists >= 0);
+
+       if (exists && !force_logfile)
+       {
+               static char w[] = "WARNING: log file exists: ";
+               strcpy(message, w);
+               strtcpy(message+sizeof(w)-1, namelogfile,
+                       sizeof(message)-sizeof(w));
+               error(message);
+               answer = 'X';   /* Ask the user what to do */
+       } else
+               answer = 'O';   /* Create the log file */
+
+loop:
+       switch (answer)
+       {
+       case 'O': case 'o':
+               logfile = creat(namelogfile, 0644);
+               break;
+       case 'A': case 'a':
+               logfile = open(namelogfile, 1);
+               if (lseek(logfile, (offset_t)0, 2) < 0)
+               {
+                       close(logfile);
+                       logfile = -1;
+               }
+               break;
+       case 'D': case 'd':
+               answer = 0;     /* Don't print an error message */
+               break;
+       case 'q':
+               quit();
+       default:
+               putstr("\n  Overwrite, Append, or Don't log? ");
+               answer = getchr();
+               putstr("\n");
+               flush();
+               goto loop;
+       }
+
+       if (logfile < 0 && answer != 0)
+       {
+               sprintf(message, "Cannot write to \"%s\"", 
+                       namelogfile);
+               error(message);
+       }
+}
+
+/*
+ * Entry point.
+ */
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *getenv();
+
+
+       /*
+        * Process command line arguments and LESS environment arguments.
+        * Command line arguments override environment arguments.
+        */
+       init_prompt();
+       init_option();
+       scan_option(getenv("LESS"));
+       argv++;
+       while ( (--argc > 0) && 
+               (argv[0][0] == '-' || argv[0][0] == '+') && 
+               argv[0][1] != '\0')
+               scan_option(*argv++);
+
+#if EDITOR
+       editor = getenv("EDITOR");
+       if (editor == NULL || *editor == '\0')
+               editor = EDIT_PGM;
+#endif
+
+       /*
+        * 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)
+               {
+                       edit("-");
+                       cat_file();
+               } else
+               {
+                       do
+                       {
+                               edit((char *)NULL);
+                               if (file >= 0)
+                                       cat_file();
+                       } while (++curr_ac < ac);
+               }
+               exit(0);
+       }
+
+       raw_mode(1);
+       get_term();
+       open_getchr();
+       init();
+       init_cmd();
+
+       init_signals(1);
+
+       /*
+        * Select the first file to examine.
+        */
+#if TAGS
+       if (tagoption)
+       {
+               /*
+                * A -t option was given.
+                * Verify that no filenames were also given.
+                * Edit the file selected by the "tags" search,
+                * and search for the proper line in the file.
+                */
+               if (ac > 0)
+               {
+                       error("No filenames allowed with -t option");
+                       quit();
+               }
+               if (tagfile == NULL)
+                       quit();
+               edit(tagfile);
+               if (file < 0)
+                       quit();
+               if (tagsearch())
+                       quit();
+       } else
+#endif
+       if (ac < 1)
+               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
+               {
+                       edit((char *)NULL);
+               } while (file < 0 && ++curr_ac < ac);
+       }
+
+       if (file >= 0)
+               commands();
+       quit();
+       /*NOTREACHED*/
+}
+
+/*
+ * Copy a string, truncating to the specified length if necessary.
+ * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
+ */
+       public void
+strtcpy(to, from, len)
+       char *to;
+       char *from;
+       unsigned int len;
+{
+       strncpy(to, from, len);
+       to[len-1] = '\0';
+}
+
+/*
+ * Copy a string to a "safe" place
+ * (that is, to a buffer allocated by calloc).
+ */
+       public char *
+save(s)
+       char *s;
+{
+       register char *p;
+
+       p = calloc(strlen(s)+1, sizeof(char));
+       if (p == NULL)
+       {
+               error("cannot allocate memory");
+               quit();
+       }
+       strcpy(p, s);
+       return (p);
+}
+
+/*
+ * Exit the program.
+ */
+       public void
+quit()
+{
+       /*
+        * Put cursor at bottom left corner, clear the line,
+        * reset the terminal modes, and exit.
+        */
+       quitting = 1;
+#if LOGFILE
+       end_logfile();
+#endif
+       lower_left();
+       clear_eol();
+       deinit();
+       flush();
+       raw_mode(0);
+       exit(0);
+}
diff --git a/usr/src/usr.bin/more/mkfuncs.awk b/usr/src/usr.bin/more/mkfuncs.awk
new file mode 100644 (file)
index 0000000..dea28ac
--- /dev/null
@@ -0,0 +1,9 @@
+BEGIN { FS="("; state = 0 }
+
+/^     public/ { ftype = $0; state = 1 }
+
+{ if (state == 1)
+       state = 2
+  else if (state == 2)
+       { print ftype,$1,"();"; state = 0 }
+}
diff --git a/usr/src/usr.bin/more/more.1 b/usr/src/usr.bin/more/more.1
new file mode 100644 (file)
index 0000000..d501c4e
--- /dev/null
@@ -0,0 +1,612 @@
+.\"
+.\" Copyright (c) 1988 Mark Nudleman
+.\" Copyright (c) 1988 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Mark Nudleman.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley.  The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\"    @(#)more.1      5.1 (Berkeley) %G%
+.\"
+.TH LESS 1
+.SH NAME
+less \- opposite of more
+.SH SYNOPSIS
+.B "less [-[+]aABcCdeEimMnqQuUsw] [-b\fIN\fB] [-h\fIN\fB] [-x\fIN\fB] [-[z]\fIN\fB]"
+.br
+.B "     [-P[mM=]\fIstring\fB] [-[lL]\fIlogfile\fB] [+\fIcmd\fB]"
+.br
+.B "     [-t\fItag\fB] [\fIfilename\fB]..."
+.SH DESCRIPTION
+.I Less
+is a program similar to 
+.I more
+(1), but which allows backwards movement
+in the file as well as forward movement.
+Also,
+.I less
+does not have to read the entire input file before starting,
+so with large input files it starts up faster than text editors like
+.I vi
+(1).
+.I Less
+uses termcap (or terminfo on some systems),
+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.)
+.PP
+Commands are based on both
+.I more
+and
+.I vi.
+Commands may be preceeded by a decimal number, 
+called N in the descriptions below.
+The number is used by some commands, as indicated.
+
+.SH COMMANDS
+In the following descriptions, ^X means control-X.
+ESC stands for the ESCAPE key; for example ESC-v means the
+two character sequence "ESCAPE", then "v".
+.IP H
+Help: display a summary of these commands.
+If you forget all the other commands, remember this one.
+.PP
+.IP "SPACE or f or ^F or ^V"
+Scroll forward N lines, default one window (see option -z below).
+If N is more than the screen size, only the final screenful is displayed.
+Warning: some systems use ^V as a special literalization character.
+.PP
+.IP "b or ^B or ESC-v"
+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.
+.PP
+.IP "RETURN or ^N or e or ^E or j or ^J"
+Scroll forward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.PP
+.IP "y or ^Y or ^P or k or ^K"
+Scroll backward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+Warning: some systems use ^Y as a special job control character.
+.PP
+.IP "d or ^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.
+.PP
+.IP "u or ^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.
+.PP
+.IP "r or ^R or ^L"
+Repaint the screen.
+.PP
+.IP R
+Repaint the screen, discarding any buffered input.
+Useful if the file is changing while it is being viewed.
+.PP
+.IP "g or < or ESC-<"
+Go to line N in the file, default 1 (beginning of file).
+(Warning: this may be slow if N is large.)
+.PP
+.IP "G or > or ESC->"
+Go to line N in the file, default the end of the file.
+(Warning: this may be slow if N is large,
+or if N is not specified and
+standard input, rather than a file, is being read.)
+.PP
+.IP "p or %"
+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
+.I less
+has already read to the end of the file.
+It is always fast, but not always useful.)
+.PP
+.IP m
+Followed by any lowercase letter, 
+marks the current position with that letter.
+.PP
+.IP "'"
+(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.
+All marks are lost when a new file is examined.
+.PP
+.IP "^X^X"
+Same as single quote.
+.PP
+.IP /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
+.I ed.
+The search starts at the second line displayed
+(but see the -a option, which changes this).
+.PP
+.IP ?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.
+.PP
+.IP /!pattern
+Like /, but the search is for the N-th line
+which does NOT contain the pattern.
+.PP
+.IP ?!pattern
+Like ?, but the search is for the N-th line
+which does NOT contain the pattern.
+.PP
+.IP n
+Repeat previous search, for N-th line containing the last pattern
+(or NOT containing the last pattern, if the previous search
+was /! or ?!).
+.PP
+.IP "E [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.
+.PP
+.IP "^X^V or :e"
+Same as E.
+Warning: some systems use ^V as a special literalization character.
+.PP
+.IP "N or :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.
+.PP
+.IP "P or :p"
+Examine the previous file.
+If a number N is specified, the N-th previous file is examined.
+.PP
+.IP "= or ^G"
+Prints some information about the file being viewed,
+including its name
+and the line number and byte offset of the bottom line being displayed.
+If possible, it also prints the length of the file
+and the percent of the file above the last displayed line.
+.PP
+.IP \-
+Followed by one of the command line option letters (see below),
+this will change the setting of that option
+and print a message describing the new setting.
+If the option letter has a numeric value (such as -b or -h),
+or a string value (such as -P or -t),
+a new value may be entered after the option letter.
+.PP
+.IP \_
+(Underscore.)
+Followed by one of the command line option letters (see below),
+this will print a message describing the current setting of that option.
+The setting of the option is not changed.
+.PP
+.IP +cmd
+Causes the specified cmd to be executed each time a new file is examined.
+For example, +G causes 
+.I less
+to initially display each file starting at the end 
+rather than the beginning.
+.PP
+.IP V
+Prints the version number of 
+.I less 
+being run.
+.PP
+.IP "q or :q or ZZ"
+Exits
+.I less.
+.PP
+The following 
+two 
+commands may or may not be valid, depending on your particular installation.
+.PP
+.IP v
+Invokes an editor to edit the current file being viewed.
+The editor is taken from the environment variable EDITOR,
+or defaults to "vi".
+.PP
+.IP "! shell-command"
+Invokes a shell to run the shell-command given.
+A percent sign in the command is replaced by the name of the
+current file.  "!!" repeats the last shell command.
+"!" with no shell command simply invokes a shell.
+In all cases, the shell is taken from the environment variable SHELL,
+or defaults to "sh".
+.PP
+.SH OPTIONS
+Command line options are described below.
+Most options may be changed while
+.I less 
+is running, via the "\-" command.
+.PP
+Options are also taken from the environment variable "LESS".
+For example, 
+to avoid typing "less -options ..." each time 
+.I less 
+is invoked, you might tell 
+.I csh:
+.sp
+setenv LESS "-options"
+.sp
+or if you use 
+.I sh:
+.sp
+LESS="-options"; export LESS
+.sp
+The environment variable is parsed before the command line,
+so command line options override the LESS environment variable.
+If an option appears in the LESS variable, it can be reset
+to its default on the command line by beginning the command
+line option with "-+".
+.sp
+A dollar sign ($) may be used to signal the end of an option string.
+This is important only for options like -P which take a
+following string.
+.IP -a
+Normally, forward searches start just after
+the top displayed line (that is, at the second displayed line).
+Thus, forward searches include the currently displayed screen.
+The -a option causes forward searches to start 
+just after the bottom line displayed,
+thus skipping the currently displayed screen.
+.IP -A
+The -A option causes searches to start at the second SCREEN line
+displayed, as opposed to the default which is to start at the second
+REAL line displayed.
+For example, suppose a long real line occupies the first three screen lines.
+The default search will start at the second real line (the fourth
+screen line), while the -A option
+will cause the search to start at the second screen line (in the
+midst of the first real line).
+(This option is rarely useful.)
+.IP -b
+The -b\fIn\fR option tells
+.I less
+to use a non-standard number of buffers.
+Buffers are 1K, and normally 10 buffers are used
+(except if data in coming from standard input; see the -B option).
+The number \fIn\fR specifies a different number of buffers to use.
+.IP -B
+Normally, when data is coming from standard input,
+buffers are allocated automatically as needed, to avoid loss of data.
+The -B option disables this feature, so that only the default number
+of buffers are used.
+If more data is read than will fit in the buffers, the oldest
+data is discarded.
+.IP -c
+Normally, 
+.I less 
+will repaint the screen by scrolling from the bottom of the screen.
+If the -c option is set, when
+.I less 
+needs to change the entire display, it will paint from the top line down.
+.IP -C
+The -C option is like -c, but the screen is cleared before it is repainted.
+.IP -d
+Normally,
+.I less
+will complain if the terminal is dumb; that is, lacks some important capability,
+such as the ability to clear the screen or scroll backwards.
+The -d option suppresses this complaint 
+(but does not otherwise change the behavior of the program on a dumb terminal).
+.IP -e
+Normally the only way to exit less is via the "q" command.
+The -e option tells less to automatically exit
+the second time it reaches end-of-file.
+.IP -E
+The -E flag causes less to exit the first time it reaches end-of-file.
+.IP -h
+Normally,
+.I less
+will scroll backwards when backwards movement is necessary.
+The -h option specifies a maximum number of lines to scroll backwards.
+If it is necessary to move backwards more than this many lines,
+the screen is repainted in a forward direction.
+(If the terminal does not have the ability to scroll
+backwards, -h0 is implied.)
+.IP -i
+The -i option causes searches to ignore case; that is,
+uppercase and lowercase are considered identical.
+Also, text which is overstruck or underlined can be searched for.
+.IP -l
+The -l option, followed immediately by a filename,
+will cause 
+.I less
+to copy its input to the named file as it is being viewed.
+This applies only when the input file is a pipe,
+not an ordinary file.
+If the file already exists, less will ask for confirmation before
+overwriting it.
+.IP -L
+The -L option is like -l, but it will overwrite an existing
+file without asking for confirmation.
+.sp
+If no log file has been specified,
+the -l and -L options can be used from within less to specify a log file.
+Without a file name, they will simply report the name of the log file.
+.IP -m
+Normally,
+.I less
+prompts with a colon.
+The -m option causes 
+.I less
+to prompt verbosely (like 
+.I more),
+with the percent into the file.
+.IP -M
+The -M option causes 
+.I less
+to prompt even more verbosely than 
+.I more.
+.IP -n
+The -n flag suppresses line numbers.
+The default (to use line numbers) may cause
+.I less
+to run more slowly in some cases, especially with a very large input file.
+Suppressing line numbers with the -n flag will avoid this problem.
+Using line numbers means: the line number will be displayed in the verbose
+prompt and in the = command,
+and the v command will pass the current line number to the editor.
+.IP -P
+The -P option provides a way to tailor the three prompt
+styles to your own preference.
+You would normally put this option in your LESS environment
+variable, rather than type it in with each less command.
+Such an option must either be the last option in the LESS variable,
+or be terminated by a dollar sign.
+-P followed by a string changes the default (short) prompt to that string.
+-Pm changes the medium (-m) prompt to the string, and
+-PM changes the long (-M) prompt.
+Also, -P= changes the message printed by the = command to the given string.
+All prompt strings consist of a sequence of 
+letters and special escape sequences.
+See the section on PROMPTS for more details.
+.IP -q
+Normally, if an attempt is made to scroll past the end of the file
+or before the beginning of the file, the terminal bell is rung to
+indicate this fact.
+The -q option tells
+.I less
+not to ring the bell at such times.
+If the terminal has a "visual bell", it is used instead.
+.IP -Q
+Even if -q is given, 
+.I less 
+will ring the bell on certain other errors,
+such as typing an invalid character.
+The -Q option tells
+.I less
+to be quiet all the time; that is, never ring the terminal bell.
+If the terminal has a "visual bell", it is used instead.
+.IP -s
+The -s option causes
+consecutive blank lines to be squeezed into a single blank line.
+This is useful when viewing
+.I nroff
+output.
+.IP -t
+The -t option, followed immediately by a TAG,
+will edit the file containing that tag.
+For this to work, there must be a file called "tags" in the
+current directory, which was previously built by the 
+.I ctags
+(1) command.
+This option may also be specified from within less 
+(using the \- command) as a way of examining a new file.
+.IP -u
+If the -u option is given, 
+backspaces are treated as printable characters;
+that is, they are sent to the terminal when they appear in the input.
+.IP -U
+If the -U option is given,
+backspaces are printed as the two character sequence "^H".
+.sp
+If neither -u nor -U is given,
+backspaces which appear adjacent to an underscore character
+are treated specially:
+the underlined text is displayed 
+using the terminal's hardware underlining capability.
+Also, backspaces which appear between two identical characters
+are treated specially: 
+the overstruck text is printed 
+using the terminal's hardware boldface capability.
+Other backspaces are deleted, along with the preceeding character.
+.IP -w
+Normally,
+.I less
+uses a tilde character to represent lines past the end of the file.
+The -w option causes blank lines to be used instead.
+.IP -x
+The -x\fIn\fR option sets tab stops every \fIn\fR positions.
+The default for \fIn\fR is 8.
+.IP -[z]
+When given a backwards or forwards window command,
+.I less
+will by
+default scroll backwards or forwards one screenful of lines. 
+The -z\fIn\fR option changes the default scrolling window size 
+to \fIn\fR lines.
+Note that the "z" is optional for compatibility with
+.I more.
+.IP +
+If a command line option begins with \fB+\fR,
+the remainder of that option is taken to be an initial command to
+.I less.
+For example, +G tells
+.I less
+to start at the end of the file rather than the beginning,
+and +/xyz tells it to start at the first occurence of "xyz" in the file.
+As a special case, +<number> acts like +<number>g; 
+that is, it starts the display at the specified line number
+(however, see the caveat under the "g" command above).
+If the option starts with \fB++\fR, the initial command applies to
+every file being viewed, not just the first one.
+The + command described previously
+may also be used to set (or change) an initial command for every file.
+
+.SH "KEY BINDINGS"
+You may define your own less commands by using the program 
+.I lesskey
+(1)
+to create a file called ".less" in your home directory.
+This file specifies a set of command keys and an action
+associated with each key.
+See the
+.I lesskey
+manual page for more details.
+
+.SH "PROMPTS"
+The -P option allows you to tailor the prompt to your preference.
+The string given to the -P option replaces the specified prompt string.
+Certain characters in the string are interpreted specially.
+The prompt mechanism is rather complicated to provide flexibility,
+but the ordinary user need not understand the details of constructing
+personalized prompt strings.
+.sp
+A percent sign followed by a single character is expanded
+according to what the following character is:
+.IP "%bX"
+Replaced by the byte offset into the current input file.
+The b is followed by a single character (shown as X above)
+which specifies the line whose byte offset is to be used.
+If the character is a "t", the byte offset of the top line in the
+display is used,
+an "m" means use the middle line,
+a "b" means use the bottom line,
+and a "B" means use the line just after the bottom line.
+.IP "%f"
+Replaced by the name of the current input file.
+.IP "%i"
+Replaced by the index of the current file in the list of
+input files.
+.IP "%lX"
+Replaced by the line number of a line in the input file.
+The line to be used is determined by the X, as with the %b option.
+.IP "%m"
+Replaced by the total number of input files.
+.IP "%pX"
+Replaced by the percent into the current input file.
+The line used is determined by the X as with the %b option.
+.IP "%s"
+Replaced by the size of the current input file.
+.IP "%t"
+Causes any trailing spaces to be removed.
+Usually used at the end of the string, but may appear anywhere.
+.IP "%x"
+Replaced by the name of the next input file in the list.
+.PP
+If any item is unknown (for example, the file size if input
+is a pipe), a question mark is printed instead.
+.PP
+The format of the prompt string can be changed
+depending on certain conditions.
+A question mark followed by a single character acts like an "IF":
+depending on the following character, a condition is evaluated.
+If the condition is true, any characters following the question mark
+and condition character, up to a period, are included in the prompt.
+If the condition is false, such characters are not included.
+A colon appearing between the question mark and the
+period can be used to establish an "ELSE": any characters between
+the colon and the period are included in the string if and only if
+the IF condition is false.
+Condition characters (which follow a question mark) may be:
+.IP "?a"
+True if any characters have been included in the prompt so far.
+.IP "?bX"
+True if the byte offset of the specified line is known.
+.IP "?e"
+True if at end-of-file.
+.IP "?f"
+True if there is an input filename
+(that is, if input is not a pipe).
+.IP "?lX"
+True if the line number of the specified line is known.
+.IP "?m"
+True if there is more than one input file.
+.IP "?n"
+True if this is the first prompt in a new input file.
+.IP "?pX"
+True if the percent into the current input file
+of the specified line is known.
+.IP "?s"
+True if the size of current input file is known.
+.IP "?x"
+True if there is a next input file
+(that is, if the current input file is not the last one).
+.PP
+Any characters other than the special ones
+(question mark, colon, period, percent, and backslash)
+become literally part of the prompt.
+Any of the special characters may be included in the prompt literally
+by preceeding it with a backslash.
+.PP
+Some examples:
+.sp
+?f%f:Standard input.
+.sp
+This prompt prints the filename, if known;
+otherwise the string "Standard input".
+.sp
+?f%f .?ltLine %lt:?pt%pt\%:?btByte %bt:-...
+.sp
+This prompt would print the filename, if known.
+The filename is followed by the line number, if known,
+otherwise the percent if known, otherwise the byte offset if known.
+Otherwise, a dash is printed.
+Notice how each question mark has a matching period,
+and how the % after the %pt
+is included literally by escaping it with a backslash.
+.sp
+?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x..%t
+.sp
+This prints the filename if this is the first prompt in a file,
+followed by the "file N of N" message if there is more
+than one input file.
+Then, if we are at end-of-file, the string "(END)" is printed
+followed by the name of the next file, if there is one.
+Finally, any trailing spaces are truncated.
+This is the default prompt.
+For reference, here are the defaults for
+the other two prompts (-m and -M respectively).
+Each is broken into two lines here for readability only.
+.nf
+.sp
+?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x.:
+       ?pB%pB\\%:byte\ %bB?s/%s...%t
+.sp
+?f%f\ .?n?m(file\ %i\ of\ %m)\ ..?ltline\ %lt\ :byte\ %bB?s/%s\ ..
+       ?e(END)\ ?x-\ Next\\:\ %x.:?pB%pB\\%..%t
+.sp
+.fi
+And here is the default message produced by the = command:
+.nf
+.sp
+?f%f\ .?m(file\ %i\ of\ %m)\ .?ltline\ %lt\ .
+       byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\\%..%t
+.fi
+
+.SH "SEE ALSO"
+lesskey(1)
+
+.SH WARNINGS
+The = command and prompts (unless changed by -P)
+report the line number of the line at the top of the screen,
+but the byte and percent of the line at the bottom of the screen.
diff --git a/usr/src/usr.bin/more/more.help b/usr/src/usr.bin/more/more.help
new file mode 100644 (file)
index 0000000..5b54509
--- /dev/null
@@ -0,0 +1,37 @@
+      Commands marked with * may be preceeded by a number, N.
+
+  H              Display this help.
+  q              Exit.
+
+  f, SPACE    *  Forward  N lines, default one screen.
+  b           *  Backward N lines, default one screen.
+  e, j, CR    *  Forward  N lines, default 1 line.
+  y, k        *  Backward N lines, default 1 line.
+  d           *  Forward  N lines, default half screen or last N to d/u.
+  u           *  Backward N lines, default half screen or last N to d/u.
+  r              Repaint screen.
+  R              Repaint screen, discarding buffered input.
+
+  /pattern    *  Search forward for N-th line containing the pattern.
+  ?pattern    *  Search backward for N-th line containing the pattern.
+  n           *  Repeat previous search (for N-th occurence).
+
+  g           *  Go to line N, default 1.
+  G           *  Like g, but default is last line in file.
+  p, %        *  Position to N percent into the file.
+  m<letter>      Mark the current position with <letter>.
+  '<letter>      Return to a previously marked position.
+  ''             Return to previous position.
+
+  E [file]       Examine a new file.
+  N           *  Examine the next file (from the command line).
+  P           *  Examine the previous file (from the command line).
+  =              Print current file name.
+  V              Print version number of "less".
+
+  -<flag>        Toggle a command line flag.
+  _<flag>        Display the setting of a command line flag.
+  +cmd           Execute the less cmd each time a new file is examined.
+
+  !command       Passes the command to the system to be executed (by $SHELL).
+  v              Edit the current file (with $EDITOR).
diff --git a/usr/src/usr.bin/more/option.c b/usr/src/usr.bin/more/option.c
new file mode 100644 (file)
index 0000000..0f48787
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)option.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Process command line options.
+ * Each option is a single letter which controls a program variable.
+ * The options have defaults which may be changed via
+ * the command line option, or toggled via the "-" command.
+ */
+
+#include "less.h"
+
+#define        toupper(c)      ((c)-'a'+'A')
+
+#define        END_OPTION_STRING       ('$')
+
+/*
+ * Types of options.
+ */
+#define        BOOL            01      /* Boolean option: 0 or 1 */
+#define        TRIPLE          02      /* Triple-valued option: 0, 1 or 2 */
+#define        NUMBER          04      /* Numeric option */
+#define        REPAINT         040     /* Repaint screen after toggling option */
+#define        NO_TOGGLE       0100    /* Option cannot be toggled with "-" cmd */
+
+/*
+ * Variables controlled by command line options.
+ */
+public int clean_data;         /* Can we assume the data is "clean"? 
+                                  (That is, free of nulls, etc) */
+public int quiet;              /* Should we suppress the audible bell? */
+public int how_search;         /* Where should forward searches start? */
+public int top_scroll;         /* Repaint screen from top?
+                                  (alternative is scroll from bottom) */
+public int pr_type;            /* Type of prompt (short, medium, long) */
+public int bs_mode;            /* How to process backspaces */
+public int know_dumb;          /* Don't complain about dumb terminals */
+public int quit_at_eof;                /* Quit after hitting end of file twice */
+public int squeeze;            /* Squeeze multiple blank lines into one */
+public int tabstop;            /* Tab settings */
+public int back_scroll;                /* Repaint screen on backwards movement */
+public int twiddle;            /* Display "~" for lines after EOF */
+public int caseless;           /* Do "caseless" searches */
+public int linenums;           /* Use line numbers */
+public int cbufs;              /* Current number of buffers */
+public int autobuf;
+public int plusoption;
+
+extern char *prproto[];
+extern char *eqproto;
+extern int nbufs;
+extern int sc_window;
+extern int ispipe;
+extern char *first_cmd;
+extern char *every_first_cmd;
+#if LOGFILE
+extern char *namelogfile;
+extern int force_logfile;
+extern int logfile;
+#endif
+#if TAGS
+extern char *tagfile;
+extern char *tagpattern;
+public int tagoption = 0;
+#endif
+
+static char *opt_P();
+
+static struct option
+{
+       char oletter;           /* The controlling letter (a-z) */
+       char otype;             /* Type of the option */
+       int odefault;           /* Default value */
+       int *ovar;              /* Pointer to the associated variable */
+       char *odesc[3];         /* Description of each value */
+} option[] =
+{
+       { 'a', TRIPLE, 0, &how_search,
+               { "Forward search starts at second REAL line displayed",
+                 "Forward search starts at bottom of screen",
+                 "Forward search starts at second SCREEN line displayed"
+               }
+       },
+       { 'b', NUMBER, 10, &cbufs,
+               { "%d buffers",
+                 NULL, NULL
+               }
+       },
+       { 'B', BOOL, 1, &autobuf,
+               { "Don't automatically allocate buffers",
+                 "Automatically allocate buffers when needed",
+                 NULL
+               }
+       },
+       { 'c', TRIPLE, 0, &top_scroll,
+               { "Repaint by scrolling from bottom of screen",
+                 "Repaint by clearing each line",
+                 "Repaint by painting from top of screen"
+               }
+       },
+       { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
+               { NULL, NULL, NULL}
+       },
+       { 'e', TRIPLE, 0, &quit_at_eof,
+               { "Don't quit at end-of-file",
+                 "Quit at end-of-file",
+                 "Quit immediately at end-of-file"
+               }
+       },
+       { 'h', NUMBER, -1, &back_scroll,
+               { "Backwards scroll limit is %d lines",
+                 NULL, NULL
+               }
+       },
+       { 'i', BOOL, 0, &caseless,
+               { "Case is significant in searches",
+                 "Ignore case in searches",
+                 NULL
+               }
+       },
+       { 'm', TRIPLE, 0, &pr_type,
+               { "Short prompt",
+                 "Medium prompt",
+                 "Long prompt"
+               }
+       },
+       { 'n', BOOL, 1, &linenums,
+               { "Don't use line numbers",
+                 "Use line numbers",
+                 NULL
+               }
+       },
+       { 'q', TRIPLE, 0, &quiet,
+               { "Ring the bell for errors AND at eof/bof",
+                 "Ring the bell for errors but not at eof/bof",
+                 "Never ring the bell"
+               }
+       },
+       { 's', BOOL|REPAINT, 0, &squeeze,
+               { "Don't squeeze multiple blank lines",
+                 "Squeeze multiple blank lines",
+                 NULL
+               }
+       },
+       { 'u', TRIPLE|REPAINT, 0, &bs_mode,
+               { "Underlined text displayed in underline mode",
+                 "Backspaces cause overstrike",
+                 "Backspaces print as ^H"
+               }
+       },
+       { 'w', BOOL|REPAINT, 1, &twiddle,
+               { "Display nothing for lines after end-of-file",
+                 "Display ~ for lines after end-of-file",
+                 NULL
+               }
+       },
+       { 'x', NUMBER|REPAINT, 8, &tabstop,
+               { "Tab stops every %d spaces", 
+                 NULL, NULL 
+               }
+       },
+       { 'z', NUMBER|REPAINT, -1, &sc_window,
+               { "Scroll window size is %d lines",
+                 NULL, NULL
+               }
+       },
+       { '\0' }
+};
+
+/*
+ * Initialize each option to its default value.
+ */
+       public void
+init_option()
+{
+       register struct option *o;
+
+       first_cmd = every_first_cmd = NULL;
+
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               /*
+                * Set each variable to its default.
+                */
+               *(o->ovar) = o->odefault;
+       }
+}
+
+/*
+ * Toggle command line flags from within the program.
+ * Used by the "-" and "_" commands.
+ * If do_toggle is zero, just report the current setting, without changing it.
+ */
+       public void
+toggle_option(s, do_toggle)
+       char *s;
+       int do_toggle;
+{
+       int c;
+       register struct option *o;
+       char *msg;
+       int n;
+       int dorepaint;
+       char message[100];
+
+       c = *s++;
+
+       switch (c)
+       {
+       case 'P':
+               /*
+                * Special case for -P.
+                */
+               if (*s == '\0')
+                       error(prproto[pr_type]);
+               else
+                       (void) opt_P(s);
+               return;
+#if TAGS
+       case 't':
+               /*
+                * Special case for -t.
+                */
+               if (*s == '\0')
+               {
+                       error("no tag");
+                       return;
+               }
+               findtag(s);
+               if (tagfile != NULL)
+               {
+                       edit(tagfile);
+                       (void) tagsearch();
+               }
+               return;
+#endif
+#if LOGFILE
+       case 'L':
+               /*
+                * Special case for -l and -L.
+                */
+               force_logfile = 1;
+               goto case_l;
+       case 'l':
+               force_logfile = 0;
+       case_l:
+               if (*s == '\0')
+               {
+                       if (logfile < 0)
+                               error("no log file");
+                       else
+                       {
+                               sprintf(message, "log file \"%s\"",
+                                       namelogfile);
+                               error(message);
+                       }
+                       return;
+               }
+               if (!ispipe)
+               {
+                       error("input is not a pipe");
+                       return;
+               }
+               if (logfile >= 0)
+               {
+                       error("log file is already in use");
+                       return;
+               }
+               namelogfile = save(s);
+               use_logfile();
+               sync_logfile();
+               return;
+#endif
+       }
+
+       msg = NULL;
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               if (o->otype & NO_TOGGLE)
+                       continue;
+               dorepaint = (o->otype & REPAINT);
+               if ((o->otype & BOOL) && (o->oletter == c))
+               {
+                       /*
+                        * Boolean option: 
+                        * just toggle it.
+                        */
+                       if (do_toggle)
+                               *(o->ovar) = ! *(o->ovar);
+               } else if ((o->otype & TRIPLE) && (o->oletter == c))
+               {
+                       /*
+                        * Triple-valued option with lower case letter:
+                        * make it 1 unless already 1, then make it 0.
+                        */
+                       if (do_toggle)
+                               *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
+               } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
+               {
+                       /*
+                        * Triple-valued option with upper case letter:
+                        * make it 2 unless already 2, then make it 0.
+                        */
+                       if (do_toggle)
+                               *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
+               } else if ((o->otype & NUMBER) && (o->oletter == c))
+               {
+                       n = getnum(&s, '\0');
+                       if (n < 0)
+                       {
+                               /*
+                                * No number; just a query.
+                                * No need to repaint screen.
+                                */
+                               dorepaint = 0;
+                       } else
+                       {
+                               /*
+                                * Number follows the option letter.
+                                * Set the variable to that number.
+                                */
+                               if (do_toggle)
+                                       *(o->ovar) = n;
+                       }
+
+                       /*
+                        * Special case for -b.
+                        * Call ch_init to set new number of buffers.
+                        */
+                       if (o->ovar == &cbufs)
+                               ch_init(cbufs, 1);
+
+                       sprintf(message, o->odesc[0], 
+                               (o->ovar == &back_scroll) ? 
+                               get_back_scroll() : *(o->ovar));
+                       msg = message;
+               } else
+                       continue;
+
+               /*
+                * Print a message describing the new setting.
+                */
+               if (msg == NULL)
+                       msg = o->odesc[*(o->ovar)];
+               error(msg);
+
+               if (do_toggle && dorepaint)
+                       repaint();
+               return;
+       }
+
+       if (control_char(c))
+               sprintf(message, "-^%c", carat_char(c));
+       else
+               sprintf(message, "-%c", c);
+       strcat(message, ": no such flag.");
+       error(message);
+}
+
+/*
+ * Determine if an option is a single character option (BOOL or TRIPLE),
+ * or if it a multi-character option (NUMBER).
+ */
+       public int
+single_char_option(c)
+       int c;
+{
+       register struct option *o;
+
+       if (c == 'P')
+               return (0);
+#if TAGS
+       if (c == 't')
+               return (0);
+#endif
+#if LOGFILE
+       if (c == 'l' || c == 'L')
+               return (0);
+#endif
+       for (o = option;  o->oletter != '\0';  o++)
+               if (o->oletter == c)
+                       return (o->otype & (BOOL|TRIPLE));
+       return (1);
+}
+
+/*
+ * Scan to end of string or to an END_OPTION_STRING character.
+ * In the latter case, replace the char with a null char.
+ * Return a pointer to the remainder of the string, if any.
+ */
+       static char *
+optstring(s, c)
+       char *s;
+       int c;
+{
+       register char *p;
+       char message[80];
+
+       if (*s == '\0')
+       {
+               sprintf(message, "string is required after -%c", c);
+               error(message);
+               exit(1);
+       }
+       for (p = s;  *p != '\0';  p++)
+               if (*p == END_OPTION_STRING)
+               {
+                       *p = '\0';
+                       return (p+1);
+               }
+       return (p);
+}
+
+/* 
+ * Scan an argument (either from command line or from LESS environment 
+ * variable) and process it.
+ */
+       public void
+scan_option(s)
+       char *s;
+{
+       register struct option *o;
+       register int c;
+       int set_default;
+       char message[80];
+
+       if (s == NULL)
+               return;
+
+       set_default = 0;
+    next:
+       if (*s == '\0')
+               return;
+       switch (c = *s++)
+       {
+       case ' ':
+       case '\t':
+       case END_OPTION_STRING:
+               goto next;
+       case '-':
+               if (set_default = (*s == '+'))
+                       s++;
+               goto next;
+       case '+':
+               plusoption = 1;
+               if (*s == '+')
+                       every_first_cmd = save(++s);
+               first_cmd = s;
+               s = optstring(s, c);
+               goto next;
+#if LOGFILE
+       case 'L':
+               force_logfile = 1;
+               /* FALLTHRU */
+       case 'l':
+               namelogfile = s;
+               s = optstring(s, c);
+               goto next;
+#endif
+#if TAGS
+       case 't':
+       {
+               char *p;
+               tagoption = 1;
+               p = s;
+               s = optstring(s, c);
+               findtag(p);
+               goto next;
+       }
+#endif
+       case 'P':
+               s = opt_P(s);
+               goto next;
+       case '0':  case '1':  case '2':  case '3':  case '4':
+       case '5':  case '6':  case '7':  case '8':  case '9':
+               /*
+                * Handle special "more" compatibility form "-number"
+                * (instead of -znumber) to set the scrolling window size.
+                */
+               s--;
+               c = 'z';
+               break;
+       }
+
+       for (o = option;  o->oletter != '\0';  o++)
+       {
+               if ((o->otype & BOOL) && (o->oletter == c))
+               {
+                       if (set_default)
+                               *(o->ovar) = o->odefault;
+                       else
+                               *(o->ovar) = ! o->odefault;
+                       goto next;
+               } else if ((o->otype & TRIPLE) && (o->oletter == c))
+               {
+                       if (set_default)
+                               *(o->ovar) = o->odefault;
+                       else
+                               *(o->ovar) = (o->odefault == 1) ? 0 : 1;
+                       goto next;
+               } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
+               {
+                       if (set_default)
+                               *(o->ovar) = o->odefault;
+                       else
+                               *(o->ovar) = (o->odefault == 2) ? 0 : 2;
+                       goto next;
+               } else if ((o->otype & NUMBER) && (o->oletter == c))
+               {
+                       *(o->ovar) = getnum(&s, c);
+                       goto next;
+               }
+       }
+
+       sprintf(message, "\"-%c\": invalid flag", c);
+       error(message);
+       exit(1);
+}
+
+/*
+ * Special case for -P.
+ */
+       static char *
+opt_P(s)
+       register char *s;
+{
+       register char *es;
+       register char **proto;
+
+       es = optstring(s, 'P');
+
+       /*
+        * Figure out which prototype string should be changed.
+        */
+       switch (*s)
+       {
+       case 'm':  proto = &prproto[PR_MEDIUM]; s++;    break;
+       case 'M':  proto = &prproto[PR_LONG];   s++;    break;
+       case '=':  proto = &eqproto;            s++;    break;
+       default:   proto = &prproto[pr_type];           break;
+       }
+
+       free(*proto);
+       *proto = save(s);
+
+       return (es);
+}
+
+/*
+ * Translate a string into a number.
+ * Like atoi(), but takes a pointer to a char *, and updates
+ * the char * to point after the translated number.
+ */
+       public int
+getnum(sp, c)
+       char **sp;
+       int c;
+{
+       register char *s;
+       register int n;
+       char message[80];
+
+       s = *sp;
+       if (*s < '0' || *s > '9')
+       {
+               if (c == '\0')
+                       return (-1);
+               sprintf(message, "number is required after -%c", c);
+               error(message);
+               exit(1);
+       }
+
+       n = 0;
+       while (*s >= '0' && *s <= '9')
+               n = 10 * n + *s++ - '0';
+       *sp = s;
+       return (n);
+}
diff --git a/usr/src/usr.bin/more/os.c b/usr/src/usr.bin/more/os.c
new file mode 100644 (file)
index 0000000..d8346f9
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)os.c       5.1 (Berkeley) %G%";
+#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 <stdio.h>
+#include <signal.h>
+#include <setjmp.h>
+#include "less.h"
+
+char *getenv();
+
+public 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.
+ */
+       public void
+lsystem(cmd)
+       char *cmd;
+{
+       int inp;
+       char cmdbuf[256];
+       char *shell;
+
+       /*
+        * 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);
+       close(0);
+       if (open("/dev/tty", 0) < 0)
+               dup(inp);
+
+       /*
+        * Pass the command to the system to be executed.
+        * If we have a SHELL environment variable, use
+        * <$SHELL -c "command"> instead of just <command>.
+        * If the command is empty, just invoke a shell.
+        */
+       if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
+       {
+               if (*cmd == '\0')
+                       cmd = shell;
+               else
+               {
+                       sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
+                       cmd = cmdbuf;
+               }
+       }
+       if (*cmd == '\0')
+               cmd = "sh";
+
+       system(cmd);
+
+       /*
+        * Restore standard input, reset signals, raw mode, etc.
+        */
+       close(0);
+       dup(inp);
+       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().
+ */
+       public int
+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);
+}
+
+       public void
+intread()
+{
+#if SIGSETMASK
+       sigsetmask(0);
+#endif
+       longjmp(read_label, 1);
+}
+
+#if GET_TIME
+       public long
+get_time()
+{
+       long t;
+
+       time(&t);
+       return (t);
+}
+#endif
+
+/*
+ * 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.
+ */
+#if GLOB
+
+FILE *popen();
+
+       public char *
+glob(filename)
+       char *filename;
+{
+       FILE *f;
+       char *p;
+       int ch;
+       char *cmd;
+       static char buffer[FILENAME];
+
+       if (filename[0] == '#')
+               return (filename);
+
+       /*
+        * We get the shell to expand the filename for us by passing
+        * an "echo" command to the shell and reading its output.
+        */
+       p = getenv("SHELL");
+       if (p == NULL || *p == '\0')
+       {
+               /*
+                * Read the output of <echo filename>.
+                */
+               cmd = calloc(strlen(filename)+8, sizeof(char));
+               if (cmd == NULL)
+                       return (filename);
+               sprintf(cmd, "echo \"%s\"", filename);
+       } else
+       {
+               /*
+                * Read the output of <$SHELL -c "echo filename">.
+                */
+               cmd = calloc(strlen(p)+12);
+               if (cmd == NULL)
+                       return (filename);
+               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';
+       pclose(f);
+       return (buffer);
+}
+
+#else
+
+       public char *
+glob(filename)
+       char *filename;
+{
+       return (filename);
+}
+
+#endif
+
+
+/*
+ * Returns NULL if the file can be opened and
+ * is an ordinary file, otherwise an error message
+ * (if it cannot be opened or is a directory, etc.)
+ */
+
+#if STAT
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+       public char *
+bad_file(filename, message, len)
+       char *filename;
+       char *message;
+       unsigned int len;
+{
+       struct stat statbuf;
+
+       if (stat(filename, &statbuf) < 0)
+               return (errno_message(filename, message, len));
+
+       if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+       {
+               static char is_dir[] = " is a directory";
+               strtcpy(message, filename, len-sizeof(is_dir)-1);
+               strcat(message, is_dir);
+               return (message);
+       }
+       if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+       {
+               static char not_reg[] = " is not a regular file";
+               strtcpy(message, filename, len-sizeof(not_reg)-1);
+               strcat(message, not_reg);
+               return (message);
+       }
+       return (NULL);
+}
+
+#else
+
+       public char *
+bad_file(filename, message, len)
+       char *filename;
+       char *message;
+       unsigned int len;
+{
+       return (NULL);
+}
+
+#endif
+
+/*
+ * errno_message: Return an error message based on the value of "errno".
+ * okreadfail: Return true if the previous failure of a read
+ *     (on the input tty) should be considered ok.
+ */
+
+#if PERROR
+
+extern char *sys_errlist[];
+extern int sys_nerr;
+extern int errno;
+
+       public char *
+errno_message(filename, message, len)
+       char *filename;
+       char *message;
+       unsigned int len;
+{
+       char *p;
+       char msg[16];
+
+       if (errno < sys_nerr)
+               p = sys_errlist[errno];
+       else
+       {
+               sprintf(msg, "Error %d", errno);
+               p = msg;
+       }
+       strtcpy(message, filename, len-strlen(p)-3);
+       strcat(message, ": ");
+       strcat(message, p);
+       return (message);
+}
+
+#else
+
+       public char *
+errno_message(filename, message, len)
+       char *filename;
+       char *message;
+       unsigned int len;
+{
+       static char msg[] = ": cannot open";
+
+       strtcpy(message, filename, len-sizeof(msg)-1);
+       strcat(message, msg);
+       return (message);
+}
+
+#endif
diff --git a/usr/src/usr.bin/more/output.c b/usr/src/usr.bin/more/output.c
new file mode 100644 (file)
index 0000000..42cb259
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * High level routines dealing with the output to the screen.
+ */
+
+#include "less.h"
+
+public 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 twiddle;
+extern int screen_trashed;
+extern int any_display;
+extern char *line;
+extern char *first_cmd;
+
+/*
+ * Display the line which is in the line buffer.
+ */
+       public void
+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 = (twiddle) ? "~" : "";
+
+       column = 0;
+       for (p = line;  *p != '\0';  p++)
+       {
+               switch (c = *p)
+               {
+               case UL_CHAR:
+                       ul_enter();
+                       column += ul_width;
+                       break;
+               case UE_CHAR:
+                       ul_exit();
+                       column += ue_width;
+                       break;
+               case BO_CHAR:
+                       bo_enter();
+                       column += bo_width;
+                       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');
+}
+
+/*
+ * Is a given character a "control" character?
+ * {{ ASCII DEPENDENT }}
+ */
+       public int
+control_char(c)
+       int c;
+{
+       return (c < ' ' || c == '\177');
+}
+
+/*
+ * Return the printable character used to identify a control character
+ * (printed after a carat; e.g. '\3' => "^C").
+ * {{ ASCII DEPENDENT }}
+ */
+       public int
+carat_char(c)
+       int c;
+{
+       return ((c == '\177') ? '?' : (c | 0100));
+}
+
+
+static char obuf[1024];
+static char *ob = obuf;
+
+/*
+ * Flush buffered output.
+ */
+       public void
+flush()
+{
+       register int n;
+
+       n = ob - obuf;
+       if (n == 0)
+               return;
+       if (write(1, obuf, n) != n)
+               screen_trashed = 1;
+       ob = obuf;
+}
+
+/*
+ * Discard buffered output.
+ */
+       public void
+dropout()
+{
+       ob = obuf;
+}
+
+/*
+ * Output a character.
+ */
+       public void
+putchr(c)
+       int c;
+{
+       if (ob >= &obuf[sizeof(obuf)])
+               flush();
+       *ob++ = c;
+}
+
+/*
+ * Output a string.
+ */
+       public void
+putstr(s)
+       register char *s;
+{
+       while (*s != '\0')
+               putchr(*s++);
+}
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+
+static char return_to_continue[] = "  (press RETURN)";
+
+       public void
+error(s)
+       char *s;
+{
+       register int c;
+       static char buf[2];
+
+       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.
+                */
+               write(2, s, strlen(s));
+               write(2, "\n", 1);
+               return;
+       }
+
+       lower_left();
+       clear_eol();
+       so_enter();
+       putstr(s);
+       putstr(return_to_continue);
+       so_exit();
+
+#if ONLY_RETURN
+       while ((c = getchr()) != '\n' && c != '\r')
+               bell();
+#else
+       c = getchr();
+       if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
+       {
+               buf[0] = c;
+               first_cmd = buf;
+       }
+#endif
+       lower_left();
+
+       if (strlen(s) + sizeof(return_to_continue) + 
+               so_width + se_width + 1 > sc_width)
+               /*
+                * Printing the message has probably scrolled the screen.
+                * {{ Unless the terminal doesn't have auto margins,
+                *    in which case we just hammered on the right margin. }}
+                */
+               repaint();
+
+       flush();
+}
+
+static char intr_to_abort[] = "... (interrupt to abort)";
+
+       public void
+ierror(s)
+       char *s;
+{
+
+       lower_left();
+       clear_eol();
+       so_enter();
+       putstr(s);
+       putstr(intr_to_abort);
+       so_exit();
+       flush();
+}
diff --git a/usr/src/usr.bin/more/position.c b/usr/src/usr.bin/more/position.c
new file mode 100644 (file)
index 0000000..998c61c
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)position.c 5.1 (Berkeley) %G%";
+#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 "less.h"
+#include "position.h"
+
+#define        NPOS    100             /* {{ sc_height must be less than NPOS }} */
+static POSITION table[NPOS];   /* The position table */
+
+extern int sc_width, 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
+ */
+       public POSITION
+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.
+ */
+       public void
+add_forw_pos(pos)
+       POSITION 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.
+ */
+       public void
+add_back_pos(pos)
+       POSITION 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;
+}
+
+/*
+ * Initialize the position table, done whenever we clear the screen.
+ */
+       public void
+pos_clear()
+{
+       register int i;
+
+       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.
+ */
+       public int
+onscreen(pos)
+       POSITION 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/src/usr.bin/more/position.h b/usr/src/usr.bin/more/position.h
new file mode 100644 (file)
index 0000000..610c1fc
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)position.h  5.1 (Berkeley) %G%
+ */
+
+/*
+ * Include file for interfacing to position.c modules.
+ */
+#define        TOP             (0)
+#define        TOP_PLUS_ONE    (1)
+#define        BOTTOM          (-1)
+#define        BOTTOM_PLUS_ONE (-2)
+#define        MIDDLE          (-3)
diff --git a/usr/src/usr.bin/more/prim.c b/usr/src/usr.bin/more/prim.c
new file mode 100644 (file)
index 0000000..21e7dcb
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)prim.c     5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Primitives for displaying the file on the screen.
+ */
+
+#include "less.h"
+#include "position.h"
+
+public int hit_eof;    /* Keeps track of how many times we hit end of file */
+public int screen_trashed;
+
+static int squished;
+
+extern int quiet;
+extern int sigs;
+extern int how_search;
+extern int top_scroll;
+extern int back_scroll;
+extern int sc_width, sc_height;
+extern int quit_at_eof;
+extern int caseless;
+extern int linenums;
+extern int plusoption;
+extern char *line;
+extern char *first_cmd;
+#if TAGS
+extern int tagoption;
+#endif
+
+/*
+ * Sound the bell to indicate he is trying to move past end of file.
+ */
+       static void
+eof_bell()
+{
+       if (quiet == NOT_QUIET)
+               bell();
+       else
+               vbell();
+}
+
+/*
+ * Check to see if the end of file is currently "displayed".
+ */
+       static void
+eof_check()
+{
+       POSITION 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.
+ */
+       static void
+squish_check()
+{
+       if (!squished)
+               return;
+       squished = 0;
+       repaint();
+}
+
+/*
+ * Display n lines, scrolling forward, 
+ * starting at position pos in the input file.
+ * "force" means display the n lines even if we hit end of file.
+ * "only_last" means display only the last screenful if n > screen size.
+ */
+       static void
+forw(n, pos, force, only_last)
+       register int n;
+       POSITION pos;
+       int force;
+       int only_last;
+{
+       int eof = 0;
+       int nlines = 0;
+       int do_repaint;
+       static int first_time = 1;
+
+       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. }}
+                        */
+                       if (top_scroll == 2)
+                               clear();
+                       home();
+                       force = 1;
+               } else
+               {
+                       lower_left();
+                       clear_eol();
+               }
+
+               if (pos != position(BOTTOM_PLUS_ONE))
+               {
+                       /*
+                        * This is not contiguous with what is
+                        * currently displayed.  Clear the screen image 
+                        * (position table) and start a new screen.
+                        */
+                       pos_clear();
+                       add_forw_pos(pos);
+                       force = 1;
+                       if (top_scroll)
+                       {
+                               if (top_scroll == 2)
+                                       clear();
+                               home();
+                       } else if (!first_time)
+                       {
+                               putstr("...skipping...\n");
+                       }
+               }
+       }
+
+       while (--n >= 0)
+       {
+               /*
+                * Read the next line of input.
+                */
+               pos = forw_line(pos);
+               if (pos == NULL_POSITION)
+               {
+                       /*
+                        * End of file: stop here unless the top line 
+                        * is still empty, or "force" is true.
+                        */
+                       eof = 1;
+                       if (!force && position(TOP) != NULL_POSITION)
+                               break;
+                       line = NULL;
+               }
+               /*
+                * Add the position of the next line to the position table.
+                * Display the current line on the screen.
+                */
+               add_forw_pos(pos);
+               nlines++;
+               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 + option or a -t option
+                * was given.  These options 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 && 
+#if TAGS
+                   !tagoption &&
+#endif
+                   !plusoption)
+               {
+                       squished = 1;
+                       continue;
+               }
+               if (top_scroll == 1)
+                       clear_eol();
+               put_line();
+       }
+
+       if (eof && !sigs)
+               hit_eof++;
+       else
+               eof_check();
+       if (nlines == 0)
+               eof_bell();
+       else if (do_repaint)
+               repaint();
+       first_time = 0;
+       (void) currline(BOTTOM);
+}
+
+/*
+ * Display n lines, scrolling backward.
+ */
+       static void
+back(n, pos, force, only_last)
+       register int n;
+       POSITION pos;
+       int force;
+       int only_last;
+{
+       int nlines = 0;
+       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)
+               {
+                       /*
+                        * Beginning of file: stop here unless "force" is true.
+                        */
+                       if (!force)
+                               break;
+                       line = NULL;
+               }
+               /*
+                * Add the position of the previous line to the position table.
+                * Display the line on the screen.
+                */
+               add_back_pos(pos);
+               nlines++;
+               if (!do_repaint)
+               {
+                       home();
+                       add_line();
+                       put_line();
+               }
+       }
+
+       eof_check();
+       if (nlines == 0)
+               eof_bell();
+       else 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.
+ */
+       public void
+forward(n, only_last)
+       int n;
+       int only_last;
+{
+       POSITION pos;
+
+       if (quit_at_eof && hit_eof)
+       {
+               /*
+                * If the -e flag is set and 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)
+       {
+               eof_bell();
+               hit_eof++;
+               return;
+       }
+       forw(n, pos, 0, only_last);
+}
+
+/*
+ * Display n more lines, backward.
+ * Start just before the line currently displayed at the top of the screen.
+ */
+       public void
+backward(n, only_last)
+       int n;
+       int only_last;
+{
+       POSITION pos;
+
+       pos = position(TOP);
+       if (pos == NULL_POSITION)
+       {
+               /* 
+                * This will almost never happen,
+                * because the top line is almost never empty. 
+                */
+               eof_bell();
+               return;   
+       }
+       back(n, pos, 0, only_last);
+}
+
+/*
+ * Repaint the screen, starting from a specified position.
+ */
+       static void
+prepaint(pos)  
+       POSITION pos;
+{
+       hit_eof = 0;
+       forw(sc_height-1, pos, 1, 0);
+       screen_trashed = 0;
+}
+
+/*
+ * Repaint the screen.
+ */
+       public void
+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.
+ */
+       public void
+jump_forw()
+{
+       POSITION 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, 0);
+}
+
+/*
+ * Jump to line n in the file.
+ */
+       public void
+jump_back(n)
+       register int n;
+{
+       register int c;
+       int 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((POSITION)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];
+                               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.
+ */
+       public void
+jump_percent(percent)
+       int percent;
+{
+       POSITION pos, len;
+       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.
+ */
+       public void
+jump_loc(pos)
+       POSITION pos;
+{
+       register int nline;
+       POSITION tpos;
+
+       if ((nline = onscreen(pos)) >= 0)
+       {
+               /*
+                * The line is currently displayed.  
+                * Just scroll there.
+                */
+               forw(nline, position(BOTTOM_PLUS_ONE), 1, 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)
+       {
+               POSITION 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, 1, 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 POSITION marks[NMARKS];
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+       public void
+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.
+ */
+       public void
+setmark(c)
+       int c;
+{
+       if (badmark(c))
+               return;
+       marks[c-'a'] = position(TOP);
+}
+
+       public void
+lastmark()
+{
+       marks[LASTMARK] = position(TOP);
+}
+
+/*
+ * Go to a previously set mark.
+ */
+       public void
+gomark(c)
+       int c;
+{
+       POSITION pos;
+
+       if (c == '\'')
+               pos = marks[LASTMARK];
+       else if (badmark(c))
+               return;
+       else 
+               pos = marks[c-'a'];
+
+       if (pos == NULL_POSITION)
+               error("mark not set");
+       else
+               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.
+ */
+       public int
+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.
+ */
+       public void
+search(search_forward, pattern, n, wantmatch)
+       register int search_forward;
+       register char *pattern;
+       register int n;
+       int wantmatch;
+{
+       POSITION pos, linepos;
+       register char *p;
+       register char *q;
+       int linenum;
+       int linematch;
+#if RECOMP
+       char *re_comp();
+       char *errmsg;
+#else
+#if REGCMP
+       char *regcmp();
+       static char *cpattern = NULL;
+#else
+       static char lpbuf[100];
+       static char *last_pattern = NULL;
+#endif
+#endif
+
+       if (caseless && pattern != NULL)
+       {
+               /*
+                * For a caseless search, convert any uppercase
+                * in the pattern to lowercase.
+                */
+               for (p = pattern;  *p != '\0';  p++)
+                       if (*p >= 'A' && *p <= 'Z')
+                               *p += 'a' - 'A';
+       }
+#if 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;
+       }
+#else
+#if 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;
+               }
+       } else
+       {
+               /*
+                * Otherwise compile the given pattern.
+                */
+               char *s;
+               if ((s = regcmp(pattern, 0)) == NULL)
+               {
+                       error("Invalid pattern");
+                       return;
+               }
+               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;
+               }
+               pattern = last_pattern;
+       } else
+       {
+               strcpy(lpbuf, pattern);
+               last_pattern = lpbuf;
+       }
+#endif
+#endif
+
+       /*
+        * Figure out where to start the search.
+        */
+
+       if (position(TOP) == NULL_POSITION)
+       {
+               /*
+                * Nothing is currently displayed.
+                * Start at the beginning of the file.
+                * (This case is mainly for first_cmd searches,
+                * for example, "+/xyz" on the command line.)
+                */
+               pos = (POSITION)0;
+       } else if (!search_forward)
+       {
+               /*
+                * Backward search: start just before the top line
+                * displayed on the screen.
+                */
+               pos = position(TOP);
+       } else if (how_search == 0)
+       {
+               /*
+                * Start at the second real line displayed on the screen.
+                */
+               pos = position(TOP);
+               do
+                       pos = forw_raw_line(pos);
+               while (pos < position(TOP+1));
+       } else if (how_search == 1)
+       {
+               /*
+                * Start just after the bottom line displayed on the screen.
+                */
+               pos = position(BOTTOM_PLUS_ONE);
+       } 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;
+       }
+
+       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;
+
+               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;
+               }
+
+               /*
+                * 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 (caseless)
+               {
+                       /*
+                        * If this is a caseless search, convert 
+                        * uppercase in the input line to lowercase.
+                        * While we're at it, remove any backspaces
+                        * along with the preceeding char.
+                        * This allows us to match text which is 
+                        * underlined or overstruck.
+                        */
+                       for (p = q = line;  *p != '\0';  p++, q++)
+                       {
+                               if (*p >= 'A' && *p <= 'Z')
+                                       /* Convert uppercase to lowercase. */
+                                       *q = *p + 'a' - 'A';
+                               else 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.
+                */
+#if REGCMP
+               linematch = (regex(cpattern, line) != NULL);
+#else
+#if RECOMP
+               linematch = (re_exec(line) == 1);
+#else
+               linematch = match(pattern, line);
+#endif
+#endif
+               /*
+                * We are successful if wantmatch and linematch are
+                * both true (want a match and got it),
+                * or both false (want a non-match and got it).
+                */
+               if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
+                     --n <= 0)
+                       /*
+                        * Found the line.
+                        */
+                       break;
+       }
+
+       jump_loc(linepos);
+}
+
+#if (!REGCMP) && (!RECOMP)
+/*
+ * We have neither regcmp() nor re_comp().
+ * We use this function to do simple pattern matching.
+ * It supports no metacharacters like *, etc.
+ */
+       static int
+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/src/usr.bin/more/prompt.c b/usr/src/usr.bin/more/prompt.c
new file mode 100644 (file)
index 0000000..42dd415
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)prompt.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Prompting and other messages.
+ * There are three flavors of prompts, SHORT, MEDIUM and LONG,
+ * selected by the -m/-M options.
+ * There is also the "equals message", printed by the = command.
+ * A prompt is a message composed of various pieces, such as the 
+ * name of the file being viewed, the percentage into the file, etc.
+ */
+
+#include "less.h"
+#include "position.h"
+
+extern int pr_type;
+extern int ispipe;
+extern int hit_eof;
+extern int new_file;
+extern int sc_width;
+extern int so_width, se_width;
+extern char *current_file;
+extern int ac;
+extern char **av;
+extern int curr_ac;
+extern int linenums;
+
+/*
+ * Prototypes for the three flavors of prompts.
+ * These strings are expanded by pr_expand().
+ */
+static char s_proto[] =
+  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
+static char m_proto[] =
+  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
+static char M_proto[] =
+  "?f%f .?n?m(file %i of %m) ..?ltline %lt :byte %bB?s/%s ..?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
+static char e_proto[] =
+  "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
+
+char *prproto[3];
+char *eqproto = e_proto;
+
+static char message[250];
+static char *mp;
+
+/*
+ * Initialize the prompt prototype strings.
+ */
+       public void
+init_prompt()
+{
+       prproto[0] = save(s_proto);
+       prproto[1] = save(m_proto);
+       prproto[2] = save(M_proto);
+       eqproto = save(e_proto);
+}
+
+/*
+ * Set the message pointer to the end of the message string.
+ */
+       static void
+setmp()
+{
+       while (*mp != '\0')
+               mp++;
+}
+
+/*
+ * Append a POSITION (as a decimal integer) to the end of the message.
+ */
+       static void
+ap_pos(pos)
+       POSITION pos;
+{
+       sprintf(mp, "%ld", (long)pos);
+       setmp();
+}
+
+/*
+ * Append an integer to the end of the message.
+ */
+       static void
+ap_int(n)
+       int n;
+{
+       sprintf(mp, "%d", n);
+       setmp();
+}
+
+/*
+ * Append a question mark to the end of the message.
+ */
+       static void
+ap_quest()
+{
+       *mp++ = '?';
+}
+
+/*
+ * Return the "current" byte offset in the file.
+ */
+       static POSITION
+curr_byte(where)
+       int where;
+{
+       POSITION pos;
+
+       pos = position(where);
+       if (pos == NULL_POSITION)
+               pos = ch_length();
+       return (pos);
+}
+
+/*
+ * Return the value of a prototype conditional.
+ * A prototype string may include conditionals which consist of a 
+ * question mark followed by a single letter.
+ * Here we decode that letter and return the appropriate boolean value.
+ */
+       static int
+cond(c, where)
+       char c;
+       int where;
+{
+       switch (c)
+       {
+       case 'a':       /* Anything in the message yet? */
+               return (mp > message);
+       case 'b':       /* Current byte offset known? */
+               return (curr_byte(where) != NULL_POSITION);
+       case 'e':       /* At end of file? */
+               return (hit_eof);
+       case 'f':       /* Filename known? */
+               return (!ispipe);
+       case 'l':       /* Line number known? */
+               return (linenums);
+       case 'm':       /* More than one file? */
+               return (ac > 1);
+       case 'n':       /* First prompt in a new file? */
+               return (new_file);
+       case 'p':       /* Percent into file known? */
+               return (curr_byte(where) != NULL_POSITION && 
+                               ch_length() > 0);
+       case 's':       /* Size of file known? */
+               return (ch_length() != NULL_POSITION);
+       case 'x':       /* Is there a "next" file? */
+               return (curr_ac + 1 < ac);
+       }
+       return (0);
+}
+
+/*
+ * Decode a "percent" prototype character.
+ * A prototype string may include various "percent" escapes;
+ * that is, a percent sign followed by a single letter.
+ * Here we decode that letter and take the appropriate action,
+ * usually by appending something to the message being built.
+ */
+       static void
+protochar(c, where)
+       int c;
+       int where;
+{
+       POSITION pos;
+       POSITION len;
+       int n;
+
+       switch (c)
+       {
+       case 'b':       /* Current byte offset */
+               pos = curr_byte(where);
+               if (pos != NULL_POSITION)
+                       ap_pos(pos);
+               else
+                       ap_quest();
+               break;
+       case 'f':       /* File name */
+               strtcpy(mp, current_file,
+                       (unsigned int)(&message[sizeof(message)] - mp));
+               setmp();
+               break;
+       case 'i':       /* Index into list of files */
+               ap_int(curr_ac + 1);
+               break;
+       case 'l':       /* Current line number */
+               n = currline(where);
+               if (n != 0)
+                       ap_int(n);
+               else
+                       ap_quest();
+               break;
+       case 'm':       /* Number of files */
+               ap_int(ac);
+               break;
+       case 'p':       /* Percent into file */
+               pos = curr_byte(where);
+               len = ch_length();
+               if (pos != NULL_POSITION && len > 0)
+                       ap_int((int)(100*pos / len));
+               else
+                       ap_quest();
+               break;
+       case 's':       /* Size of file */
+               len = ch_length();
+               if (len != NULL_POSITION)
+                       ap_pos(len);
+               else
+                       ap_quest();
+               break;
+       case 't':       /* Truncate trailing spaces in the message */
+               while (mp > message && mp[-1] == ' ')
+                       mp--;
+               break;
+       case 'x':       /* Name of next file */
+               if (curr_ac + 1 < ac)
+               {
+                       strtcpy(mp, av[curr_ac+1],
+                               (unsigned int)(&message[sizeof(message)] - mp));
+                       setmp();
+               } else
+                       ap_quest();
+               break;
+       }
+}
+
+/*
+ * Skip a false conditional.
+ * When a false condition is found (either a false IF or the ELSE part 
+ * of a true IF), this routine scans the prototype string to decide
+ * where to resume parsing the string.
+ * We must keep track of nested IFs and skip them properly.
+ */
+       static char *
+skipcond(p)
+       register char *p;
+{
+       register int iflevel = 1;
+
+       for (;;) switch (*++p)
+       {
+       case '?':
+               /*
+                * Start of a nested IF.
+                */
+               iflevel++;
+               break;
+       case ':':
+               /*
+                * Else.
+                * If this matches the IF we came in here with,
+                * then we're done.
+                */
+               if (iflevel == 1)
+                       return (p);
+               break;
+       case '.':
+               /*
+                * Endif.
+                * If this matches the IF we came in here with,
+                * then we're done.
+                */
+               if (--iflevel == 0)
+                       return (p);
+               break;
+       case '\\':
+               /*
+                * Backslash escapes the next character.
+                */
+               ++p;
+               break;
+       case '\0':
+               /*
+                * Whoops.  Hit end of string.
+                * This is a malformed conditional, but just treat it
+                * as if all active conditionals ends here.
+                */
+               return (p-1);
+       }
+       /*NOTREACHED*/
+}
+
+       static char *
+wherechar(p, wp)
+       char *p;
+       int *wp;
+{
+       int c;
+
+       switch (c = *p)
+       {
+       case 'b': case 'l': case 'p':
+               switch (*++p)
+               {
+               case 't':   *wp = TOP;                  break;
+               case 'm':   *wp = MIDDLE;               break;
+               case 'b':   *wp = BOTTOM;               break;
+               case 'B':   *wp = BOTTOM_PLUS_ONE;      break;
+               default:    *wp = TOP;                  break;
+               }
+       }
+       return (p);
+}
+
+/*
+ * Construct a message based on a prototype string.
+ */
+       static char *
+pr_expand(proto, maxwidth)
+       char *proto;
+       int maxwidth;
+{
+       register char *p;
+       register int c;
+       int where;
+
+       mp = message;
+
+       if (*proto == '\0')
+               return ("");
+
+       for (p = proto;  *p != '\0';  p++)
+       {
+               switch (*p)
+               {
+               default:        /* Just put the character in the message */
+                       *mp++ = *p;
+                       break;
+               case '\\':      /* Backslash escapes the next character */
+                       p++;
+                       *mp++ = *p;
+                       break;
+               case '?':       /* Conditional (IF) */
+                       if ((c = *++p) == '\0')
+                               --p;
+                       else
+                       {
+                               p = wherechar(p, &where);
+                               if (!cond(c, where))
+                                       p = skipcond(p);
+                       }
+                       break;
+               case ':':       /* ELSE */
+                       p = skipcond(p);
+                       break;
+               case '.':       /* ENDIF */
+                       break;
+               case '%':       /* Percent escape */
+                       if ((c = *++p) == '\0')
+                               --p;
+                       else
+                       {
+                               p = wherechar(p, &where);
+                               protochar(c, where);
+                       }
+                       break;
+               }
+       }
+
+       new_file = 0;
+       if (mp == message)
+               return (NULL);
+       *mp = '\0';
+       if (maxwidth > 0 && mp >= message + maxwidth)
+       {
+               /*
+                * Message is too long.
+                * Return just the final portion of it.
+                */
+               return (mp - maxwidth);
+       }
+       return (message);
+}
+
+/*
+ * Return a message suitable for printing by the "=" command.
+ */
+       public char *
+eq_message()
+{
+       return (pr_expand(eqproto, 0));
+}
+
+/*
+ * Return a prompt.
+ * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
+ * If we can't come up with an appropriate prompt, return NULL
+ * and the caller will prompt with a colon.
+ */
+       public char *
+pr_string()
+{
+       return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
+}
diff --git a/usr/src/usr.bin/more/screen.c b/usr/src/usr.bin/more/screen.c
new file mode 100644 (file)
index 0000000..c074fb3
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)screen.c   5.1 (Berkeley) %G%";
+#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 "less.h"
+#if XENIX
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#endif
+
+#if TERMIO
+#include <termio.h>
+#else
+#include <sgtty.h>
+#endif
+
+#ifdef TIOCGWINSZ
+#include <sys/ioctl.h>
+#else
+/*
+ * For the Unix PC (ATT 7300 & 3B1):
+ * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
+ * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
+ */
+#include <sys/signal.h>
+#ifdef SIGPHONE
+#include <sys/window.h>
+#endif
+#endif
+
+/*
+ * Strings passed to tputs() to do various terminal functions.
+ */
+static char
+       *sc_pad,                /* Pad string */
+       *sc_home,               /* Cursor home */
+       *sc_addline,            /* Add line, scroll down following lines */
+       *sc_lower_left,         /* Cursor to last line, first column */
+       *sc_move,               /* General cursor positioning */
+       *sc_clear,              /* Clear screen */
+       *sc_eol_clear,          /* Clear to end of line */
+       *sc_s_in,               /* Enter standout (highlighted) mode */
+       *sc_s_out,              /* Exit standout mode */
+       *sc_u_in,               /* Enter underline mode */
+       *sc_u_out,              /* Exit underline mode */
+       *sc_b_in,               /* Enter bold mode */
+       *sc_b_out,              /* Exit bold mode */
+       *sc_visual_bell,        /* Visual bell (flash screen) sequence */
+       *sc_backspace,          /* Backspace cursor */
+       *sc_init,               /* Startup terminal initialization */
+       *sc_deinit;             /* Exit terminal de-intialization */
+
+public int auto_wrap;          /* Terminal does \r\n when write past margin */
+public int ignaw;              /* Terminal ignores \n immediately after wrap */
+public int erase_char, kill_char; /* The user's erase and line-kill chars */
+public int sc_width, sc_height;        /* Height & width of screen */
+public int sc_window = -1;     /* window size for forward and backward */
+public int bo_width, be_width; /* Printing width of boldface sequences */
+public int ul_width, ue_width; /* Printing width of underline sequences */
+public 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 quiet;              /* If VERY_QUIET, use visual bell for bell */
+extern int know_dumb;          /* Don't complain about a dumb terminal */
+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.
+ */
+       public void
+raw_mode(on)
+       int on;
+{
+#if TERMIO
+       struct termio s;
+       static struct termio save_term;
+
+       if (on)
+       {
+               /*
+                * Get terminal modes.
+                */
+               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];
+
+               /*
+                * 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;
+       }
+       ioctl(2, TCSETAW, &s);
+#else
+       struct sgttyb s;
+       static struct sgttyb save_term;
+
+       if (on)
+       {
+               /*
+                * Get terminal modes.
+                */
+               ioctl(2, TIOCGETP, &s);
+
+               /*
+                * 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;
+
+               /*
+                * 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;
+       }
+       ioctl(2, TIOCSETN, &s);
+#endif
+}
+
+       static void
+cannot(s)
+       char *s;
+{
+       char message[100];
+
+       if (know_dumb)
+               /* 
+                * He knows he has a dumb terminal, so don't tell him. 
+                */
+               return;
+
+       sprintf(message, "WARNING: terminal cannot \"%s\"", s);
+       error(message);
+}
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+       public void
+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();
+
+       /*
+        * Find out what kind of terminal this is.
+        */
+       if ((term = getenv("TERM")) == NULL)
+               term = "unknown";
+       if (tgetent(termbuf, term) <= 0)
+               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
+#else
+#ifdef WIOCGETD
+       if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
+               sc_height = w.uw_height/w.uw_vs;
+       else
+#endif
+#endif
+               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')
+       {
+               cannot("clear to end of line");
+               sc_eol_clear = "";
+       }
+
+       sc_clear = tgetstr("cl", &sp);
+       if (hard || sc_clear == NULL || *sc_clear == '\0')
+       {
+               cannot("clear screen");
+               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_visual_bell = tgetstr("vb", &sp);
+       if (hard || sc_visual_bell == NULL)
+               sc_visual_bell = "";
+
+       sc_home = tgetstr("ho", &sp);
+       if (hard || sc_home == NULL || *sc_home == '\0')
+       {
+               if (*sc_move == '\0')
+               {
+                       cannot("home cursor");
+                       /*
+                        * 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)".
+                        */
+                       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')
+               {
+                       cannot("move cursor to lower left of screen");
+                       sc_lower_left = "\r";
+               } else
+               {
+                       /*
+                        * No "lower-left" string, 
+                        * but we can use "move(0,last-line)".
+                        */
+                       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')
+       {
+               cannot("scroll backwards");
+               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.
+ */
+
+
+/*
+ * Initialize terminal
+ */
+       public void
+init()
+{
+       tputs(sc_init, sc_height, putchr);
+}
+
+/*
+ * Deinitialize terminal
+ */
+       public void
+deinit()
+{
+       tputs(sc_deinit, sc_height, putchr);
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+       public void
+home()
+{
+       tputs(sc_home, 1, putchr);
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+       public void
+add_line()
+{
+       tputs(sc_addline, sc_height, putchr);
+}
+
+/*
+ * Move cursor to lower left corner of screen.
+ */
+       public void
+lower_left()
+{
+       tputs(sc_lower_left, 1, putchr);
+}
+
+/*
+ * Ring the terminal bell.
+ */
+       public void
+bell()
+{
+       if (quiet == VERY_QUIET)
+               vbell();
+       else
+               putchr('\7');
+}
+
+/*
+ * Output the "visual bell", if there is one.
+ */
+       public void
+vbell()
+{
+       if (*sc_visual_bell == '\0')
+               return;
+       tputs(sc_visual_bell, sc_height, putchr);
+}
+
+/*
+ * Clear the screen.
+ */
+       public void
+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. }}
+ */
+       public void
+clear_eol()
+{
+       tputs(sc_eol_clear, 1, putchr);
+}
+
+/*
+ * Begin "standout" (bold, underline, or whatever).
+ */
+       public void
+so_enter()
+{
+       tputs(sc_s_in, 1, putchr);
+}
+
+/*
+ * End "standout".
+ */
+       public void
+so_exit()
+{
+       tputs(sc_s_out, 1, putchr);
+}
+
+/*
+ * Begin "underline" (hopefully real underlining, 
+ * otherwise whatever the terminal provides).
+ */
+       public void
+ul_enter()
+{
+       tputs(sc_u_in, 1, putchr);
+}
+
+/*
+ * End "underline".
+ */
+       public void
+ul_exit()
+{
+       tputs(sc_u_out, 1, putchr);
+}
+
+/*
+ * Begin "bold"
+ */
+       public void
+bo_enter()
+{
+       tputs(sc_b_in, 1, putchr);
+}
+
+/*
+ * End "bold".
+ */
+       public void
+bo_exit()
+{
+       tputs(sc_b_out, 1, putchr);
+}
+
+/*
+ * Erase the character to the left of the cursor 
+ * and move the cursor left.
+ */
+       public void
+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.
+ */
+       public void
+putbs()
+{
+       tputs(sc_backspace, 1, putchr);
+}
diff --git a/usr/src/usr.bin/more/signal.c b/usr/src/usr.bin/more/signal.c
new file mode 100644 (file)
index 0000000..879039b
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)signal.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Routines dealing with signals.
+ *
+ * A signal usually merely causes a bit to be set in the "signals" word.
+ * At some convenient time, the mainline code checks to see if any
+ * signals need processing by calling psignal().
+ * If we happen to be reading from a file [in iread()] at the time
+ * the signal is received, we call intread to interrupt the iread.
+ */
+
+#include "less.h"
+#include <signal.h>
+
+/*
+ * "sigs" contains bits indicating signals which need to be processed.
+ */
+public int sigs;
+
+#define        S_INTERRUPT     01
+#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;
+
+/*
+ * Interrupt signal handler.
+ */
+       static HANDLER
+interrupt()
+{
+       SIGNAL(SIGINT, interrupt);
+       sigs |= S_INTERRUPT;
+       if (reading)
+               intread();
+}
+
+#ifdef SIGTSTP
+/*
+ * "Stop" (^Z) signal handler.
+ */
+       static HANDLER
+stop()
+{
+       SIGNAL(SIGTSTP, stop);
+       sigs |= S_STOP;
+       if (reading)
+               intread();
+}
+#endif
+
+#ifdef SIGWINCH
+/*
+ * "Window" change handler
+ */
+       public HANDLER
+winch()
+{
+       SIGNAL(SIGWINCH, winch);
+       sigs |= S_WINCH;
+       if (reading)
+               intread();
+}
+#else
+#ifdef SIGWIND
+/*
+ * "Window" change handler
+ */
+       public HANDLER
+winch()
+{
+       SIGNAL(SIGWIND, winch);
+       sigs |= S_WINCH;
+       if (reading)
+               intread();
+}
+#endif
+#endif
+
+/*
+ * Set up the signal handlers.
+ */
+       public void
+init_signals(on)
+       int on;
+{
+       if (on)
+       {
+               /*
+                * Set signal handlers.
+                */
+               (void) SIGNAL(SIGINT, interrupt);
+#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".
+ */
+       public int
+psignals()
+{
+       register int tsignals;
+
+       if ((tsignals = sigs) == 0)
+               return (0);
+       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
+               SIGNAL(SIGTTOU, SIG_IGN);
+#endif
+               lower_left();
+               clear_eol();
+               deinit();
+               flush();
+               raw_mode(0);
+#ifdef SIGTTOU
+               SIGNAL(SIGTTOU, SIG_DFL);
+#endif
+               SIGNAL(SIGTSTP, SIG_DFL);
+               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.
+                */
+               SIGNAL(SIGTSTP, stop);
+               raw_mode(1);
+               init();
+               screen_trashed = 1;
+       }
+#endif
+       if (tsignals & S_INTERRUPT)
+       {
+               bell();
+               /*
+                * {{ You may wish to replace the bell() with 
+                *    error("Interrupt"); }}
+                */
+
+               /*
+                * If we were interrupted while in the "calculating 
+                * line numbers" loop, turn off line numbers.
+                */
+               if (lnloop)
+               {
+                       lnloop = 0;
+                       linenums = 0;
+                       error("Line numbers turned off");
+               }
+
+       }
+
+       return (1);
+}
diff --git a/usr/src/usr.bin/more/tags.c b/usr/src/usr.bin/more/tags.c
new file mode 100644 (file)
index 0000000..4992981
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tags.c     5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "less.h"
+
+#define        WHITESP(c)      ((c)==' ' || (c)=='\t')
+
+#if TAGS
+
+public char *tagfile;
+public 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.
+ */
+       public int
+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';
+
+               fclose(f);
+               return;
+       }
+       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).
+ */
+       public int
+tagsearch()
+{
+       POSITION pos, linepos;
+       int linenum;
+
+       pos = (POSITION)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);
+}
+
+#endif
diff --git a/usr/src/usr.bin/more/ttyin.c b/usr/src/usr.bin/more/ttyin.c
new file mode 100644 (file)
index 0000000..10b910b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttyin.c    5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * Routines dealing with getting input from the keyboard (i.e. from the user).
+ */
+
+#include "less.h"
+
+static int tty;
+
+/*
+ * Open keyboard for input.
+ * (Just use file descriptor 2.)
+ */
+       public void
+open_getchr()
+{
+       tty = 2;
+}
+
+/*
+ * Get a character from the keyboard.
+ */
+       public int
+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);
+}
diff --git a/usr/src/usr.bin/more/vecho.c b/usr/src/usr.bin/more/vecho.c
new file mode 100644 (file)
index 0000000..26188e4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#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[] = "@(#)vecho.c    5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * This dumb little program emulates the System V "echo" command,
+ * to accomodate BSD systems which don't understand the \c escape,
+ * meaning don't echo a newline.  BSD uses "echo -n".
+ */
+
+#include <stdio.h>
+
+int putnl;
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       putnl = 1;
+       while (--argc > 0)
+       {
+               vecho(*++argv);
+               if (argc > 1)
+                       putchar(' ');
+       }
+       if (putnl)
+               putchar('\n');
+}
+
+vecho(str)
+       char *str;
+{
+       register char *s;
+
+       for (s = str;  *s != '\0';  s++)
+       {
+               if (*s == '\\' && s[1] == 'c')
+               {
+                       putnl = 0;
+                       return;
+               }
+               putchar(*s);
+       }
+}
diff --git a/usr/src/usr.bin/more/version.c b/usr/src/usr.bin/more/version.c
new file mode 100644 (file)
index 0000000..8b9f724
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mark Nudleman.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)version.c  5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ *             less
+ *     Copyright (c) 1984,1985  Mark Nudelman
+ *
+ *     This program may be freely used and/or modified, 
+ *     with the following provisions:
+ *     1. This notice and the above copyright notice must remain intact.
+ *     2. Neither this program, nor any modification of it,
+ *        may be sold for profit without written consent of the author.
+ *
+ *     -----------------------------------------------------------------
+ *
+ *     This program is a paginator similar to "more", 
+ *     but allows you to move both forward and backward in the file.  
+ *     Commands are based on "more" and "vi".
+ *
+ *     ----------------------- CHANGES ---------------------------------
+ *
+ *         Allowed use on standard input               1/29/84   markn
+ *         Added E, N, P commands                      2/1/84    markn
+ *         Added '=' command, 'stop' signal handling   4/17/84   markn
+ *         Added line folding                          4/20/84   markn
+ *     v2: Fixed '=' command to use BOTTOM_PLUS_ONE, 
+ *         instead of TOP, added 'p' & 'v' commands    4/27/84   markn
+ *     v3: Added -m and -t options, '-' command        5/3/84    markn
+ *     v4: Added LESS environment variable             5/3/84    markn
+ *     v5: New comments, fixed '-' command slightly    5/3/84    markn
+ *     v6: Added -Q, visual bell                       5/15/84   markn
+ *     v7: Fixed jump_back(n) bug: n should count real
+ *         lines, not folded lines.  Also allow number
+ *         on G command.                               5/24/84   markn
+ *     v8: Re-do -q and -Q commands                    5/30/84   markn
+ *     v9: Added "+<cmd>" argument                     9/25/84   markn
+ *     v10: Fixed bug in -b<n> argument processing     10/10/84  markn
+ *     v11: Made error() ring bell if \n not entered.  10/18/84  markn
+ *     -----------------------------------------------------------------
+ *     v12: Reorganized signal handling and made
+ *          portable to 4.2bsd.                        2/13/85   mark
+ *     v13: Reword error message for '-' command.      2/16/85   mark
+ *     v14: Added -bf and -bp variants of -b.          2/22/85   mark
+ *     v15: Miscellaneous changes.                     2/25/85   mark
+ *     v16: Added -u flag for backspace processing.    3/13/85   mark
+ *     v17: Added j and k commands, 
+ *             changed -t default.                     4/13/85   mark
+ *     v18: Rewrote signal handling code.              4/20/85   mark
+ *     v19: Got rid of "verbose" eq_message().         5/2/85    mark
+ *          Made search() scroll in some cases.
+ *     v20: Fixed screen.c ioctls for System V.        5/21/85   mark
+ *     v21: Fixed some first_cmd bugs.                 5/23/85   mark
+ *     v22: Added support for no RECOMP nor REGCMP.    5/24/85   mark
+ *     v23: Miscellanous changes and prettying up.     5/25/85   mark
+ *             Posted to USENET.
+ *     -----------------------------------------------------------------
+ *      v24: Added ti,te terminal init & de-init       6/3/85 Mike Kersenbrock
+ *     v25: Added -U flag, standout mode underlining.  6/8/85    mark
+ *     v26: Added -M flag.                             6/9/85    mark
+ *          Use underline termcap (us) if it exists.
+ *     v27: Renamed some variables to make unique in   6/15/85   mark
+ *          6 chars.  Minor fix to -m.
+ *     v28: Fixed right margin bug.                    6/28/85   mark
+ *     v29: Incorporated M.Rose's changes to signal.c  6/28/85   mark
+ *     v30: Fixed stupid bug in argument processing.   6/29/85   mark
+ *     v31: Added -p flag, changed repaint algorithm.  7/15/85   mark
+ *          Added kludge for magic cookie terminals.
+ *     v32: Added cat_file if output not a tty.        7/16/85   mark
+ *     v33: Added -e flag and EDITOR.                  7/23/85   mark
+ *     v34: Added -s flag.                             7/26/85   mark
+ *     v35: Rewrote option handling; added option.c.   7/27/85   mark
+ *     v36: Fixed -e flag to work if not last file.    7/29/85   mark
+ *     v37: Added -x flag.                             8/10/85   mark
+ *     v38: Changed prompting; created prompt.c.       8/19/85   mark
+ *     v39: (Not -p) does not initially clear screen.  8/24/85   mark
+ *     v40: Added "skipping" indicator in forw().      8/26/85   mark
+ *             Posted to USENET.
+ *     -----------------------------------------------------------------
+ *     v41: ONLY_RETURN, control char commands,        9/17/85   mark
+ *          faster search, other minor fixes.
+ *     v42: Added ++ command line syntax;              9/25/85   mark
+ *          ch_fsize for pipes.
+ *     v43: Added -h flag, changed prim.c algorithms.  10/15/85  mark
+ *     v44: Made END print in all cases of eof;        10/16/85  mark
+ *          ignore SIGTTOU after receiving SIGTSTP.
+ *     v45: Never print backspaces unless -u.          10/16/85  mark
+ *     v46: Backwards scroll in jump_loc.              10/24/85  mark
+ *     v47: Fixed bug in edit(): *first_cmd==0         10/30/85  mark
+ *     v48: Use TIOCSETN instead of TIOCSETP.          11/16/85  mark
+ *          Added marks (m and ' commands).
+ *             Posted to USENET.
+ *     -----------------------------------------------------------------
+ *     v49: Fixed bug: signal didn't clear mcc.        1/9/86    mark
+ *     v50: Added ' (quote) to gomark.                 1/15/86   mark
+ *     v51: Added + cmd, fixed problem if first_cmd
+ *          fails, made g cmd sort of "work" on pipes
+ *          even if bof is no longer buffered.         1/16/86   mark
+ *     v52: Made short files work better.              1/17/86   mark
+ *     v53: Added -P option.                           1/20/86   mark
+ *     v54: Changed help to use HELPFILE.              1/20/86   mark
+ *     v55: Messages work better if not tty output.    1/23/86   mark
+ *     v56: Added -l option.                           1/24/86   mark
+ *     v57: Fixed -l to get confirmation before
+ *          overwriting an existing file.              1/31/86   mark
+ *     v58: Added filename globbing.                   8/28/86   mark
+ *     v59: Fixed some bugs with very long filenames.  9/15/86   mark
+ *     v60: Incorporated changes from Leith (Casey)
+ *          Leedom for boldface and -z option.         9/26/86   mark
+ *     v61: Got rid of annoying repaints after ! cmd.  9/26/86   mark
+ *             Posted to USENET.
+ *     -----------------------------------------------------------------
+ *     v62: Added is_directory(); change -z default to
+ *          -1 instead of 24; cat-and-exit if -e and
+ *          file is less than a screenful.             12/23/86  mark
+ *     v63: Fixed bug in cat-and-exit if > 1 file.     1/8/87    mark
+ *     v64: Changed puts/putstr, putc/putchr, 
+ *          getc/getchr to avoid name conflict with 
+ *          stdio functions.                           1/12/87  mark
+ *     v65: Allowed '-' command to change NUMBER
+ *          valued options (thanks to Gary Puckering)  1/26/87  mark
+ *     v66: Fixed bug: prepaint should use force=1.    2/13/87  mark
+ *     v67: Added !! and % expansion to ! command.     2/24/87  mark
+ *     v68: Added SIGWINCH and TIOCGWINSZ support;
+ *          changed is_directory to bad_file.
+ *          (thanks to J. Robert Ward)                 2/25/87  mark
+ *     v69: Added SIGWIND and WIOCGETD (for Unix PC).  2/25/87  mark
+ *     v70: Changed help cmd from 'h' to 'H'; better 
+ *          error msgs in bad_file, errno_message.     3/13/87  mark
+ *     v71: Changed -p to -c, made triple -c/-C
+ *          for clear-eol like more's -c.              5/11/87  mark
+ *     v72: Added -E, -L, use $SHELL in lsystem().     6/26/87  mark
+ *          (thanks to Steve Spearman)
+ *     v73: Allow Examine "#" for previous file.       6/26/87  mark
+ *             Posted to USENET 8/25/87.
+ *     -----------------------------------------------------------------
+ *     v74: Fix conflict in EOF symbol with stdio.h,   9/18/87  mark
+ *          Make os.c more portable to BSD.
+ *     v75: Fix problems in get_term (thanks to        9/23/87  mark
+ *          Paul Eggert); new backwards scrolling in
+ *          jump_loc (thanks to Marion Hakanson).
+ *     v76: Added -i flag; allow single "!" to         9/23/87  mark
+ *          invoke a shell (thanks to Franco Barber).
+ *     v77: Added -n flag and line number support.     9/24/87  mark
+ *     v78: Fixed problem with prompts longer than     9/25/87  mark
+ *          the screen width.  
+ *     v79: Added the _ command.                       9/29/87  mark
+ *     v80: Allow signal to break out of linenum scan. 10/6/87  mark
+ *     v81: Allow -b to be changed from within less.   10/6/87  mark
+ *     v82: Add cmd_decode to use a table for key      10/7/87  mark
+ *          binding (thanks to David Nason).
+ *     v83: Allow .less file for user-defined keys.    10/9/87  mark
+ *     v84: Fix -e/-E problems (thanks to Felix Lee).  10/11/87 mark
+ *     v85: Search now keeps track of line numbers.    10/15/87 mark
+ *     v86: Added -B option and autobuf; fixed         10/20/87 mark
+ *          "pipe error" bug.
+ *     v87: Fix bug re BSD signals while reading file. 3/1/88   mark
+ *     v88: Use new format for -P option (thanks to    3/12/88  mark
+ *          der Mouse), allow "+-c" without message,
+ *          fix bug re BSD hangup.
+ *     v89: Turn off line numbers if linenum scan      3/18/88  mark
+ *          is interrupted.
+ *     v90: Allow -P from within less.                 3/30/88  mark
+ *     v91: Added tags file support (new -t option)    3/30/88  mark
+ *          (thanks to Brian Campbell).
+ *     v92: Added -+option syntax.                     4/4/88   mark
+ *     v93: Add support for slow input (thanks to      4/11/88  mark
+ *          Joe Orost & apologies for taking almost
+ *          3 years to get this in!)
+ *     v94: Redo reading/signal stuff.                 4/11/88  mark
+ *     v95: Repaint screen better after signal.        4/20/88  mark
+ *     v96: Add /! and ?! commands.                    4/21/88  mark
+ *     v97: Allow -l/-L from within less.              5/17/88  mark
+ *          Eliminate some static arrays (use calloc).
+ */
+
+char version[] = "@(#) less  version 97";