summaryrefslogtreecommitdiff
path: root/text-utils
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2006-12-07 00:25:58 +0100
committerKarel Zak <kzak@redhat.com>2006-12-07 00:25:58 +0100
commit63cccae4684f83d2a462bc8abf24e51d1bd6efb6 (patch)
tree433db3f0b44e0f46e4130141f4a59db9c3564557 /text-utils
parenta2c5f3cadcfd2ceab9d37fc847fa0d800a95633a (diff)
downloadutil-linux-old-63cccae4684f83d2a462bc8abf24e51d1bd6efb6.tar.gz
Imported from util-linux-2.11t tarball.
Diffstat (limited to 'text-utils')
-rw-r--r--text-utils/LICENSE.pg28
-rw-r--r--text-utils/Makefile31
-rw-r--r--text-utils/README.pg23
-rw-r--r--text-utils/column.c31
-rw-r--r--text-utils/line.129
-rw-r--r--text-utils/line.c38
-rw-r--r--text-utils/line.sh22
-rw-r--r--text-utils/more.c15
-rw-r--r--text-utils/pg.1238
-rw-r--r--text-utils/pg.c1775
10 files changed, 2154 insertions, 76 deletions
diff --git a/text-utils/LICENSE.pg b/text-utils/LICENSE.pg
new file mode 100644
index 00000000..e805a315
--- /dev/null
+++ b/text-utils/LICENSE.pg
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [deleted]
+ * 4. Neither the name of Gunnar Ritter nor the names of his contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/text-utils/Makefile b/text-utils/Makefile
index c8d32ad1..6fecb2f8 100644
--- a/text-utils/Makefile
+++ b/text-utils/Makefile
@@ -6,9 +6,9 @@
include ../make_include
include ../MCONFIG
-MAN1= col.1 colcrt.1 colrm.1 column.1 hexdump.1 rev.1 line.1
+MAN1= col.1 colcrt.1 colrm.1 column.1 hexdump.1 rev.1 line.1 pg.1
-USRBIN= col colcrt colrm column hexdump rev line
+USRBIN= col colcrt colrm column hexdump rev line pg
BIN=
@@ -25,26 +25,32 @@ MAN1:=$(MAN1) more.1
endif
endif
-# Where to put datebase files?
-
+# Where to put database files?
MOREHELP= more.help
MOREHELPFILE=$(MOREHELP_DIR)/$(MOREHELP)
# MOREHELP_DIR and MOREHELPDIR set in ../MCONFIG
CFLAGS:=$(CFLAGS) -DMOREHELPFILE=\"$(MOREHELPFILE)\"
+# Is pg silent?
+ifeq "$(SILENT_PG)" "yes"
+CFLAGS:=$(CFLAGS) -DPGNOBELL
+endif
+
all: $(BIN) $(USRBIN)
-# more and ul use curses - maybe we can't compile them
+# more and pg and ul use curses - maybe we can't compile them
ifeq "$(HAVE_NCURSES)" "yes"
-# Have ncurses - make more and ul
-more ul:
+# Have ncurses - make more and pg and ul
+more pg ul:
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBCURSES)
more: more.o $(LIB)/xstrncpy.o
+pg: pg.o
ul: ul.o
else
-# Do not have ncurses - give up on ul
-ul:
+# Do not have ncurses - give up on pg and ul
+pg ul:
@echo $@ not made since it requires ncurses
+# For more we can also try termcap
ifeq "$(HAVE_TERMCAP)" "yes"
more: more.o $(LIB)/xstrncpy.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBTERMCAP)
@@ -71,12 +77,7 @@ column.o: $(LIB)/errs.h
column: column.o $(ERR_O)
more.o: more.c $(LIB)/pathnames.h
rev: rev.o
-line: line.sh
-
-%: %.sh
- cp $@.sh $@
- chmod 755 $@
-
+line: line.o
colcrt.o colrm.o column.o rev.o ul.o: $(LIB)/widechar.h
diff --git a/text-utils/README.pg b/text-utils/README.pg
new file mode 100644
index 00000000..df92b85f
--- /dev/null
+++ b/text-utils/README.pg
@@ -0,0 +1,23 @@
+README for the "pg" utility for Linux.
+
+The "pg" utility is the historic System V equivalent to BSD's "more". This
+is a free clone of it, and it is intended to conform to the SVID 4 as well
+as the SUSv2 specification of this command.
+
+Contrasting to the System V implementation, this one filters backspace
+formatting sequences while searching, so you can comfortably search in nroff
+output like manual pages.
+
+This code uses routines as defined by SUSv2, so a glibc version of 2.1 or
+higher is required on Linux. A curses implementation (like ncurses) must
+be present as well.
+
+If large files > 2GB are supported by the kernel and the C library, pg is
+able to handle them.
+
+Please send comments, bug-reports and especially bug-fixes to
+<g-r@bigfoot.de> .
+
+Gunnar Ritter
+Freiburg i. Br.
+Germany
diff --git a/text-utils/column.c b/text-utils/column.c
index 22b4c120..0320bd0a 100644
--- a/text-utils/column.c
+++ b/text-utils/column.c
@@ -62,13 +62,13 @@ static char *mtsafe_strtok(char *, const char *, char **);
#define wcstok mtsafe_strtok
#endif
-void c_columnate __P((void));
-void *emalloc __P((int));
-void input __P((FILE *));
-void maketbl __P((void));
-void print __P((void));
-void r_columnate __P((void));
-void usage __P((void));
+static void c_columnate __P((void));
+static void *emalloc __P((int));
+static void input __P((FILE *));
+static void maketbl __P((void));
+static void print __P((void));
+static void r_columnate __P((void));
+static void usage __P((void));
int termwidth = 80; /* default terminal width */
@@ -87,10 +87,9 @@ main(int argc, char **argv)
int ch, tflag, xflag;
char *p;
-#ifdef __linux__
extern char *__progname;
__progname = argv[0];
-#endif
+
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
@@ -151,7 +150,7 @@ main(int argc, char **argv)
}
#define TAB 8
-void
+static void
c_columnate()
{
int chcnt, col, cnt, endcol, numcols;
@@ -181,7 +180,7 @@ c_columnate()
putwchar('\n');
}
-void
+static void
r_columnate()
{
int base, chcnt, cnt, col, endcol, numcols, numrows, row;
@@ -211,7 +210,7 @@ r_columnate()
}
}
-void
+static void
print()
{
int cnt;
@@ -229,7 +228,7 @@ typedef struct _tbl {
} TBL;
#define DEFCOLS 25
-void
+static void
maketbl()
{
TBL *t;
@@ -280,7 +279,7 @@ maketbl()
#define DEFNUM 1000
#define MAXLINELEN (LINE_MAX + 1)
-void
+static void
input(fp)
FILE *fp;
{
@@ -355,7 +354,7 @@ static char *mtsafe_strtok(char *str, const char *delim, char **ptr)
}
#endif
-void *
+static void *
emalloc(size)
int size;
{
@@ -367,7 +366,7 @@ emalloc(size)
return (p);
}
-void
+static void
usage()
{
diff --git a/text-utils/line.1 b/text-utils/line.1
index 2c356a6b..2d8e6680 100644
--- a/text-utils/line.1
+++ b/text-utils/line.1
@@ -1,17 +1,14 @@
.\" This page is in the public domain
-.Dd August 2, 2001
-.Dt line 1
-.Os Linux 2.4
-.Sh NAME
-.Nm line
-.Nd read one line
-.Sh SYNOPSIS
-.Nm
-.Sh DESCRIPTION
-.Nm
-copies one line (up to a newline) from standard input and writes it to
-standard output. It always prints at least a newline and returns an exit
-code of 1 on EOF.
-.Sh SEE ALSO
-.Xr read 2 ,
-.Xr sh 1
+.TH LINE 1 "2002-07-07" "" "User Commands"
+.SH NAME
+line \- read one line
+.SH SYNOPSIS
+.B line
+.SH DESCRIPTION
+The utility
+.I line
+copies one line (up to a newline) from standard input to standard output.
+It always prints at least a newline and returns an exit status of 1
+on EOF or read error.
+.SH "SEE ALSO"
+.BR read (1)
diff --git a/text-utils/line.c b/text-utils/line.c
new file mode 100644
index 00000000..d4bb86dd
--- /dev/null
+++ b/text-utils/line.c
@@ -0,0 +1,38 @@
+/*
+ * line - read one line
+ *
+ * Gunnar Ritter, Freiburg i. Br., Germany, December 2000.
+ *
+ * Public Domain.
+ */
+
+#ident "@(#)line.c 1.7 (gritter) 7/5/02"
+
+#include <stdio.h>
+#include <unistd.h>
+
+static int status; /* exit status */
+
+static void
+doline(int fd)
+{
+ char c;
+
+ for (;;) {
+ if (read(fd, &c, 1) <= 0) {
+ status = 1;
+ break;
+ }
+ if (c == '\n')
+ break;
+ putchar(c);
+ }
+ putchar('\n');
+}
+
+int
+main(int argc, char **argv)
+{
+ doline(0);
+ return status;
+}
diff --git a/text-utils/line.sh b/text-utils/line.sh
deleted file mode 100644
index 0d1c59c7..00000000
--- a/text-utils/line.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-#
-# line read one line
-#
-# Version: $Id: line,v 1.1 2001/08/03 14:54:10 hch Exp hch $
-#
-# Author: Christoph Hellwig <hch@caldera.de>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version
-# 2 of the License, or (at your option) any later version.
-#
-
-read line
-echo ${line}
-
-if [ -z "${line}" ]; then
- exit 1
-else
- exit 0
-fi
diff --git a/text-utils/more.c b/text-utils/more.c
index 130f1020..8a5640a4 100644
--- a/text-utils/more.c
+++ b/text-utils/more.c
@@ -1439,7 +1439,8 @@ int command (char *filename, register FILE *f)
kill_line ();
if (Senter && Sexit) {
my_putstring (Senter);
- promptlen = pr (_("[Press 'h' for instructions.]")) + (2 * soglitch);
+ promptlen = pr (_("[Press 'h' for instructions.]"))
+ + 2 * soglitch;
my_putstring (Sexit);
}
else
@@ -1794,13 +1795,13 @@ retry:
* Wait until we're in the foreground before we save the
* the terminal modes.
*/
- if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
- perror("TIOCGPGRP");
- exit(1);
+ if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
+ perror("tcgetpgrp");
+ exit(1);
}
if (tgrp != getpgrp(0)) {
- kill(0, SIGTTOU);
- goto retry;
+ kill(0, SIGTTOU);
+ goto retry;
}
}
#endif
@@ -1903,7 +1904,7 @@ int readch () {
unsigned char c;
errno = 0;
- if (read (2, &c, 1) <= 0) {
+ if (read (fileno(stderr), &c, 1) <= 0) {
if (errno != EINTR)
end_it(0);
else
diff --git a/text-utils/pg.1 b/text-utils/pg.1
new file mode 100644
index 00000000..056be06c
--- /dev/null
+++ b/text-utils/pg.1
@@ -0,0 +1,238 @@
+.\" @(#)pg.1 1.7 (gritter) 4/25/01
+.TH PG 1 "2001-04-25" "Gunnar Ritter" "User Commands"
+.SH NAME
+pg \- browse pagewise through text files
+.SH SYNOPSIS
+.B pg
+[
+.I \-number
+] [
+.BI \-p \ string
+] [
+.B \-cefnrs
+] [
+.I +line
+] [
+.I +/pattern/
+] [ file . . . ]
+.SH DESCRIPTION
+.I Pg
+displays a text file on a
+.SM CRT
+one screenful at once.
+After each page, a prompt is displayed. The user may then either press the
+newline key to view the next page or one of the keys described below.
+.PP
+If no filename is given on the command line,
+.I pg
+reads from standard input.
+If the standard output is not a terminal,
+.I pg
+acts like
+.IR cat (1)
+but precedes each file with its name if there is more than one.
+.PP
+If input comes from a pipe,
+.I pg
+stores the data in a buffer file while reading
+to make navigation possible.
+.SH OPTIONS
+.I Pg
+accepts the following options:
+.TP
+.BI \- number
+The number of lines per page. Usually, this is the number of
+.SM CRT
+lines
+minus one.
+.TP
+.B \-c
+Clear the screen before a page is displayed
+if the terminfo entry for the terminal provides this capability.
+.TP
+.B \-e
+.I pg
+will not pause and display
+.SM (EOF)
+at the end of a file.
+.TP
+.B \-f
+.I pg
+does not split long lines.
+.TP
+.B \-n
+Without this option, commands must be terminated by a newline character. With
+this option,
+.I pg
+advances once a command letter is entered.
+.TP
+.BI \-p \ string
+Instead of the prompt
+.I " :"
+,
+.I string
+is displayed.
+If
+.I string
+contains
+.I %d
+, its first occurence is replaced by the number of the current page.
+.TP
+.B \-r
+Disallow the shell escape.
+.TP
+.B \-s
+Print messages in
+.I standout
+mode
+if the terminfo entry for the terminal provides this capability.
+.TP
+.BI + number
+Start at the given line.
+.TP
+.BI +/ pattern /
+Start at the line containing the Basic Regular Expression
+.I pattern
+given.
+.SH USAGE
+The following commands may be entered at the prompt. Commands preceeded by
+.I i
+in this document accept a number as argument, positive or negative.
+If this argument starts with
+.I +
+or
+.I \-,
+it is interpreted relative to the current position in the input file,
+otherwise relative to the beginning.
+.TP
+.IB i <newline>
+Display the next or the indicated page.
+.TP
+\fIi\fR\fBd\fR or \fB^D\fR
+Display the next halfpage. If
+.I i
+is given, it is always interpreted relative to the current position.
+.TP
+.IB i l
+Display the next or the indicated line.
+.TP
+.IB i f
+Skip a page forward.
+.I i
+must be a positive number and is always interpreted relative
+to the current position.
+.TP
+\fIi\fR\fBw\fR or \fIi\fR\fBz\fR
+Behave as
+.I <newline>
+except that
+.I i
+becomes the new page size.
+.TP
+.BR . " or " ^L
+Redraw the screen.
+.TP
+.B $
+Advance to the last line of the input file.
+.TP
+.IB i / pattern /
+Search forward until the first or the \fIi\fR-th
+occurence of the Basic Regular Expression
+.I pattern
+is found. The search starts
+after the current page and stops at the end of the file. No wrap-around is
+performed.
+.I i
+must be a positive number.
+.TP
+\fIi\fR\fB?\fR\fIpattern\fR\fB?\fR or \fIi\fR\fB^\fR\fIpattern\fR\fB^\fR
+Search backward until the first or the \fIi\fR-th
+occurence of the Basic Regular Expression
+.I pattern
+is found. The search starts
+before the current page and stops at the beginning of the file.
+No wrap-around is performed.
+.I i
+must be a positive number.
+.PP
+The search commands accept an added letter. If
+.B t
+is given, the line containing the pattern is displayed at the top of the
+screen, which is the default.
+.B m
+selects the middle and
+.B b
+the bottom of the screen.
+The selected position is used in following searches, too.
+.TP
+.IB i n
+Advance to the next file or
+.I i
+files forward.
+.TP
+.IB i p
+Reread the previous file or
+.I i
+files backward.
+.TP
+.BI s \ filename
+Save the current file to the given
+.I filename.
+.TP
+.B h
+Display a command summary.
+.TP
+.BI ! command
+Execute
+.I command
+using the shell.
+.TP
+.BR q " or " Q
+Quit.
+.PP
+If the user presses the interrupt or quit key while
+.I pg
+reads from the
+input file or writes on the terminal,
+.I pg
+will immediately display the prompt.
+In all other situations these keys will terminate
+.I pg.
+.SH "ENVIRONMENT VARIABLES"
+The following environment variables
+affect the behaviour of
+.I pg:
+.TP
+.B COLUMNS
+Overrides the system-supplied number of columns if set.
+.TP
+.BR LANG ,\ LC_ALL ,\ LC_COLLATE ,\ LC_CTYPE ,\ LC_MESSAGES
+See
+.IR locale (7).
+.TP
+.B LINES
+Overrides the system-supplied number of lines if set.
+.TP
+.B SHELL
+Used by the
+.B !
+command.
+.TP
+.B TERM
+Determines the terminal type.
+.SH "SEE ALSO"
+.BR cat (1),
+.BR more (1),
+.BR sh (1),
+.BR terminfo (5),
+.BR locale (7),
+.BR regex (7),
+.BR term (7)
+.SH NOTES
+.I pg
+expects the terminal tabulators to set on eight positions.
+.PP
+Files that include
+.SM NUL
+characters cannot be displayed by
+.IR pg .
diff --git a/text-utils/pg.c b/text-utils/pg.c
new file mode 100644
index 00000000..57290756
--- /dev/null
+++ b/text-utils/pg.c
@@ -0,0 +1,1775 @@
+/*
+ * pg - A clone of the System V CRT paging utility.
+ *
+ * Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [deleted]
+ * 4. Neither the name of Gunnar Ritter nor the names of his contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Sccsid @(#)pg.c 1.44 (gritter) 2/8/02 - modified for util-linux */
+
+/*
+ * #define _XOPEN_SOURCE 500L
+ *
+ * Adding this define gives us the correct prototypes for fseeko, ftello,
+ * but (for some glibc versions) conflicting prototype for wcwidth.
+ * So, avoid defining _XOPEN_SOURCE, and give prototypes for fseeko, ftello
+ * by hand.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#ifndef TIOCGWINSZ
+#include <sys/ioctl.h>
+#endif
+#include <sys/termios.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <locale.h>
+#include <nl_types.h>
+#include <libgen.h>
+#include <curses.h>
+#include <term.h>
+
+#include "nls.h"
+#include "widechar.h"
+#include "../defines.h" /* for HAVE_fseeko */
+
+#define READBUF LINE_MAX /* size of input buffer */
+#define CMDBUF 255 /* size of command buffer */
+#define TABSIZE 8 /* spaces consumed by tab character */
+
+/*
+ * Avoid the message "`var' might be clobbered by `longjmp' or `vfork'"
+ */
+#define CLOBBGRD(a) (void)(&(a));
+
+#define cuc(c) ((c) & 0377)
+
+enum { FORWARD = 1, BACKWARD = 2 }; /* search direction */
+enum { TOP, MIDDLE, BOTTOM }; /* position of matching line */
+
+/*
+ * States for syntax-aware command line editor.
+ */
+enum {
+ COUNT,
+ SIGN,
+ CMD_FIN,
+ SEARCH,
+ SEARCH_FIN,
+ ADDON_FIN,
+ STRING,
+ INVALID
+};
+
+/*
+ * Current command
+ */
+struct {
+ char cmdline[CMDBUF];
+ size_t cmdlen;
+ int count;
+ int key;
+ char pattern[CMDBUF];
+ char addon;
+} cmd;
+
+/*
+ * Position of file arguments on argv[] to main()
+ */
+struct {
+ int first;
+ int current;
+ int last;
+} files;
+
+void (*oldint)(int); /* old SIGINT handler */
+void (*oldquit)(int); /* old SIGQUIT handler */
+void (*oldterm)(int); /* old SIGTERM handler */
+char *tty; /* result of ttyname(1) */
+char *progname; /* program name */
+unsigned ontty; /* whether running on tty device */
+unsigned exitstatus; /* exit status */
+int pagelen = 23; /* lines on a single screen page */
+int ttycols = 79; /* screen columns (starting at 0) */
+struct termios otio; /* old termios settings */
+int tinfostat = -1; /* terminfo routines initialized */
+int searchdisplay = TOP; /* matching line position */
+regex_t re; /* regular expression to search for */
+int remembered; /* have a remembered search string */
+int cflag; /* clear screen before each page */
+int eflag; /* suppress (EOF) */
+int fflag; /* do not split lines */
+int nflag; /* no newline for commands required */
+int rflag; /* "restricted" pg */
+int sflag; /* use standout mode */
+char *pstring = ":"; /* prompt string */
+char *searchfor; /* search pattern from argv[] */
+int havepagelen; /* page length is manually defined */
+long startline; /* start line from argv[] */
+int nextfile = 1; /* files to advance */
+jmp_buf jmpenv; /* jump from signal handlers */
+int canjump; /* jmpenv is valid */
+wchar_t wbuf[READBUF]; /* used in several widechar routines */
+
+const char *copyright =
+"@(#)pg 1.44 2/8/02. Copyright (c) 2000-2001 Gunnar Ritter. ";
+const char *helpscreen = "All rights reserved.\n\
+-------------------------------------------------------\n\
+ h this screen\n\
+ q or Q quit program\n\
+ <newline> next page\n\
+ f skip a page forward\n\
+ d or ^D next halfpage\n\
+ l next line\n\
+ $ last page\n\
+ /regex/ search forward for regex\n\
+ ?regex? or ^regex^ search backward for regex\n\
+ . or ^L redraw screen\n\
+ w or z set page size and go to next page\n\
+ s filename save current file to filename\n\
+ !command shell escape\n\
+ p go to previous file\n\
+ n go to next file\n\
+\n\
+Many commands accept preceding numbers, for example:\n\
++1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
+\n\
+See pg(1) for more information.\n\
+-------------------------------------------------------\n";
+
+#ifdef HAVE_fseeko
+ extern int fseeko(FILE *f, off_t off, int whence);
+ extern off_t ftello(FILE *f);
+ #define my_fseeko fseeko
+ #define my_ftello ftello
+#else
+ static int my_fseeko(FILE *f, off_t off, int whence) {
+ return fseek(f, (long) off, whence);
+ }
+ static off_t my_ftello(FILE *f) {
+ return (off_t) ftell(f);
+ }
+#endif
+
+#ifdef USE_SIGSET /* never defined */
+/* sigset and sigrelse are obsolete - use when POSIX stuff is unavailable */
+#define my_sigset sigset
+#define my_sigrelse sigrelse
+#else
+static int my_sigrelse(int sig) {
+ sigset_t sigs;
+
+ if (sigemptyset(&sigs) || sigaddset(&sigs, sig))
+ return -1;
+ return sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+}
+typedef void (*my_sighandler_t)(int);
+static my_sighandler_t my_sigset(int sig, my_sighandler_t disp) {
+ struct sigaction act, oact;
+
+ act.sa_handler = disp;
+ if (sigemptyset(&act.sa_mask))
+ return SIG_ERR;
+ act.sa_flags = 0;
+ if (sigaction(sig, &act, &oact))
+ return SIG_ERR;
+ if (my_sigrelse(sig))
+ return SIG_ERR;
+ return oact.sa_handler;
+}
+#endif
+
+/*
+ * Quit pg.
+ */
+static void
+quit(int status)
+{
+ exit(status < 0100 ? status : 077);
+}
+
+/*
+ * Memory allocator including check.
+ */
+static char *
+smalloc(size_t s)
+{
+ char *m = (char *)malloc(s);
+ if (m == NULL) {
+ write(2, "Out of memory\n", 14);
+ quit(++exitstatus);
+ }
+ return m;
+}
+
+/*
+ * Usage message and similar routines.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, _("%s: Usage: %s [-number] [-p string] [-cefnrs] "
+ "[+line] [+/pattern/] [files]\n"),
+ progname, progname);
+ quit(2);
+}
+
+static void
+needarg(char *s)
+{
+ fprintf(stderr, _("%s: option requires an argument -- %s\n"),
+ progname, s);
+ usage();
+}
+
+static void
+invopt(char *s)
+{
+ fprintf(stderr, _("%s: illegal option -- %s\n"), progname, s);
+ usage();
+}
+
+#ifdef ENABLE_WIDECHAR
+/*
+ * A mbstowcs()-alike function that transparently handles invalid sequences.
+ */
+static size_t
+xmbstowcs(wchar_t *pwcs, const char *s, size_t nwcs)
+{
+ size_t n = nwcs;
+ int c;
+
+ mbtowc(pwcs, NULL, MB_CUR_MAX);
+ while (*s && n) {
+ if ((c = mbtowc(pwcs, s, MB_CUR_MAX)) < 0) {
+ s++;
+ *pwcs = L'?';
+ } else
+ s += c;
+ pwcs++;
+ n--;
+ }
+ if (n)
+ *pwcs = L'\0';
+ mbtowc(pwcs, NULL, MB_CUR_MAX);
+ return nwcs - n;
+}
+#endif
+
+/*
+ * Helper function for tputs().
+ */
+static int
+outcap(int i)
+{
+ char c = i;
+ return write(1, &c, 1);
+}
+
+/*
+ * Write messages to terminal.
+ */
+static void
+mesg(char *message)
+{
+ if (ontty == 0)
+ return;
+ if (*message != '\n' && sflag)
+ vidputs(A_STANDOUT, outcap);
+ write(1, message, strlen(message));
+ if (*message != '\n' && sflag)
+ vidputs(A_NORMAL, outcap);
+}
+
+/*
+ * Get the window size.
+ */
+static void
+getwinsize(void)
+{
+ static int initialized, envlines, envcols, deflines, defcols;
+#ifdef TIOCGWINSZ
+ struct winsize winsz;
+ int badioctl;
+#endif
+ char *p;
+
+ if (initialized == 0) {
+ if ((p = getenv("LINES")) != NULL && *p != '\0')
+ if ((envlines = atoi(p)) < 0)
+ envlines = 0;
+ if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
+ if ((envcols = atoi(p)) < 0)
+ envcols = 0;
+ /* terminfo values. */
+ if (tinfostat != 1 || columns == 0)
+ defcols = 24;
+ else
+ defcols = columns;
+ if (tinfostat != 1 || lines == 0)
+ deflines = 80;
+ else
+ deflines = lines;
+ initialized = 1;
+ }
+#ifdef TIOCGWINSZ
+ badioctl = ioctl(1, TIOCGWINSZ, &winsz);
+#endif
+ if (envcols)
+ ttycols = envcols - 1;
+#ifdef TIOCGWINSZ
+ else if (!badioctl)
+ ttycols = winsz.ws_col - 1;
+#endif
+ else
+ ttycols = defcols - 1;
+ if (havepagelen == 0) {
+ if (envlines)
+ pagelen = envlines - 1;
+#ifdef TIOCGWINSZ
+ else if (!badioctl)
+ pagelen = winsz.ws_row - 1;
+#endif
+ else
+ pagelen = deflines - 1;
+ }
+}
+
+/*
+ * Message if skipping parts of files.
+ */
+static void
+skip(int direction)
+{
+ if (direction > 0)
+ mesg(_("...skipping forward\n"));
+ else
+ mesg(_("...skipping backward\n"));
+}
+
+/*
+ * Signal handler while reading from input file.
+ */
+static void
+sighandler(int signum)
+{
+ if (canjump && (signum == SIGINT || signum == SIGQUIT))
+ longjmp(jmpenv, signum);
+ tcsetattr(1, TCSADRAIN, &otio);
+ quit(exitstatus);
+}
+
+/*
+ * Check whether the requested file was specified on the command line.
+ */
+static int
+checkf(void)
+{
+ if (files.current + nextfile >= files.last) {
+ mesg(_("No next file"));
+ return 1;
+ }
+ if (files.current + nextfile < files.first) {
+ mesg(_("No previous file"));
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef ENABLE_WIDECHAR
+/*
+ * Return the last character that will fit on the line at col columns
+ * in case MB_CUR_MAX > 1.
+ */
+static char *
+endline_for_mb(unsigned col, char *s)
+{
+ unsigned pos = 0;
+ wchar_t *p = wbuf;
+ wchar_t *end;
+ size_t wl;
+ char *t = s;
+
+ if ((wl = xmbstowcs(wbuf, t, sizeof wbuf - 1)) == (size_t)-1)
+ return s + 1;
+ wbuf[wl] = L'\0';
+ while (*p != L'\0') {
+ switch (*p) {
+ /*
+ * Cursor left.
+ */
+ case L'\b':
+ if (pos > 0)
+ pos--;
+ break;
+ /*
+ * No cursor movement.
+ */
+ case L'\a':
+ break;
+ /*
+ * Special.
+ */
+ case L'\r':
+ pos = 0;
+ break;
+ case L'\n':
+ end = p + 1;
+ goto ended;
+ /*
+ * Cursor right.
+ */
+ case L'\t':
+ pos += TABSIZE - (pos % TABSIZE);
+ break;
+ default:
+ pos += wcwidth(*p);
+ }
+ if (pos > col) {
+ if (*p == L'\t')
+ p++;
+ else if (pos > col + 1)
+ /*
+ * wcwidth() found a character that
+ * has multiple columns. What happens
+ * now? Assume the terminal will print
+ * the entire character onto the next
+ * row.
+ */
+ p--;
+ if (*++p == L'\n')
+ p++;
+ end = p;
+ goto ended;
+ }
+ p++;
+ }
+ end = p;
+ ended:
+ *end = L'\0';
+ p = wbuf;
+ if ((pos = wcstombs(NULL, p, READBUF)) == -1)
+ return s + 1;
+ return s + pos;
+}
+#endif
+
+/*
+ * Return the last character that will fit on the line at col columns.
+ */
+static char *
+endline(unsigned col, char *s)
+{
+ unsigned pos = 0;
+ char *t = s;
+
+#ifdef ENABLE_WIDECHAR
+ if (MB_CUR_MAX > 1)
+ return endline_for_mb(col, s);
+#endif
+
+ while (*s != '\0') {
+ switch (*s) {
+ /*
+ * Cursor left.
+ */
+ case '\b':
+ if (pos > 0)
+ pos--;
+ break;
+ /*
+ * No cursor movement.
+ */
+ case '\a':
+ break;
+ /*
+ * Special.
+ */
+ case '\r':
+ pos = 0;
+ break;
+ case '\n':
+ t = s + 1;
+ goto cend;
+ /*
+ * Cursor right.
+ */
+ case '\t':
+ pos += TABSIZE - (pos % TABSIZE);
+ break;
+ default:
+ pos++;
+ }
+ if (pos > col) {
+ if (*s == '\t')
+ s++;
+ if (*++s == '\n')
+ s++;
+ t = s;
+ goto cend;
+ }
+ s++;
+ }
+ t = s;
+ cend:
+ return t;
+}
+
+/*
+ * Clear the current line on the terminal's screen.
+ */
+static void
+cline(void)
+{
+ char *buf = (char *)smalloc(ttycols + 2);
+ memset(buf, ' ', ttycols + 2);
+ buf[0] = '\r';
+ buf[ttycols + 1] = '\r';
+ write(1, buf, ttycols + 2);
+ free(buf);
+}
+
+/*
+ * Evaluate a command character's semantics.
+ */
+static int
+getstate(int c)
+{
+ switch (c) {
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0':
+ case '\0':
+ return COUNT;
+ case '-': case '+':
+ return SIGN;
+ case 'l': case 'd': case '\004': case 'f': case 'z':
+ case '.': case '\014': case '$': case 'n': case 'p':
+ case 'w': case 'h': case 'q': case 'Q':
+ return CMD_FIN;
+ case '/': case '?': case '^':
+ return SEARCH;
+ case 's': case '!':
+ return STRING;
+ case 'm': case 'b': case 't':
+ return ADDON_FIN;
+ default:
+#ifndef PGNOBELL
+ if (bell)
+ tputs(bell, 1, outcap);
+#endif /* PGNOBELL */
+ return INVALID;
+ }
+}
+
+/*
+ * Get the count and ignore last character of string.
+ */
+static int
+getcount(char *cmdstr)
+{
+ char *buf;
+ char *p;
+ int i;
+
+ if (*cmdstr == '\0')
+ return 1;
+ buf = (char *)smalloc(strlen(cmdstr) + 1);
+ strcpy(buf, cmdstr);
+ if (cmd.key != '\0') {
+ if (cmd.key == '/' || cmd.key == '?' || cmd.key == '^') {
+ if ((p = strchr(buf, cmd.key)) != NULL)
+ *p = '\0';
+ } else
+ *(buf + strlen(buf) - 1) = '\0';
+ }
+ if (*buf == '\0')
+ return 1;
+ if (buf[0] == '-' && buf[1] == '\0') {
+ i = -1;
+ } else {
+ if (*buf == '+')
+ i = atoi(buf + 1);
+ else
+ i = atoi(buf);
+ }
+ free(buf);
+ return i;
+}
+
+/*
+ * Read what the user writes at the prompt. This is tricky because
+ * we check for valid input.
+ */
+static void
+prompt(long long pageno)
+{
+ struct termios tio;
+ char key;
+ int state = COUNT;
+ int escape = 0;
+ char b[LINE_MAX], *p;
+
+ if (pageno != -1) {
+ if ((p = strstr(pstring, "%d")) == NULL) {
+ mesg(pstring);
+ } else {
+ strcpy(b, pstring);
+ sprintf(b + (p - pstring), "%lld", pageno);
+ strcat(b, p + 2);
+ mesg(b);
+ }
+ }
+ cmd.key = cmd.addon = cmd.cmdline[0] = '\0';
+ cmd.cmdlen = 0;
+ tcgetattr(1, &tio);
+ tio.c_lflag &= ~(ICANON | ECHO);
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ tcsetattr(1, TCSADRAIN, &tio);
+ tcflush(1, TCIFLUSH);
+ for (;;) {
+ switch (read(1, &key, 1)) {
+ case 0: quit(0);
+ /*NOTREACHED*/
+ case -1: quit(1);
+ }
+ if (key == tio.c_cc[VERASE]) {
+ if (cmd.cmdlen) {
+ write(1, "\b \b", 3);
+ cmd.cmdline[--cmd.cmdlen] = '\0';
+ switch (state) {
+ case ADDON_FIN:
+ state = SEARCH_FIN;
+ cmd.addon = '\0';
+ break;
+ case CMD_FIN:
+ cmd.key = '\0';
+ state = COUNT;
+ break;
+ case SEARCH_FIN:
+ state = SEARCH;
+ /*FALLTHRU*/
+ case SEARCH:
+ if (cmd.cmdline[cmd.cmdlen - 1]
+ == '\\') {
+ escape = 1;
+ while(cmd.cmdline[cmd.cmdlen
+ - escape - 1]
+ == '\\') escape++;
+ escape %= 2;
+ }
+ else {
+ escape = 0;
+ if (strchr(cmd.cmdline, cmd.key)
+ == NULL) {
+ cmd.key = '\0';
+ state = COUNT;
+ }
+ }
+ break;
+ }
+ }
+ if (cmd.cmdlen == 0) {
+ state = COUNT;
+ cmd.key = '\0';
+ }
+ continue;
+ }
+ if (key == tio.c_cc[VKILL]) {
+ cline();
+ cmd.cmdlen = 0;
+ cmd.cmdline[0] = '\0';
+ state = COUNT;
+ cmd.key = '\0';
+ continue;
+ }
+ if (key == '\n' || (nflag && state == COUNT && key == ' '))
+ break;
+ if (cmd.cmdlen >= CMDBUF - 1)
+ continue;
+ switch (state) {
+ case STRING:
+ break;
+ case SEARCH:
+ if (!escape) {
+ if (key == cmd.key)
+ state = SEARCH_FIN;
+ if (key == '\\')
+ escape = 1;
+ } else
+ escape = 0;
+ break;
+ case SEARCH_FIN:
+ if (getstate(key) != ADDON_FIN)
+ continue;
+ state = ADDON_FIN;
+ cmd.addon = key;
+ switch (key) {
+ case 't':
+ searchdisplay = TOP;
+ break;
+ case 'm':
+ searchdisplay = MIDDLE;
+ break;
+ case 'b':
+ searchdisplay = BOTTOM;
+ break;
+ }
+ break;
+ case CMD_FIN:
+ case ADDON_FIN:
+ continue;
+ default:
+ state = getstate(key);
+ switch (state) {
+ case SIGN:
+ if (cmd.cmdlen != 0) {
+ state = INVALID;
+ continue;
+ }
+ state = COUNT;
+ /*FALLTHRU*/
+ case COUNT:
+ break;
+ case ADDON_FIN:
+ case INVALID:
+ continue;
+ default:
+ cmd.key = key;
+ }
+ }
+ write(1, &key, 1);
+ cmd.cmdline[cmd.cmdlen++] = key;
+ cmd.cmdline[cmd.cmdlen] = '\0';
+ if (nflag && state == CMD_FIN)
+ goto endprompt;
+ }
+endprompt:
+ tcsetattr(1, TCSADRAIN, &otio);
+ cline();
+ cmd.count = getcount(cmd.cmdline);
+}
+
+#ifdef ENABLE_WIDECHAR
+/*
+ * Remove backspace formatting, for searches
+ * in case MB_CUR_MAX > 1.
+ */
+static char *
+colb_for_mb(char *s)
+{
+ char *p = s;
+ wchar_t *wp, *wq;
+ size_t l = strlen(s), wl;
+ unsigned i;
+
+ if ((wl = xmbstowcs(wbuf, p, sizeof wbuf)) == (size_t)-1)
+ return s;
+ for (wp = wbuf, wq = wbuf, i = 0; *wp != L'\0' && i < wl;
+ wp++, wq++) {
+ if (*wp == L'\b') {
+ if (wq != wbuf)
+ wq -= 2;
+ else
+ wq--;
+ } else
+ *wq = *wp;
+ }
+ *wq = L'\0';
+ wp = wbuf;
+ wcstombs(s, wp, l + 1);
+
+ return s;
+}
+#endif
+
+/*
+ * Remove backspace formatting, for searches.
+ */
+static char *
+colb(char *s)
+{
+ char *p = s, *q;
+
+#ifdef ENABLE_WIDECHAR
+ if (MB_CUR_MAX > 1)
+ return colb_for_mb(s);
+#endif
+
+ for (q = s; *p != '\0'; p++, q++) {
+ if (*p == '\b') {
+ if (q != s)
+ q -= 2;
+ else
+ q--;
+ } else
+ *q = *p;
+ }
+ *q = '\0';
+
+ return s;
+}
+
+#ifdef ENABLE_WIDECHAR
+/*
+ * Convert nonprintable characters to spaces
+ * in case MB_CUR_MAX > 1.
+ */
+static void
+makeprint_for_mb(char *s, size_t l)
+{
+ char *t = s;
+ wchar_t *wp = wbuf;
+ size_t wl;
+
+ if ((wl = xmbstowcs(wbuf, t, sizeof wbuf)) == (size_t)-1)
+ return;
+ while (wl--) {
+ if (!iswprint(*wp) && *wp != L'\n' && *wp != L'\r'
+ && *wp != L'\b' && *wp != L'\t')
+ *wp = L'?';
+ wp++;
+ }
+ wp = wbuf;
+ wcstombs(s, wp, l);
+}
+#endif
+
+/*
+ * Convert nonprintable characters to spaces.
+ */
+static void
+makeprint(char *s, size_t l)
+{
+#ifdef ENABLE_WIDECHAR
+ if (MB_CUR_MAX > 1)
+ return makeprint_for_mb(s, l);
+#endif
+
+ while (l--) {
+ if (!isprint(cuc(*s)) && *s != '\n' && *s != '\r'
+ && *s != '\b' && *s != '\t')
+ *s = '?';
+ s++;
+ }
+}
+
+/*
+ * Strip backslash characters from the given string.
+ */
+static void
+striprs(char *s)
+{
+ char *p = s;
+
+ do {
+ if (*s == '\\') {
+ s++;
+ }
+ *p++ = *s;
+ } while (*s++ != '\0');
+}
+
+/*
+ * Extract the search pattern off the command line.
+ */
+static char *
+makepat(void)
+{
+ char *p;
+
+ if (cmd.addon == '\0')
+ p = cmd.cmdline + strlen(cmd.cmdline) - 1;
+ else
+ p = cmd.cmdline + strlen(cmd.cmdline) - 2;
+ if (*p == cmd.key)
+ *p = '\0';
+ else
+ *(p + 1) = '\0';
+ if ((p = strchr(cmd.cmdline, cmd.key)) != NULL) {
+ p++;
+ striprs(p);
+ }
+ return p;
+}
+
+/*
+ * Process errors that occurred in temporary file operations.
+ */
+static void
+tmperr(FILE *f, char *ftype)
+{
+ if (ferror(f))
+ fprintf(stderr, _("%s: Read error from %s file\n"),
+ progname, ftype);
+ else if (feof(f))
+ /*
+ * Most likely '\0' in input.
+ */
+ fprintf(stderr, _("%s: Unexpected EOF in %s file\n"),
+ progname, ftype);
+ else
+ fprintf(stderr, _("%s: Unknown error in %s file\n"),
+ progname, ftype);
+ quit(++exitstatus);
+}
+
+/*
+ * perror()-like, but showing the program's name.
+ */
+static void
+pgerror(int eno, char *string)
+{
+ fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno));
+}
+
+/*
+ * Read the file and respond to user input.
+ * Beware: long and ugly.
+ */
+static void
+pgfile(FILE *f, char *name)
+{
+ off_t pos, oldpos, fpos;
+ off_t line = 0, fline = 0, bline = 0, oldline = 0, eofline = 0;
+ int dline = 0;
+ /*
+ * These are the line counters:
+ * line the line desired to display
+ * fline the current line of the input file
+ * bline the current line of the file buffer
+ * oldline the line before a search was started
+ * eofline the last line of the file if it is already reached
+ * dline the line on the display
+ */
+ int search = 0;
+ unsigned searchcount = 0;
+ /*
+ * Advance to EOF immediately.
+ */
+ int seekeof = 0;
+ /*
+ * EOF has been reached by `line'.
+ */
+ int eof = 0;
+ /*
+ * f and fbuf refer to the same file.
+ */
+ int nobuf = 0;
+ int sig;
+ int rerror;
+ size_t sz;
+ char b[READBUF + 1];
+ char *p;
+ /*
+ * fbuf an exact copy of the input file as it gets read
+ * find index table for input, one entry per line
+ * save for the s command, to save to a file
+ */
+ FILE *fbuf, *find, *save;
+
+ /* silence compiler - it may warn about longjmp() */
+ CLOBBGRD(line);
+ CLOBBGRD(fline);
+ CLOBBGRD(bline);
+ CLOBBGRD(oldline);
+ CLOBBGRD(eofline);
+ CLOBBGRD(dline);
+ CLOBBGRD(ttycols);
+ CLOBBGRD(search);
+ CLOBBGRD(searchcount);
+ CLOBBGRD(seekeof);
+ CLOBBGRD(eof);
+ CLOBBGRD(fpos);
+ CLOBBGRD(nobuf);
+ CLOBBGRD(fbuf);
+
+ if (ontty == 0) {
+ /*
+ * Just copy stdin to stdout.
+ */
+ while ((sz = fread(b, sizeof *b, READBUF, f)) != 0)
+ write(1, b, sz);
+ if (ferror(f)) {
+ pgerror(errno, name);
+ exitstatus++;
+ }
+ return;
+ }
+ if ((fpos = my_fseeko(f, (off_t)0, SEEK_SET)) == -1)
+ fbuf = tmpfile();
+ else {
+ fbuf = f;
+ nobuf = 1;
+ }
+ find = tmpfile();
+ if (fbuf == NULL || find == NULL) {
+ fprintf(stderr, _("%s: Cannot create tempfile\n"), progname);
+ quit(++exitstatus);
+ }
+ if (searchfor) {
+ search = FORWARD;
+ oldline = 0;
+ searchcount = 1;
+ rerror = regcomp(&re, searchfor, REG_NOSUB | REG_NEWLINE);
+ if (rerror != 0) {
+ mesg(_("RE error: "));
+ regerror(rerror, &re, b, READBUF);
+ mesg(b);
+ goto newcmd;
+ }
+ remembered = 1;
+ }
+
+ for (line = startline; ; ) {
+ /*
+ * Get a line from input file or buffer.
+ */
+ if (line < bline) {
+ my_fseeko(find, line * sizeof pos, SEEK_SET);
+ if (fread(&pos, sizeof pos, 1, find) == 0)
+ tmperr(find, "index");
+ my_fseeko(find, (off_t)0, SEEK_END);
+ my_fseeko(fbuf, pos, SEEK_SET);
+ if (fgets(b, READBUF, fbuf) == NULL)
+ tmperr(fbuf, "buffer");
+ } else if (eofline == 0) {
+ my_fseeko(find, (off_t)0, SEEK_END);
+ do {
+ if (!nobuf)
+ my_fseeko(fbuf, (off_t)0, SEEK_END);
+ pos = my_ftello(fbuf);
+ if ((sig = setjmp(jmpenv)) != 0) {
+ /*
+ * We got a signal.
+ */
+ canjump = 0;
+ my_sigrelse(sig);
+ my_fseeko(fbuf, pos, SEEK_SET);
+ *b = '\0';
+ dline = pagelen;
+ break;
+ } else {
+ if (nobuf)
+ my_fseeko(f, fpos, SEEK_SET);
+ canjump = 1;
+ p = fgets(b, READBUF, f);
+ if (nobuf)
+ if ((fpos = my_ftello(f)) == -1)
+ pgerror(errno, name);
+ canjump = 0;
+ }
+ if (p == NULL || *b == '\0') {
+ if (ferror(f))
+ pgerror(errno, name);
+ eofline = fline;
+ eof = 1;
+ break;
+ } else {
+ if (!nobuf)
+ fputs(b, fbuf);
+ fwrite(&pos, sizeof pos, 1, find);
+ if (!fflag) {
+ oldpos = pos;
+ p = b;
+ while (*(p = endline(ttycols,
+ p))
+ != '\0') {
+ pos = oldpos + (p - b);
+ fwrite(&pos,
+ sizeof pos,
+ 1, find);
+ fline++;
+ bline++;
+ }
+ }
+ fline++;
+ }
+ } while (line > bline++);
+ } else {
+ /*
+ * eofline != 0
+ */
+ eof = 1;
+ }
+ if (search == FORWARD) {
+ if (eof) {
+ line = oldline;
+ search = searchcount = 0;
+ mesg(_("Pattern not found"));
+ eof = 0;
+ goto newcmd;
+ }
+ line++;
+ colb(b);
+ if (regexec(&re, b, 0, NULL, 0) == 0) {
+ searchcount--;
+ }
+ if (searchcount == 0) {
+ search = dline = 0;
+ switch (searchdisplay) {
+ case TOP:
+ line -= 1;
+ break;
+ case MIDDLE:
+ line -= pagelen / 2 + 1;
+ break;
+ case BOTTOM:
+ line -= pagelen;
+ break;
+ }
+ skip(1);
+ }
+ continue;
+ } else if (eof) { /*
+ * We are not searching.
+ */
+ line = bline;
+ } else if (*b != '\0') {
+ if (cflag && clear_screen) {
+ switch (dline) {
+ case 0:
+ tputs(clear_screen, 1, outcap);
+ dline = 0;
+ }
+ }
+ line++;
+ if (eofline && line == eofline)
+ eof = 1;
+ dline++;
+ if ((sig = setjmp(jmpenv)) != 0) {
+ /*
+ * We got a signal.
+ */
+ canjump = 0;
+ my_sigrelse(sig);
+ dline = pagelen;
+ } else {
+ p = endline(ttycols, b);
+ sz = p - b;
+ makeprint(b, sz);
+ canjump = 1;
+ write(1, b, sz);
+ canjump = 0;
+ }
+ }
+ if (dline >= pagelen || eof) {
+ /*
+ * Time for prompting!
+ */
+ if (eof && seekeof) {
+ eof = seekeof = 0;
+ if (line >= pagelen)
+ line -= pagelen;
+ else
+ line = 0;
+ dline = -1;
+ continue;
+ }
+newcmd:
+ if (eof) {
+ if (fline == 0 || eflag)
+ break;
+ mesg(_("(EOF)"));
+ }
+ prompt((line - 1) / pagelen + 1);
+ switch (cmd.key) {
+ case '/':
+ /*
+ * Search forward.
+ */
+ search = FORWARD;
+ oldline = line;
+ searchcount = cmd.count;
+ p = makepat();
+ if (p != NULL && *p) {
+ if (remembered == 1)
+ regfree(&re);
+ rerror = regcomp(&re, p,
+ REG_NOSUB | REG_NEWLINE);
+ if (rerror != 0) {
+ mesg(_("RE error: "));
+ sz = regerror(rerror, &re,
+ b, READBUF);
+ mesg(b);
+ goto newcmd;
+ }
+ remembered = 1;
+ } else if (remembered == 0) {
+ mesg(_("No remembered search string"));
+ goto newcmd;
+ }
+ continue;
+ case '?':
+ case '^':
+ /*
+ * Search backward.
+ */
+ search = BACKWARD;
+ oldline = line;
+ searchcount = cmd.count;
+ p = makepat();
+ if (p != NULL && *p) {
+ if (remembered == 1)
+ regfree(&re);
+ rerror = regcomp(&re, p,
+ REG_NOSUB | REG_NEWLINE);
+ if (rerror != 0) {
+ mesg("RE error: ");
+ regerror(rerror, &re,
+ b, READBUF);
+ mesg(b);
+ goto newcmd;
+ }
+ remembered = 1;
+ } else if (remembered == 0) {
+ mesg("No remembered search string");
+ goto newcmd;
+ }
+ line -= pagelen;
+ if (line <= 0)
+ goto notfound_bw;
+ while (line) {
+ my_fseeko(find, --line * sizeof pos,
+ SEEK_SET);
+ if(fread(&pos, sizeof pos, 1,find)==0)
+ tmperr(find, "index");
+ my_fseeko(find, (off_t)0, SEEK_END);
+ my_fseeko(fbuf, pos, SEEK_SET);
+ if (fgets(b, READBUF, fbuf) == NULL)
+ tmperr(fbuf, "buffer");
+ colb(b);
+ if (regexec(&re, b, 0, NULL, 0) == 0)
+ searchcount--;
+ if (searchcount == 0)
+ goto found_bw;
+ }
+notfound_bw:
+ line = oldline;
+ search = searchcount = 0;
+ mesg(_("Pattern not found"));
+ goto newcmd;
+found_bw:
+ eof = search = dline = 0;
+ skip(-1);
+ switch (searchdisplay) {
+ case TOP:
+ /* line -= 1; */
+ break;
+ case MIDDLE:
+ line -= pagelen / 2;
+ break;
+ case BOTTOM:
+ if (line != 0)
+ dline = -1;
+ line -= pagelen;
+ break;
+ }
+ if (line < 0)
+ line = 0;
+ continue;
+ case 's':
+ /*
+ * Save to file.
+ */
+ p = cmd.cmdline;
+ while (*++p == ' ');
+ if (*p == '\0')
+ goto newcmd;
+ save = fopen(p, "wb");
+ if (save == NULL) {
+ cmd.count = errno;
+ mesg(_("Cannot open "));
+ mesg(p);
+ mesg(": ");
+ mesg(strerror(cmd.count));
+ goto newcmd;
+ }
+ /*
+ * Advance to EOF.
+ */
+ my_fseeko(find, (off_t)0, SEEK_END);
+ for (;;) {
+ if (!nobuf)
+ my_fseeko(fbuf,(off_t)0,SEEK_END);
+ pos = my_ftello(fbuf);
+ if (fgets(b, READBUF, f) == NULL) {
+ eofline = fline;
+ break;
+ }
+ if (!nobuf)
+ fputs(b, fbuf);
+ fwrite(&pos, sizeof pos, 1, find);
+ if (!fflag) {
+ oldpos = pos;
+ p = b;
+ while (*(p = endline(ttycols,
+ p))
+ != '\0') {
+ pos = oldpos + (p - b);
+ fwrite(&pos,
+ sizeof pos,
+ 1, find);
+ fline++;
+ bline++;
+ }
+ }
+ fline++;
+ bline++;
+ }
+ my_fseeko(fbuf, (off_t)0, SEEK_SET);
+ while ((sz = fread(b, sizeof *b, READBUF,
+ fbuf)) != 0) {
+ /*
+ * No error check for compat.
+ */
+ fwrite(b, sizeof *b, sz, save);
+ }
+ fclose(save);
+ my_fseeko(fbuf, (off_t)0, SEEK_END);
+ mesg(_("saved"));
+ goto newcmd;
+ case 'l':
+ /*
+ * Next line.
+ */
+ if (*cmd.cmdline != 'l')
+ eof = 0;
+ if (cmd.count == 0)
+ cmd.count = 1; /* compat */
+ if (isdigit(cuc(*cmd.cmdline))) {
+ line = cmd.count - 2;
+ dline = 0;
+ } else {
+ if (cmd.count != 1) {
+ line += cmd.count - 1
+ - pagelen;
+ dline = -1;
+ skip(cmd.count);
+ }
+ /*
+ * Nothing to do if count==1.
+ */
+ }
+ break;
+ case 'd':
+ /*
+ * Half screen forward.
+ */
+ case '\004': /* ^D */
+ if (*cmd.cmdline != cmd.key)
+ eof = 0;
+ if (cmd.count == 0)
+ cmd.count = 1; /* compat */
+ line += (cmd.count * pagelen / 2)
+ - pagelen - 1;
+ dline = -1;
+ skip(cmd.count);
+ break;
+ case 'f':
+ /*
+ * Skip forward.
+ */
+ if (cmd.count <= 0)
+ cmd.count = 1; /* compat */
+ line += cmd.count * pagelen - 2;
+ if (eof)
+ line += 2;
+ if (*cmd.cmdline != 'f')
+ eof = 0;
+ else if (eof)
+ break;
+ if (eofline && line >= eofline)
+ line -= pagelen;
+ dline = -1;
+ skip(cmd.count);
+ break;
+ case '\0':
+ /*
+ * Just a number, or '-', or <newline>.
+ */
+ if (cmd.count == 0)
+ cmd.count = 1; /* compat */
+ if (isdigit(cuc(*cmd.cmdline)))
+ line = (cmd.count - 1) * pagelen - 2;
+ else
+ line += (cmd.count - 1)
+ * (pagelen - 1) - 2;
+ if (*cmd.cmdline != '\0')
+ eof = 0;
+ if (cmd.count != 1) {
+ skip(cmd.count);
+ dline = -1;
+ } else {
+ dline = 1;
+ line += 2;
+ }
+ break;
+ case '$':
+ /*
+ * Advance to EOF.
+ */
+ if (!eof)
+ skip(1);
+ eof = 0;
+ line = LONG_MAX;
+ seekeof = 1;
+ dline = -1;
+ break;
+ case '.':
+ case '\014': /* ^L */
+ /*
+ * Repaint screen.
+ */
+ eof = 0;
+ if (line >= pagelen)
+ line -= pagelen;
+ else
+ line = 0;
+ dline = 0;
+ break;
+ case '!':
+ /*
+ * Shell escape.
+ */
+ if (rflag) {
+ mesg(progname);
+ mesg(_(": !command not allowed in "
+ "rflag mode.\n"));
+ } else {
+ pid_t cpid;
+
+ write(1, cmd.cmdline,
+ strlen(cmd.cmdline));
+ write(1, "\n", 1);
+ my_sigset(SIGINT, SIG_IGN);
+ my_sigset(SIGQUIT, SIG_IGN);
+ switch (cpid = fork()) {
+ case 0:
+ p = getenv("SHELL");
+ if (p == NULL) p = "/bin/sh";
+ if (!nobuf)
+ fclose(fbuf);
+ fclose(find);
+ if (isatty(0) == 0) {
+ close(0);
+ open(tty, O_RDONLY);
+ } else {
+ fclose(f);
+ }
+ my_sigset(SIGINT, oldint);
+ my_sigset(SIGQUIT, oldquit);
+ my_sigset(SIGTERM, oldterm);
+ execl(p, p, "-c",
+ cmd.cmdline + 1, NULL);
+ pgerror(errno, p);
+ _exit(0177);
+ /*NOTREACHED*/
+ case -1:
+ mesg(_("fork() failed, "
+ "try again later\n"));
+ break;
+ default:
+ while (wait(NULL) != cpid);
+ }
+ my_sigset(SIGINT, sighandler);
+ my_sigset(SIGQUIT, sighandler);
+ mesg("!\n");
+ }
+ goto newcmd;
+ case 'h':
+ /*
+ * Help!
+ */
+ write(1, copyright + 4, strlen(copyright + 4));
+ write(1, helpscreen, strlen(helpscreen));
+ goto newcmd;
+ case 'n':
+ /*
+ * Next file.
+ */
+ if (cmd.count == 0)
+ cmd.count = 1;
+ nextfile = cmd.count;
+ if (checkf()) {
+ nextfile = 1;
+ goto newcmd;
+ }
+ eof = 1;
+ break;
+ case 'p':
+ /*
+ * Previous file.
+ */
+ if (cmd.count == 0)
+ cmd.count = 1;
+ nextfile = 0 - cmd.count;
+ if (checkf()) {
+ nextfile = 1;
+ goto newcmd;
+ }
+ eof = 1;
+ break;
+ case 'q':
+ case 'Q':
+ /*
+ * Exit pg.
+ */
+ quit(exitstatus);
+ /*NOTREACHED*/
+ case 'w':
+ case 'z':
+ /*
+ * Set window size.
+ */
+ if (cmd.count < 0)
+ cmd.count = 0;
+ if (*cmd.cmdline != cmd.key)
+ pagelen = ++cmd.count;
+ dline = 1;
+ break;
+ }
+ if (line <= 0) {
+ line = 0;
+ dline = 0;
+ }
+ if (cflag && dline == 1) {
+ dline = 0;
+ line--;
+ }
+ }
+ if (eof)
+ break;
+ }
+ fclose(find);
+ if (!nobuf)
+ fclose(fbuf);
+}
+
+int
+main(int argc, char **argv)
+{
+ int arg, i;
+ char *p;
+ FILE *input;
+
+ progname = basename(argv[0]);
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ if (tcgetattr(1, &otio) == 0) {
+ ontty = 1;
+ oldint = my_sigset(SIGINT, sighandler);
+ oldquit = my_sigset(SIGQUIT, sighandler);
+ oldterm = my_sigset(SIGTERM, sighandler);
+ setlocale(LC_CTYPE, "");
+ setlocale(LC_COLLATE, "");
+ tty = ttyname(1);
+ setupterm(NULL, 1, &tinfostat);
+ getwinsize();
+ helpscreen = _(helpscreen);
+ }
+ for (arg = 1; argv[arg]; arg++) {
+ if (*argv[arg] == '+')
+ continue;
+ if (*argv[arg] != '-' || argv[arg][1] == '\0')
+ break;
+ argc--;
+ for (i = 1; argv[arg][i]; i++) {
+ switch (argv[arg][i]) {
+ case '-':
+ if (i != 1 || argv[arg][i + 1])
+ invopt(&argv[arg][i]);
+ goto endargs;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0':
+ pagelen = atoi(argv[arg] + i);
+ havepagelen = 1;
+ goto nextarg;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'p':
+ if (argv[arg][i + 1]) {
+ pstring = &argv[arg][i + 1];
+ } else if (argv[++arg]) {
+ --argc;
+ pstring = argv[arg];
+ } else
+ needarg("-p");
+ goto nextarg;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ invopt(&argv[arg][i]);
+ }
+ }
+nextarg:
+ ;
+ }
+endargs:
+ for (arg = 1; argv[arg]; arg++) {
+ if (*argv[arg] == '-') {
+ if (argv[arg][1] == '-') {
+ arg++;
+ break;
+ }
+ if (argv[arg][1] == '\0')
+ break;
+ if (argv[arg][1] == 'p' && argv[arg][2] == '\0')
+ arg++;
+ continue;
+ }
+ if (*argv[arg] != '+')
+ break;
+ argc--;
+ switch (*(argv[arg] + 1)) {
+ case '\0':
+ needarg("+");
+ /*NOTREACHED*/
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0':
+ startline = atoi(argv[arg] + 1);
+ break;
+ case '/':
+ searchfor = argv[arg] + 2;
+ if (*searchfor == '\0')
+ needarg("+/");
+ p = searchfor + strlen(searchfor) - 1;
+ if (*p == '/') *p = '\0';
+ if (*searchfor == '\0')
+ needarg("+/");
+ break;
+ default:
+ invopt(argv[arg]);
+ }
+ }
+ if (argc == 1) {
+ pgfile(stdin, "stdin");
+ } else {
+ files.first = arg;
+ files.last = arg + argc - 1;
+ for ( ; argv[arg]; arg += nextfile) {
+ nextfile = 1;
+ files.current = arg;
+ if (argc > 2) {
+ static int firsttime;
+ firsttime++;
+ if (firsttime > 1) {
+ mesg(_("(Next file: "));
+ mesg(argv[arg]);
+ mesg(")");
+newfile:
+ if (ontty) {
+ prompt(-1);
+ switch(cmd.key) {
+ case 'n':
+ /*
+ * Next file.
+ */
+ if (cmd.count == 0)
+ cmd.count = 1;
+ nextfile = cmd.count;
+ if (checkf()) {
+ nextfile = 1;
+ mesg(":");
+ goto newfile;
+ }
+ continue;
+ case 'p':
+ /*
+ * Previous file.
+ */
+ if (cmd.count == 0)
+ cmd.count = 1;
+ nextfile = 0 - cmd.count;
+ if (checkf()) {
+ nextfile = 1;
+ mesg(":");
+ goto newfile;
+ }
+ continue;
+ case 'q':
+ case 'Q':
+ quit(exitstatus);
+ }
+ } else mesg("\n");
+ }
+ }
+ if (strcmp(argv[arg], "-") == 0)
+ input = stdin;
+ else {
+ input = fopen(argv[arg], "r");
+ if (input == NULL) {
+ pgerror(errno, argv[arg]);
+ exitstatus++;
+ continue;
+ }
+ }
+ if (ontty == 0 && argc > 2) {
+ /*
+ * Use the prefix as specified by SUSv2.
+ */
+ write(1, "::::::::::::::\n", 15);
+ write(1, argv[arg], strlen(argv[arg]));
+ write(1, "\n::::::::::::::\n", 16);
+ }
+ pgfile(input, argv[arg]);
+ if (input != stdin)
+ fclose(input);
+ }
+ }
+ quit(exitstatus);
+ /*NOTREACHED*/
+ return 0;
+}