summaryrefslogtreecommitdiff
path: root/text-utils
diff options
context:
space:
mode:
Diffstat (limited to 'text-utils')
-rw-r--r--text-utils/Makemodule.am81
-rw-r--r--text-utils/col.1161
-rw-r--r--text-utils/col.c576
-rw-r--r--text-utils/colcrt.1102
-rw-r--r--text-utils/colcrt.c324
-rw-r--r--text-utils/colrm.175
-rw-r--r--text-utils/colrm.c194
-rw-r--r--text-utils/column.177
-rw-r--r--text-utils/column.c417
-rw-r--r--text-utils/conv.c109
-rw-r--r--text-utils/display.c348
-rw-r--r--text-utils/hexdump.1324
-rw-r--r--text-utils/hexdump.c82
-rw-r--r--text-utils/hexdump.h93
-rw-r--r--text-utils/hexsyntax.c142
-rw-r--r--text-utils/line.117
-rw-r--r--text-utils/line.c45
-rw-r--r--text-utils/more.1249
-rw-r--r--text-utils/more.c2197
-rw-r--r--text-utils/parse.c495
-rw-r--r--text-utils/pg.1232
-rw-r--r--text-utils/pg.c1783
-rw-r--r--text-utils/rev.160
-rw-r--r--text-utils/rev.c177
-rw-r--r--text-utils/tailf.168
-rw-r--r--text-utils/tailf.c292
-rw-r--r--text-utils/ul.1113
-rw-r--r--text-utils/ul.c672
28 files changed, 9505 insertions, 0 deletions
diff --git a/text-utils/Makemodule.am b/text-utils/Makemodule.am
new file mode 100644
index 0000000..23185ea
--- /dev/null
+++ b/text-utils/Makemodule.am
@@ -0,0 +1,81 @@
+
+usrbin_exec_PROGRAMS += \
+ col \
+ colcrt \
+ colrm \
+ column \
+ hexdump \
+ rev \
+ tailf
+
+dist_man_MANS += \
+ text-utils/col.1 \
+ text-utils/colcrt.1 \
+ text-utils/colrm.1 \
+ text-utils/column.1 \
+ text-utils/hexdump.1 \
+ text-utils/rev.1 \
+ text-utils/tailf.1
+
+col_SOURCES = text-utils/col.c
+col_LDADD = $(LDADD) libcommon.la
+
+colcrt_SOURCES = text-utils/colcrt.c
+
+colrm_SOURCES = text-utils/colrm.c
+colrm_LDADD = $(LDADD) libcommon.la
+
+column_SOURCES = text-utils/column.c
+column_LDADD = $(LDADD) libcommon.la
+
+hexdump_SOURCES = \
+ text-utils/conv.c \
+ text-utils/display.c \
+ text-utils/hexdump.c \
+ text-utils/hexdump.h \
+ text-utils/hexsyntax.c \
+ text-utils/parse.c
+hexdump_LDADD = $(LDADD) libcommon.la
+
+rev_SOURCES = text-utils/rev.c
+
+tailf_SOURCES = text-utils/tailf.c
+tailf_LDADD = $(LDADD) libcommon.la
+
+if BUILD_LINE
+usrbin_exec_PROGRAMS += line
+line_SOURCES = text-utils/line.c
+dist_man_MANS += text-utils/line.1
+endif
+
+# TODO: add BUILD_{MORE,UL,PG} ./configure.am
+if HAVE_NCURSES
+bin_PROGRAMS += more
+more_SOURCES = text-utils/more.c
+
+usrbin_exec_PROGRAMS += ul pg
+ul_SOURCES = text-utils/ul.c
+pg_SOURCES = text-utils/pg.c
+
+if HAVE_TINFO
+more_LDADD = $(LDADD) -ltinfo
+pg_LDADD = $(LDADD) -ltinfo @NCURSES_LIBS@
+ul_LDADD = $(LDADD) -ltinfo
+else
+more_LDADD = $(LDADD) @NCURSES_LIBS@
+pg_LDADD = $(LDADD) @NCURSES_LIBS@
+ul_LDADD = $(LDADD) @NCURSES_LIBS@
+endif
+
+dist_man_MANS += \
+ text-utils/ul.1 \
+ text-utils/more.1 \
+ text-utils/pg.1
+else
+if HAVE_TERMCAP
+bin_PROGRAMS += more
+more_SOURCES = text-utils/more.c
+more_LDADD = $(LDADD) -ltermcap
+dist_man_MANS += text-utils/more.1
+endif
+endif # !HAVE_NCURSES
diff --git a/text-utils/col.1 b/text-utils/col.1
new file mode 100644
index 0000000..9efc72f
--- /dev/null
+++ b/text-utils/col.1
@@ -0,0 +1,161 @@
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Michael Rendell.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)col.1 6.8 (Berkeley) 6/17/91
+.\"
+.TH COL "1" "September 2011" "util-linux" "User Commands"
+.SH COL
+col - filter reverse line feeds from input
+.SH SYNOPSIS
+.B col
+[options]
+.SH DESCRIPTION
+.B col
+filters out reverse (and half-reverse) line feeds so the output is in the
+correct order with only forward and half-forward line feeds, and replaces
+white-space characters with tabs where possible. This can be useful in
+processing the output of
+.BR nroff (1)
+and
+.BR tbl (1).
+.PP
+.B col
+reads from standard input and writes to standard output.
+.SH OPTIONS
+.TP
+\fB\-b\fR, \fB\-\-no-backspaces\fR
+Do not output any backspaces, printing only the last character written to
+each column position.
+.TP
+\fB\-f\fR, \fB\-\-fine\fR
+Forward half line feeds are permitted
+.I fine
+mode. Normally characters printed on a half-line boundary are printed on the
+following line.
+.TP
+\fB\-p\fR, \fB\-\-pass\fR
+Force unknown control sequences to be passed through unchanged. Normally,
+.B col
+will filter out any control sequences from the input other than those
+recognized and interpreted by itself, which are listed below.
+.TP
+\fB\-h\fR, \fB\-\-tabs\fR
+Output tabs instead of multiple spaces.
+.TP
+\fB\-x\fR, \fB\-\-spaces\fR
+Output multiple spaces instead of tabs.
+.It Fl l, Fl Fl lines Ar num
+.TP
+\fB\-l\fR, \fB\-\-lines\fR \fInumber\fR
+Buffer at least
+.I number
+lines in memory. By default, 128 lines are buffered.
+.It Fl V, Fl Fl version
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-H\fR, \fB\-\-help\fR
+Output help and exit.
+.SH NOTES
+The control sequences for carriage motion that
+.B col
+understands and their decimal values are listed in the following table:
+.PP
+.RS
+.PD 0
+.TP 18
+.B ESC\-7
+reverse line feed (escape then 7)
+.TP
+.B ESC\-8
+half reverse line feed (escape then 8)
+.TP
+.B ESC\-9
+half forward line feed (escape then 9)
+.TP
+.B backspace
+moves back one column (8); ignored in the first column
+.TP
+.B newline
+forward line feed (10); also does carriage return
+.TP
+.B carriage return
+(13)
+.TP
+.B shift in
+shift to normal character set (15)
+.TP
+.B shift out
+shift to alternate character set (14)
+.TP
+.B space
+moves forward one column (32)
+.TP
+.B tab
+moves forward to next tab stop (9)
+.TP
+.B vertical tab
+reverse line feed (11)
+.PD
+.RE
+.PP
+All unrecognized control characters and escape sequences are discarded.
+.PP
+.B col
+keeps track of the character set as characters are read and makes sure the
+character set is correct when they are output.
+.PP
+If the input attempts to back up to the last flushed line,
+.B col
+will display a warning message.
+.SH SEE ALSO
+.BR expand (1),
+.BR nroff (1),
+.BR tbl (1)
+.SH STANDARDS
+The
+.B col
+utility conforms to the Single UNIX Specification, Version 2. The
+.B \-l
+option is an extension to the standard.
+.SH HISTORY
+A
+.B col
+command appeared in Version 6 AT&T UNIX.
+.SH AVAILABILITY
+The col command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/col.c b/text-utils/col.c
new file mode 100644
index 0000000..57fa475
--- /dev/null
+++ b/text-utils/col.c
@@ -0,0 +1,576 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of the Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Wed Jun 22 22:15:41 1994, faith@cs.unc.edu: Added internationalization
+ * patches from Andries.Brouwer@cwi.nl
+ * Wed Sep 14 22:31:17 1994: patches from Carl Christofferson
+ * (cchris@connected.com)
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#define BS '\b' /* backspace */
+#define TAB '\t' /* tab */
+#define SPACE ' ' /* space */
+#define NL '\n' /* newline */
+#define CR '\r' /* carriage return */
+#define ESC '\033' /* escape */
+#define SI '\017' /* shift in to normal character set */
+#define SO '\016' /* shift out to alternate character set */
+#define VT '\013' /* vertical tab (aka reverse line feed) */
+#define RLF '\007' /* ESC-07 reverse line feed */
+#define RHLF '\010' /* ESC-010 reverse half-line feed */
+#define FHLF '\011' /* ESC-011 forward half-line feed */
+
+/* build up at least this many lines before flushing them out */
+#define BUFFER_MARGIN 32
+
+typedef char CSET;
+
+typedef struct char_str {
+#define CS_NORMAL 1
+#define CS_ALTERNATE 2
+ short c_column; /* column character is in */
+ CSET c_set; /* character set (currently only 2) */
+ wchar_t c_char; /* character in question */
+ int c_width; /* character width */
+} CHAR;
+
+typedef struct line_str LINE;
+struct line_str {
+ CHAR *l_line; /* characters on the line */
+ LINE *l_prev; /* previous line */
+ LINE *l_next; /* next line */
+ int l_lsize; /* allocated sizeof l_line */
+ int l_line_len; /* strlen(l_line) */
+ int l_needs_sort; /* set if chars went in out of order */
+ int l_max_col; /* max column in the line */
+};
+
+void free_line(LINE *l);
+void flush_line(LINE *l);
+void flush_lines(int);
+void flush_blanks(void);
+LINE *alloc_line(void);
+
+CSET last_set; /* char_set of last char printed */
+LINE *lines;
+int compress_spaces; /* if doing space -> tab conversion */
+int fine; /* if `fine' resolution (half lines) */
+unsigned max_bufd_lines; /* max # lines to keep in memory */
+int nblank_lines; /* # blanks after last flushed line */
+int no_backspaces; /* if not to output any backspaces */
+int pass_unknown_seqs; /* whether to pass unknown control sequences */
+
+#define PUTC(ch) \
+ if (putwchar(ch) == WEOF) \
+ wrerr();
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out, _(
+ "\nUsage:\n"
+ " %s [options]\n"), program_invocation_short_name);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -b, --no-backspaces do not output backspaces\n"
+ " -f, --fine permit forward half line feeds\n"
+ " -p, --pass pass unknown control sequences\n"
+ " -h, --tabs convert spaces to tabs\n"
+ " -x, --spaces convert tabs to spaces\n"
+ " -l, --lines NUM buffer at least NUM lines\n"
+ " -V, --version output version information and exit\n"
+ " -H, --help display this help and exit\n\n"));
+
+ fprintf(out, _(
+ "%s reads from standard input and writes to standard output\n\n"),
+ program_invocation_short_name);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void __attribute__((__noreturn__)) wrerr()
+{
+ errx(EXIT_FAILURE, _("write error."));
+}
+
+int main(int argc, char **argv)
+{
+ register wint_t ch;
+ CHAR *c;
+ CSET cur_set; /* current character set */
+ LINE *l; /* current line */
+ int extra_lines; /* # of lines above first line */
+ int cur_col; /* current column */
+ int cur_line; /* line number of current position */
+ int max_line; /* max value of cur_line */
+ int this_line; /* line l points to */
+ int nflushd_lines; /* number of lines that were flushed */
+ int adjust, opt, warned;
+ int ret = EXIT_SUCCESS;
+
+ static const struct option longopts[] = {
+ { "no-backspaces", no_argument, 0, 'b' },
+ { "fine", no_argument, 0, 'f' },
+ { "pass", no_argument, 0, 'p' },
+ { "tabs", no_argument, 0, 'h' },
+ { "spaces", no_argument, 0, 'x' },
+ { "lines", required_argument, 0, 'l' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'H' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ max_bufd_lines = 128 * 2;
+ compress_spaces = 1; /* compress spaces into tabs */
+ pass_unknown_seqs = 0; /* remove unknown escape sequences */
+
+ while ((opt = getopt_long(argc, argv, "bfhl:pxVH", longopts, NULL)) != -1)
+ switch (opt) {
+ case 'b': /* do not output backspaces */
+ no_backspaces = 1;
+ break;
+ case 'f': /* allow half forward line feeds */
+ fine = 1;
+ break;
+ case 'h': /* compress spaces into tabs */
+ compress_spaces = 1;
+ break;
+ case 'l':
+ /*
+ * Buffered line count, which is a value in half
+ * lines e.g. twice the amount specified.
+ */
+ max_bufd_lines = strtou32_or_err(optarg, _("bad -l argument")) * 2;
+ break;
+ case 'p':
+ pass_unknown_seqs = 1;
+ break;
+ case 'x': /* do not compress spaces into tabs */
+ compress_spaces = 0;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'H':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (optind != argc)
+ usage(stderr);
+
+ adjust = cur_col = extra_lines = warned = 0;
+ cur_line = max_line = nflushd_lines = this_line = 0;
+ cur_set = last_set = CS_NORMAL;
+ lines = l = alloc_line();
+
+ while (feof(stdin) == 0) {
+ errno = 0;
+ if ((ch = getwchar()) == WEOF) {
+ if (errno == EILSEQ) {
+ warn(NULL);
+ ret = EXIT_FAILURE;
+ }
+ break;
+ }
+ if (!iswgraph(ch)) {
+ switch (ch) {
+ case BS: /* can't go back further */
+ if (cur_col == 0)
+ continue;
+ --cur_col;
+ continue;
+ case CR:
+ cur_col = 0;
+ continue;
+ case ESC: /* just ignore EOF */
+ switch(getwchar()) {
+ case RLF:
+ cur_line -= 2;
+ break;
+ case RHLF:
+ cur_line--;
+ break;
+ case FHLF:
+ cur_line++;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ }
+ continue;
+ case NL:
+ cur_line += 2;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ cur_col = 0;
+ continue;
+ case SPACE:
+ ++cur_col;
+ continue;
+ case SI:
+ cur_set = CS_NORMAL;
+ continue;
+ case SO:
+ cur_set = CS_ALTERNATE;
+ continue;
+ case TAB: /* adjust column */
+ cur_col |= 7;
+ ++cur_col;
+ continue;
+ case VT:
+ cur_line -= 2;
+ continue;
+ }
+ if (iswspace(ch)) {
+ if (wcwidth(ch) > 0)
+ cur_col += wcwidth(ch);
+ continue;
+ }
+ if (!pass_unknown_seqs)
+ continue;
+ }
+
+ /* Must stuff ch in a line - are we at the right one? */
+ if (cur_line != this_line - adjust) {
+ LINE *lnew;
+ int nmove;
+
+ adjust = 0;
+ nmove = cur_line - this_line;
+ if (!fine) {
+ /* round up to next line */
+ if (cur_line & 1) {
+ adjust = 1;
+ nmove++;
+ }
+ }
+ if (nmove < 0) {
+ for (; nmove < 0 && l->l_prev; nmove++)
+ l = l->l_prev;
+ if (nmove) {
+ if (nflushd_lines == 0) {
+ /*
+ * Allow backup past first
+ * line if nothing has been
+ * flushed yet.
+ */
+ for (; nmove < 0; nmove++) {
+ lnew = alloc_line();
+ l->l_prev = lnew;
+ lnew->l_next = l;
+ l = lines = lnew;
+ extra_lines++;
+ }
+ } else {
+ if (!warned++)
+ warnx(
+ _("warning: can't back up %s."), cur_line < 0 ?
+ _("past first line") : _("-- line already flushed"));
+ cur_line -= nmove;
+ }
+ }
+ } else {
+ /* may need to allocate here */
+ for (; nmove > 0 && l->l_next; nmove--)
+ l = l->l_next;
+ for (; nmove > 0; nmove--) {
+ lnew = alloc_line();
+ lnew->l_prev = l;
+ l->l_next = lnew;
+ l = lnew;
+ }
+ }
+ this_line = cur_line + adjust;
+ nmove = this_line - nflushd_lines;
+ if (nmove > 0
+ && (unsigned) nmove >= max_bufd_lines + BUFFER_MARGIN) {
+ nflushd_lines += nmove - max_bufd_lines;
+ flush_lines(nmove - max_bufd_lines);
+ }
+ }
+ /* grow line's buffer? */
+ if (l->l_line_len + 1 >= l->l_lsize) {
+ int need;
+
+ need = l->l_lsize ? l->l_lsize * 2 : 90;
+ l->l_line = (CHAR *)xrealloc((void *) l->l_line,
+ (unsigned) need * sizeof(CHAR));
+ l->l_lsize = need;
+ }
+ c = &l->l_line[l->l_line_len++];
+ c->c_char = ch;
+ c->c_set = cur_set;
+ c->c_column = cur_col;
+ c->c_width = wcwidth(ch);
+ /*
+ * If things are put in out of order, they will need sorting
+ * when it is flushed.
+ */
+ if (cur_col < l->l_max_col)
+ l->l_needs_sort = 1;
+ else
+ l->l_max_col = cur_col;
+ if (c->c_width > 0)
+ cur_col += c->c_width;
+ }
+ /* goto the last line that had a character on it */
+ for (; l->l_next; l = l->l_next)
+ this_line++;
+ flush_lines(this_line - nflushd_lines + extra_lines + 1);
+
+ /* make sure we leave things in a sane state */
+ if (last_set != CS_NORMAL)
+ PUTC('\017');
+
+ /* flush out the last few blank lines */
+ nblank_lines = max_line - this_line;
+ if (max_line & 1)
+ nblank_lines++;
+ else if (!nblank_lines)
+ /* missing a \n on the last line? */
+ nblank_lines = 2;
+ flush_blanks();
+ return ret;
+}
+
+void flush_lines(int nflush)
+{
+ LINE *l;
+
+ while (--nflush >= 0) {
+ l = lines;
+ lines = l->l_next;
+ if (l->l_line) {
+ flush_blanks();
+ flush_line(l);
+ }
+ nblank_lines++;
+ if (l->l_line)
+ free((void *)l->l_line);
+ free_line(l);
+ }
+ if (lines)
+ lines->l_prev = NULL;
+}
+
+/*
+ * Print a number of newline/half newlines. If fine flag is set, nblank_lines
+ * is the number of half line feeds, otherwise it is the number of whole line
+ * feeds.
+ */
+void flush_blanks()
+{
+ int half, i, nb;
+
+ half = 0;
+ nb = nblank_lines;
+ if (nb & 1) {
+ if (fine)
+ half = 1;
+ else
+ nb++;
+ }
+ nb /= 2;
+ for (i = nb; --i >= 0;)
+ PUTC('\n');
+ if (half) {
+ PUTC('\033');
+ PUTC('9');
+ if (!nb)
+ PUTC('\r');
+ }
+ nblank_lines = 0;
+}
+
+/*
+ * Write a line to stdout taking care of space to tab conversion (-h flag)
+ * and character set shifts.
+ */
+void flush_line(LINE *l)
+{
+ CHAR *c, *endc;
+ int nchars, last_col, this_col;
+
+ last_col = 0;
+ nchars = l->l_line_len;
+
+ if (l->l_needs_sort) {
+ static CHAR *sorted;
+ static int count_size, *count, i, save, sorted_size, tot;
+
+ /*
+ * Do an O(n) sort on l->l_line by column being careful to
+ * preserve the order of characters in the same column.
+ */
+ if (l->l_lsize > sorted_size) {
+ sorted_size = l->l_lsize;
+ sorted = (CHAR *)xrealloc((void *)sorted,
+ (unsigned)sizeof(CHAR) * sorted_size);
+ }
+ if (l->l_max_col >= count_size) {
+ count_size = l->l_max_col + 1;
+ count = (int *)xrealloc((void *)count,
+ (unsigned)sizeof(int) * count_size);
+ }
+ memset(count, 0, sizeof(int) * l->l_max_col + 1);
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ count[c->c_column]++;
+
+ /*
+ * calculate running total (shifted down by 1) to use as
+ * indices into new line.
+ */
+ for (tot = 0, i = 0; i <= l->l_max_col; i++) {
+ save = count[i];
+ count[i] = tot;
+ tot += save;
+ }
+
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ sorted[count[c->c_column]++] = *c;
+ c = sorted;
+ } else
+ c = l->l_line;
+ while (nchars > 0) {
+ this_col = c->c_column;
+ endc = c;
+ do {
+ ++endc;
+ } while (--nchars > 0 && this_col == endc->c_column);
+
+ /* if -b only print last character */
+ if (no_backspaces) {
+ c = endc - 1;
+ if (nchars > 0 &&
+ this_col + c->c_width > endc->c_column)
+ continue;
+ }
+
+ if (this_col > last_col) {
+ int nspace = this_col - last_col;
+
+ if (compress_spaces && nspace > 1) {
+ int ntabs;
+
+ ntabs = this_col / 8 - last_col / 8;
+ if (ntabs > 0) {
+ nspace = this_col & 7;
+ while (--ntabs >= 0)
+ PUTC('\t');
+ }
+ }
+ while (--nspace >= 0)
+ PUTC(' ');
+ last_col = this_col;
+ }
+
+ for (;;) {
+ if (c->c_set != last_set) {
+ switch (c->c_set) {
+ case CS_NORMAL:
+ PUTC('\017');
+ break;
+ case CS_ALTERNATE:
+ PUTC('\016');
+ }
+ last_set = c->c_set;
+ }
+ PUTC(c->c_char);
+ if ((c + 1) < endc) {
+ int i;
+ for (i=0; i < c->c_width; i++)
+ PUTC('\b');
+ }
+ if (++c >= endc)
+ break;
+ }
+ last_col += (c - 1)->c_width;
+ }
+}
+
+#define NALLOC 64
+
+static LINE *line_freelist;
+
+LINE *
+alloc_line()
+{
+ LINE *l;
+ int i;
+
+ if (!line_freelist) {
+ l = xmalloc(sizeof(LINE) * NALLOC);
+ line_freelist = l;
+ for (i = 1; i < NALLOC; i++, l++)
+ l->l_next = l + 1;
+ l->l_next = NULL;
+ }
+ l = line_freelist;
+ line_freelist = l->l_next;
+
+ memset(l, 0, sizeof(LINE));
+ return l;
+}
+
+void free_line(LINE *l)
+{
+ l->l_next = line_freelist;
+ line_freelist = l;
+}
diff --git a/text-utils/colcrt.1 b/text-utils/colcrt.1
new file mode 100644
index 0000000..f8a5420
--- /dev/null
+++ b/text-utils/colcrt.1
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)colcrt.1 8.1 (Berkeley) 6/30/93
+.\"
+.TH COLCRT "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+colcrt \- filter nroff output for CRT previewing
+.SH SYNOPSIS
+.B colcrt
+[options] [file ...]
+.SH DESCRIPTION
+.B colcrt
+provides virtual half-line and reverse line feed sequences for terminals
+without such capability, and on which overstriking is destructive.
+Half-line characters and underlining (changed to dashing `\-') are placed on
+new lines in between the normal output lines.
+.SH OPTIONS
+.TP
+\fB\-\fR, \fB\-\-no-underlining\fR
+Suppress all underlining. This option is especially useful for previewing
+.I allboxed
+tables from
+.BR tbl (1).
+.TP
+\fB\-2\fR, \fB\-\-half-lines\fR
+Causes all half-lines to be printed, effectively double spacing the output.
+Normally, a minimal space output format is used which will suppress empty
+lines. The program never suppresses two consecutive empty lines, however.
+The
+.B \-2
+option is useful for sending output to the line printer when the output
+contains superscripts and subscripts which would otherwise be invisible.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Output help and exit.
+.SH EXAMPLES
+A typical use of
+.B colcrt
+would be
+.PP
+tbl exum2.n \&| nroff \-ms \&| colcrt \- \&| more
+.SH SEE ALSO
+.BR nroff (1),
+.BR troff (1),
+.BR col (1),
+.BR more (1),
+.BR ul (1)
+.SH BUGS
+Should fold underlines onto blanks even with the
+.B '\-'
+option so that a true underline character would show.
+.PP
+Can't back up more than 102 lines.
+.PP
+General overstriking is lost; as a special case '|' overstruck with '\-' or
+underline becomes '+'.
+.PP
+Lines are trimmed to 132 characters.
+.PP
+Some provision should be made for processing superscripts and subscripts in
+documents which are already double-spaced.
+.SH HISTORY
+The
+.B colcrt
+command appeared in 3.0BSD.
+.SH AVAILABILITY
+The colcrt command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/colcrt.c b/text-utils/colcrt.c
new file mode 100644
index 0000000..3393d0c
--- /dev/null
+++ b/text-utils/colcrt.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h> /* for close() */
+#include <string.h>
+#include <getopt.h>
+#include "nls.h"
+
+#include "widechar.h"
+#include "c.h"
+#include "closestream.h"
+
+int plus(wchar_t c, wchar_t d);
+void move(int l, int m);
+void pflush(int ol);
+static void __attribute__ ((__noreturn__)) usage(FILE * out);
+
+/*
+ * colcrt - replaces col for crts with new nroff esp. when using tbl.
+ * Bill Joy UCB July 14, 1977
+ *
+ * This filter uses a screen buffer, 267 half-lines by 132 columns.
+ * It interprets the up and down sequences generated by the new
+ * nroff when used with tbl and by \u \d and \r.
+ * General overstriking doesn't work correctly.
+ * Underlining is split onto multiple lines, etc.
+ *
+ * Option - suppresses all underlining.
+ * Option -2 forces printing of all half lines.
+ */
+
+wchar_t page[267][132];
+
+int outline = 1;
+int outcol;
+
+char suppresul;
+char printall;
+
+void colcrt(FILE *f);
+
+int main(int argc, char **argv) {
+ FILE *f;
+ int i, opt;
+ enum { NO_UL_OPTION = CHAR_MAX + 1 };
+
+ static const struct option longopts[] = {
+ { "no-underlining", no_argument, 0, NO_UL_OPTION },
+ { "half-lines", no_argument, 0, '2' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ /* Take care of lonely hyphen option. */
+ for (i = 0; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == '\0') {
+ suppresul = 1;
+ argc--;
+ memmove(argv + i, argv + i + 1,
+ sizeof(char *) * (argc - i));
+ i--;
+ }
+
+ while ((opt = getopt_long(argc, argv, "2Vh", longopts, NULL)) != -1)
+ switch (opt) {
+ case NO_UL_OPTION:
+ suppresul = 1;
+ break;
+ case '2':
+ printall = 1;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ argc -= optind;
+ argv += optind;
+
+ do {
+ if (argc > 0) {
+ if (!(f = fopen(argv[0], "r"))) {
+ fflush(stdout);
+ err(EXIT_FAILURE, "%s", argv[0]);
+ }
+ argc--;
+ argv++;
+ } else {
+ f = stdin;
+ }
+ colcrt(f);
+ if (f != stdin)
+ fclose(f);
+ } while (argc > 0);
+ fflush(stdout);
+ return EXIT_SUCCESS;
+}
+
+void colcrt(FILE *f) {
+ wint_t c;
+ wchar_t *cp, *dp;
+ int i, w;
+
+ for (;;) {
+ c = getwc(f);
+ if (c == WEOF) {
+ pflush(outline);
+ fflush(stdout);
+ break;
+ }
+ switch (c) {
+ case '\n':
+ if (outline >= 265)
+ pflush(62);
+ outline += 2;
+ outcol = 0;
+ continue;
+ case '\016':
+ case '\017':
+ continue;
+ case 033:
+ c = getwc(f);
+ switch (c) {
+ case '9':
+ if (outline >= 266)
+ pflush(62);
+ outline++;
+ continue;
+ case '8':
+ if (outline >= 1)
+ outline--;
+ continue;
+ case '7':
+ outline -= 2;
+ if (outline < 0)
+ outline = 0;
+ continue;
+ default:
+ continue;
+ }
+ case '\b':
+ if (outcol)
+ outcol--;
+ continue;
+ case '\t':
+ outcol += 8;
+ outcol &= ~7;
+ outcol--;
+ c = ' ';
+ default:
+ w = wcwidth(c);
+ if (outcol + w > 132) {
+ outcol++;
+ continue;
+ }
+ cp = &page[outline][outcol];
+ outcol += w;
+ if (c == '_') {
+ if (suppresul)
+ continue;
+ cp += 132;
+ c = '-';
+ }
+ if (*cp == 0) {
+ /* trick! */
+ for (i = 0; i < w; i++)
+ cp[i] = c;
+ dp = cp - (outcol - w);
+ for (cp--; cp >= dp && *cp == 0; cp--)
+ *cp = ' ';
+ } else {
+ if (plus(c, *cp) || plus(*cp, c))
+ *cp = '+';
+ else if (*cp == ' ' || *cp == 0) {
+ for (i = 1; i < w; i++)
+ if (cp[i] != ' ' && cp[i] != 0)
+ continue;
+ for (i = 0; i < w; i++)
+ cp[i] = c;
+ }
+ }
+ continue;
+ }
+ }
+}
+
+int plus(wchar_t c, wchar_t d)
+{
+
+ return (c == '|' && (d == '-' || d == '_'));
+}
+
+int first;
+
+void pflush(int ol)
+{
+ register int i;
+ register wchar_t *cp;
+ char lastomit;
+ int l, w;
+
+ l = ol;
+ lastomit = 0;
+ if (l > 266)
+ l = 266;
+ else
+ l |= 1;
+ for (i = first | 1; i < l; i++) {
+ move(i, i - 1);
+ move(i, i + 1);
+ }
+ for (i = first; i < l; i++) {
+ cp = page[i];
+ if (printall == 0 && lastomit == 0 && *cp == 0) {
+ lastomit = 1;
+ continue;
+ }
+ lastomit = 0;
+ while (*cp) {
+ if ((w = wcwidth(*cp)) > 0) {
+ putwchar(*cp);
+ cp += w;
+ } else
+ cp++;
+ }
+ putwchar('\n');
+ }
+ memmove(page, page[ol], (267 - ol) * 132 * sizeof(wchar_t));
+ memset(page[267 - ol], '\0', ol * 132 * sizeof(wchar_t));
+ outline -= ol;
+ outcol = 0;
+ first = 1;
+}
+
+void move(int l, int m)
+{
+ register wchar_t *cp, *dp;
+
+ for (cp = page[l], dp = page[m]; *cp; cp++, dp++) {
+ switch (*cp) {
+ case '|':
+ if (*dp != ' ' && *dp != '|' && *dp != 0)
+ return;
+ break;
+ case ' ':
+ break;
+ default:
+ return;
+ }
+ }
+ if (*cp == 0) {
+ for (cp = page[l], dp = page[m]; *cp; cp++, dp++)
+ if (*cp == '|')
+ *dp = '|';
+ else if (*dp == 0)
+ *dp = ' ';
+ page[l][0] = 0;
+ }
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out,
+ _("\nUsage:\n"
+ " %s [options] [file ...]\n"), program_invocation_short_name);
+
+ fprintf(out,
+ _(" -, --no-underlining suppress all underlining\n"
+ " -2, --half-lines print all half-lines\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/text-utils/colrm.1 b/text-utils/colrm.1
new file mode 100644
index 0000000..03c7f13
--- /dev/null
+++ b/text-utils/colrm.1
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)colrm.1 6.6 (Berkeley) 3/14/91
+.\"
+.TH COLRM "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+colrm \- remove columns from a file
+.SH SYNOPSIS
+.B colrm
+.RI [ first \ [ last ]]
+.SH DESCRIPTION
+.B colrm
+removes selected columns from a file. Input is taken from standard input.
+Output is sent to standard output.
+.PP
+If called with one parameter the columns of each line will be removed
+starting with the specified
+.I first
+column. If called with two parameters the columns from the
+.I first
+column to the
+.I last
+column will be removed.
+.PP
+Column numbering starts with column 1.
+.SH OPTIONS
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Output help and exit.
+.SH SEE ALSO
+.BR awk (1),
+.BR column (1),
+.BR expand (1),
+.BR paste (1)
+.SH HISTORY
+The
+.B colrm
+command appeared in 3.0BSD.
+.SH AVAILABILITY
+The colrm command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/colrm.c b/text-utils/colrm.c
new file mode 100644
index 0000000..a241fb8
--- /dev/null
+++ b/text-utils/colrm.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include "nls.h"
+#include "widechar.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+/*
+COLRM removes unwanted columns from a file
+ Jeff Schriebman UC Berkeley 11-74
+*/
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, _("\nUsage:\n"
+ " %s [startcol [endcol]]\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _("\nOptions:\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ fprintf(out, _("%s reads from standard input and writes to standard output\n\n"),
+ program_invocation_short_name);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int process_input(unsigned long first, unsigned long last)
+{
+ unsigned long ct = 0;
+ wint_t c;
+ unsigned long i;
+ int w;
+ int padding;
+
+ for (;;) {
+ c = getwc(stdin);
+ if (c == WEOF)
+ return 0;
+ if (c == '\t')
+ w = ((ct + 8) & ~7) - ct;
+ else if (c == '\b')
+ w = (ct ? ct - 1 : 0) - ct;
+ else {
+ w = wcwidth(c);
+ if (w < 0)
+ w = 0;
+ }
+ ct += w;
+ if (c == '\n') {
+ putwc(c, stdout);
+ ct = 0;
+ continue;
+
+ }
+ if (!first || ct < first) {
+ putwc(c, stdout);
+ continue;
+ }
+ break;
+ }
+
+ for (i = ct - w + 1; i < first; i++)
+ putwc(' ', stdout);
+
+ /* Loop getting rid of characters */
+ while (!last || ct < last) {
+ c = getwc(stdin);
+ if (c == WEOF)
+ return 0;
+ if (c == '\n') {
+ putwc(c, stdout);
+ return 1;
+ }
+ if (c == '\t')
+ ct = (ct + 8) & ~7;
+ else if (c == '\b')
+ ct = ct ? ct - 1 : 0;
+ else {
+ w = wcwidth(c);
+ if (w < 0)
+ w = 0;
+ ct += w;
+ }
+ }
+
+ padding = 0;
+
+ /* Output last of the line */
+ for (;;) {
+ c = getwc(stdin);
+ if (c == WEOF)
+ break;
+ if (c == '\n') {
+ putwc(c, stdout);
+ return 1;
+ }
+ if (padding == 0 && last < ct) {
+ for (i = last; i < ct; i++)
+ putwc(' ', stdout);
+ padding = 1;
+ }
+ putwc(c, stdout);
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ unsigned long first = 0, last = 0;
+ int opt;
+
+ static const struct option longopts[] = {
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {NULL, 0, 0, 0}
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((opt =
+ getopt_long(argc, argv, "bfhl:pxVH", longopts,
+ NULL)) != -1)
+ switch (opt) {
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc > 1)
+ first = strtoul_or_err(*++argv, _("first argument"));
+ if (argc > 2)
+ last = strtoul_or_err(*++argv, _("second argument"));
+
+ while (process_input(first, last))
+ ;
+
+ fflush(stdout);
+ return EXIT_SUCCESS;
+}
diff --git a/text-utils/column.1 b/text-utils/column.1
new file mode 100644
index 0000000..2050666
--- /dev/null
+++ b/text-utils/column.1
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)column.1 8.1 (Berkeley) 6/6/93
+.\"
+.TH COLUMN 1 "October 2010" "util-linux" "User Commands"
+.SH NAME
+column - columnate lists
+.SH SYNOPSIS
+.B column
+.RI [ options ]
+.IR file ...
+.SH DESCRIPTION
+The
+.B column
+utility formats its input into multiple columns. Rows
+are filled before columns. Input is taken from \fIfile\fR or, by
+default, from standard input. Empty lines are ignored.
+.PP
+.SH OPTIONS
+.IP "\fB\-c, \-\-columns\fP \fIwidth\fP"
+Output is formatted to a width specified as number of characters.
+.IP "\fB\-t, \-\-table\fP"
+Determine the number of columns the input contains and create a table.
+Columns are delimited with whitespace, by default, or with the characters
+supplied using the separator. Table output is useful for pretty-printing.
+.IP "\fB\-s, \-\-separator\fP \fIseparator\fP"
+Specify table separator (default is whitespace).
+.IP "\fB\-x, \-\-fillrows\fP"
+Fill columns before filling rows.
+.IP "\fB\-h, \-\-help\fP"
+Print help and exit.
+.SH ENVIRONMENT
+The environment variable COLUMNS is used to determine the size of
+the screen if no other information is available.
+.SH EXAMPLES
+.nf
+sed 's/#.*//' /etc/fstab | column -t
+.nf
+.SH "SEE ALSO"
+.BR colrm (1),
+.BR ls (1),
+.BR paste (1),
+.BR sort (1)
+.SH HISTORY
+The column command appeared in 4.3BSD-Reno.
+.SH AVAILABILITY
+The column command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/text-utils/column.c b/text-utils/column.c
new file mode 100644
index 0000000..f33cc14
--- /dev/null
+++ b/text-utils/column.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "widechar.h"
+#include "c.h"
+#include "xalloc.h"
+#include "strutils.h"
+#include "closestream.h"
+#include "ttyutils.h"
+
+#ifdef HAVE_WIDECHAR
+#define wcs_width(s) wcswidth(s,wcslen(s))
+static wchar_t *mbs_to_wcs(const char *);
+#else
+#define wcs_width(s) strlen(s)
+#define mbs_to_wcs(s) xstrdup(s)
+static char *mtsafe_strtok(char *, const char *, char **);
+#define wcstok mtsafe_strtok
+#endif
+
+#define DEFCOLS 25
+#define TAB 8
+#define DEFNUM 1000
+#define MAXLINELEN (LINE_MAX + 1)
+
+static int input(FILE *fp, int *maxlength, wchar_t ***list, int *entries);
+static void c_columnate(int maxlength, long termwidth, wchar_t **list, int entries);
+static void r_columnate(int maxlength, long termwidth, wchar_t **list, int entries);
+static void maketbl(wchar_t **list, int entries, wchar_t *separator);
+static void print(wchar_t **list, int entries);
+
+typedef struct _tbl {
+ wchar_t **list;
+ int cols, *len;
+} TBL;
+
+static void __attribute__((__noreturn__)) usage(int rc)
+{
+ FILE *out = rc == EXIT_FAILURE ? stderr : stdout;
+
+ fprintf(out, _("\nUsage: %s [options] [file ...]\n"),
+ program_invocation_short_name);
+ fprintf(out, _("\nOptions:\n"));
+
+ fprintf(out, _(
+ " -h, --help displays this help text\n"
+ " -V, --version output version information and exit\n"
+ " -c, --columns <width> width of output in number of characters\n"
+ " -t, --table create a table\n"
+ " -s, --separator <string> table delimeter\n"
+ " -x, --fillrows fill rows before columns\n"));
+
+ fprintf(out, _("\nFor more information see column(1).\n"));
+ exit(rc);
+}
+
+int main(int argc, char **argv)
+{
+ int ch, tflag = 0, xflag = 0;
+ int i;
+ int termwidth = 80;
+ int entries = 0; /* number of records */
+ unsigned int eval = 0; /* exit value */
+ int maxlength = 0; /* longest record */
+ wchar_t **list = NULL; /* array of pointers to records */
+
+ /* field separator for table option */
+ wchar_t default_separator[] = { '\t', ' ', 0 };
+ wchar_t *separator = default_separator;
+
+ static const struct option longopts[] =
+ {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "columns", 1, 0, 'c' },
+ { "table", 0, 0, 't' },
+ { "separator", 1, 0, 's' },
+ { "fillrows", 0, 0, 'x' },
+ { NULL, 0, 0, 0 },
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ termwidth = get_terminal_width();
+ if (termwidth <= 0)
+ termwidth = 80;
+
+ while ((ch = getopt_long(argc, argv, "hVc:s:tx", longopts, NULL)) != -1)
+ switch(ch) {
+ case 'h':
+ usage(EXIT_SUCCESS);
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'c':
+ termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
+ break;
+ case 's':
+ separator = mbs_to_wcs(optarg);
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ eval += input(stdin, &maxlength, &list, &entries);
+ else
+ for (; *argv; ++argv) {
+ FILE *fp;
+
+ if ((fp = fopen(*argv, "r")) != NULL) {
+ eval += input(fp, &maxlength, &list, &entries);
+ fclose(fp);
+ } else {
+ warn("%s", *argv);
+ eval += EXIT_FAILURE;
+ }
+ }
+
+ if (!entries)
+ exit(eval);
+
+ if (tflag)
+ maketbl(list, entries, separator);
+ else if (maxlength >= termwidth)
+ print(list, entries);
+ else if (xflag)
+ c_columnate(maxlength, termwidth, list, entries);
+ else
+ r_columnate(maxlength, termwidth, list, entries);
+
+ for (i = 0; i < entries; i++)
+ free(list[i]);
+ free(list);
+
+ if (eval == 0)
+ return EXIT_SUCCESS;
+ else
+ return EXIT_FAILURE;
+}
+
+static void c_columnate(int maxlength, long termwidth, wchar_t **list, int entries)
+{
+ int chcnt, col, cnt, endcol, numcols;
+ wchar_t **lp;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ endcol = maxlength;
+ for (chcnt = col = 0, lp = list;; ++lp) {
+ fputws(*lp, stdout);
+ chcnt += wcs_width(*lp);
+ if (!--entries)
+ break;
+ if (++col == numcols) {
+ chcnt = col = 0;
+ endcol = maxlength;
+ putwchar('\n');
+ } else {
+ while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
+ putwchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ }
+ if (chcnt)
+ putwchar('\n');
+}
+
+static void r_columnate(int maxlength, long termwidth, wchar_t **list, int entries)
+{
+ int base, chcnt, cnt, col, endcol, numcols, numrows, row;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ if (!numcols)
+ numcols = 1;
+ numrows = entries / numcols;
+ if (entries % numcols)
+ ++numrows;
+
+ for (row = 0; row < numrows; ++row) {
+ endcol = maxlength;
+ for (base = row, chcnt = col = 0; col < numcols; ++col) {
+ fputws(list[base], stdout);
+ chcnt += wcs_width(list[base]);
+ if ((base += numrows) >= entries)
+ break;
+ while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
+ putwchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ putwchar('\n');
+ }
+}
+
+static void print(wchar_t **list, int entries)
+{
+ int cnt;
+ wchar_t **lp;
+
+ for (cnt = entries, lp = list; cnt--; ++lp) {
+ fputws(*lp, stdout);
+ putwchar('\n');
+ }
+}
+
+static void maketbl(wchar_t **list, int entries, wchar_t *separator)
+{
+ TBL *t;
+ int cnt, i;
+ wchar_t *p, **lp;
+ ssize_t *lens;
+ ssize_t maxcols = DEFCOLS, coloff;
+ TBL *tbl;
+ wchar_t **cols;
+ wchar_t *wcstok_state;
+
+ t = tbl = xcalloc(entries, sizeof(TBL));
+ cols = xcalloc(maxcols, sizeof(wchar_t *));
+ lens = xcalloc(maxcols, sizeof(ssize_t));
+
+ for (lp = list, cnt = 0; cnt < entries; ++cnt, ++lp, ++t) {
+ coloff = 0;
+ p = *lp;
+ while ((cols[coloff] = wcstok(p, separator, &wcstok_state)) != NULL) {
+ if (++coloff == maxcols) {
+ maxcols += DEFCOLS;
+ cols = xrealloc(cols, maxcols * sizeof(wchar_t *));
+ lens = xrealloc(lens, maxcols * sizeof(ssize_t));
+ /* zero fill only new memory */
+ memset(lens + (maxcols - DEFCOLS), 0,
+ DEFCOLS * sizeof(*lens));
+ }
+ p = NULL;
+ }
+ t->list = xcalloc(coloff, sizeof(wchar_t *));
+ t->len = xcalloc(coloff, sizeof(int));
+ for (t->cols = coloff; --coloff >= 0;) {
+ t->list[coloff] = cols[coloff];
+ t->len[coloff] = wcs_width(cols[coloff]);
+ if (t->len[coloff] > lens[coloff])
+ lens[coloff] = t->len[coloff];
+ }
+ }
+
+ for (t = tbl, cnt = 0; cnt < entries; ++cnt, ++t) {
+ for (coloff = 0; coloff < t->cols - 1; ++coloff) {
+ fputws(t->list[coloff], stdout);
+ for (i = lens[coloff] - t->len[coloff] + 2; i > 0; i--)
+ putwchar(' ');
+ }
+ if (coloff < t->cols) {
+ fputws(t->list[coloff], stdout);
+ putwchar('\n');
+ }
+ }
+
+ for (cnt = 0; cnt < entries; ++cnt) {
+ free((tbl+cnt)->list);
+ free((tbl+cnt)->len);
+ }
+ free(cols);
+ free(lens);
+ free(tbl);
+}
+
+static int input(FILE *fp, int *maxlength, wchar_t ***list, int *entries)
+{
+ static int maxentry = DEFNUM;
+ int len, lineno = 1, reportedline = 0, eval = 0;
+ wchar_t *p, buf[MAXLINELEN];
+ wchar_t **local_list = *list;
+ int local_entries = *entries;
+
+ if (!local_list)
+ local_list = xcalloc(maxentry, sizeof(wchar_t *));
+
+ while (fgetws(buf, MAXLINELEN, fp)) {
+ for (p = buf; *p && iswspace(*p); ++p)
+ ;
+ if (!*p)
+ continue;
+ if (!(p = wcschr(p, '\n')) && !feof(fp)) {
+ if (reportedline < lineno) {
+ warnx(_("line %d is too long, output will be truncated"),
+ lineno);
+ reportedline = lineno;
+ }
+ eval = 1;
+ continue;
+ }
+ lineno++;
+ if (!feof(fp))
+ *p = '\0';
+ len = wcs_width(buf); /* len = p - buf; */
+ if (*maxlength < len)
+ *maxlength = len;
+ if (local_entries == maxentry) {
+ maxentry += DEFNUM;
+ local_list = xrealloc(local_list,
+ (u_int)maxentry * sizeof(wchar_t *));
+ }
+ local_list[local_entries++] = wcsdup(buf);
+ }
+
+ *list = local_list;
+ *entries = local_entries;
+
+ return eval;
+}
+
+#ifdef HAVE_WIDECHAR
+static wchar_t *mbs_to_wcs(const char *s)
+{
+ ssize_t n;
+ wchar_t *wcs;
+
+ n = mbstowcs((wchar_t *)0, s, 0);
+ if (n < 0)
+ return NULL;
+ wcs = xmalloc((n + 1) * sizeof(wchar_t));
+ n = mbstowcs(wcs, s, n + 1);
+ if (n < 0)
+ return NULL;
+ return wcs;
+}
+#endif
+
+#ifndef HAVE_WIDECHAR
+static char *mtsafe_strtok(char *str, const char *delim, char **ptr)
+{
+ if (str == NULL) {
+ str = *ptr;
+ if (str == NULL)
+ return NULL;
+ }
+ str += strspn(str, delim);
+ if (*str == '\0') {
+ *ptr = NULL;
+ return NULL;
+ } else {
+ char *token_end = strpbrk(str, delim);
+ if (token_end) {
+ *token_end = '\0';
+ *ptr = token_end + 1;
+ } else
+ *ptr = NULL;
+ return str;
+ }
+}
+#endif
diff --git a/text-utils/conv.c b/text-utils/conv.c
new file mode 100644
index 0000000..95b8cd2
--- /dev/null
+++ b/text-utils/conv.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "hexdump.h"
+
+void
+conv_c(PR *pr, u_char *p)
+{
+ char buf[10];
+ char const *str;
+
+ switch(*p) {
+ case '\0':
+ str = "\\0";
+ goto strpr;
+ /* case '\a': */
+ case '\007':
+ str = "\\a";
+ goto strpr;
+ case '\b':
+ str = "\\b";
+ goto strpr;
+ case '\f':
+ str = "\\f";
+ goto strpr;
+ case '\n':
+ str = "\\n";
+ goto strpr;
+ case '\r':
+ str = "\\r";
+ goto strpr;
+ case '\t':
+ str = "\\t";
+ goto strpr;
+ case '\v':
+ str = "\\v";
+ goto strpr;
+ default:
+ break;
+ }
+ if (isprint(*p)) {
+ *pr->cchar = 'c';
+ (void)printf(pr->fmt, *p);
+ } else {
+ (void)sprintf(buf, "%03o", (int)*p);
+ str = buf;
+strpr: *pr->cchar = 's';
+ (void)printf(pr->fmt, str);
+ }
+}
+
+void
+conv_u(PR *pr, u_char *p)
+{
+ static const char *list[] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs", "ht", "lf", "vt", "ff", "cr", "so", "si",
+ "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em", "sub", "esc", "fs", "gs", "rs", "us",
+ };
+
+ /* od used nl, not lf */
+ if (*p <= 0x1f) {
+ *pr->cchar = 's';
+ (void)printf(pr->fmt, list[*p]);
+ } else if (*p == 0x7f) {
+ *pr->cchar = 's';
+ (void)printf(pr->fmt, "del");
+ } else if (isprint(*p)) {
+ *pr->cchar = 'c';
+ (void)printf(pr->fmt, *p);
+ } else {
+ *pr->cchar = 'x';
+ (void)printf(pr->fmt, (int)*p);
+ }
+}
diff --git a/text-utils/display.c b/text-utils/display.c
new file mode 100644
index 0000000..1f9a11b
--- /dev/null
+++ b/text-utils/display.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hexdump.h"
+#include "xalloc.h"
+#include "c.h"
+#include "nls.h"
+
+static void doskip(const char *, int);
+static u_char *get(void);
+
+enum _vflag vflag = FIRST;
+
+static off_t address; /* address/offset in stream */
+static off_t eaddress; /* end address */
+
+static inline void
+print(PR *pr, unsigned char *bp) {
+
+ switch(pr->flags) {
+ case F_ADDRESS:
+ (void)printf(pr->fmt, (int64_t)address);
+ break;
+ case F_BPAD:
+ (void)printf(pr->fmt, "");
+ break;
+ case F_C:
+ conv_c(pr, bp);
+ break;
+ case F_CHAR:
+ (void)printf(pr->fmt, *bp);
+ break;
+ case F_DBL:
+ {
+ double dval;
+ float fval;
+ switch(pr->bcnt) {
+ case 4:
+ memmove(&fval, bp, sizeof(fval));
+ (void)printf(pr->fmt, fval);
+ break;
+ case 8:
+ memmove(&dval, bp, sizeof(dval));
+ (void)printf(pr->fmt, dval);
+ break;
+ }
+ break;
+ }
+ case F_INT:
+ {
+ short sval; /* int16_t */
+ int ival; /* int32_t */
+ long long Lval; /* int64_t, int64_t */
+
+ switch(pr->bcnt) {
+ case 1:
+ (void)printf(pr->fmt, (int64_t)*bp);
+ break;
+ case 2:
+ memmove(&sval, bp, sizeof(sval));
+ (void)printf(pr->fmt, (int64_t)sval);
+ break;
+ case 4:
+ memmove(&ival, bp, sizeof(ival));
+ (void)printf(pr->fmt, (int64_t)ival);
+ break;
+ case 8:
+ memmove(&Lval, bp, sizeof(Lval));
+ (void)printf(pr->fmt, (int64_t)Lval);
+ break;
+ }
+ break;
+ }
+ case F_P:
+ (void)printf(pr->fmt, isprint(*bp) ? *bp : '.');
+ break;
+ case F_STR:
+ (void)printf(pr->fmt, (char *)bp);
+ break;
+ case F_TEXT:
+ (void)printf("%s", pr->fmt);
+ break;
+ case F_U:
+ conv_u(pr, bp);
+ break;
+ case F_UINT:
+ {
+ unsigned short sval; /* u_int16_t */
+ unsigned int ival; /* u_int32_t */
+ unsigned long long Lval;/* u_int64_t, u_int64_t */
+
+ switch(pr->bcnt) {
+ case 1:
+ (void)printf(pr->fmt, (uint64_t)*bp);
+ break;
+ case 2:
+ memmove(&sval, bp, sizeof(sval));
+ (void)printf(pr->fmt, (uint64_t)sval);
+ break;
+ case 4:
+ memmove(&ival, bp, sizeof(ival));
+ (void)printf(pr->fmt, (uint64_t)ival);
+ break;
+ case 8:
+ memmove(&Lval, bp, sizeof(Lval));
+ (void)printf(pr->fmt, (uint64_t)Lval);
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void bpad(PR *pr)
+{
+ static const char *spec = " -0+#";
+ char *p1, *p2;
+
+ /*
+ * remove all conversion flags; '-' is the only one valid
+ * with %s, and it's not useful here.
+ */
+ pr->flags = F_BPAD;
+ pr->cchar[0] = 's';
+ pr->cchar[1] = 0;
+ for (p1 = pr->fmt; *p1 != '%'; ++p1);
+ for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1);
+ while ((*p2++ = *p1++) != 0) ;
+}
+
+void display(void)
+{
+ register FS *fs;
+ register FU *fu;
+ register PR *pr;
+ register int cnt;
+ register unsigned char *bp;
+ off_t saveaddress;
+ unsigned char savech = 0, *savebp;
+
+ while ((bp = get()) != NULL)
+ for (fs = fshead, savebp = bp, saveaddress = address; fs;
+ fs = fs->nextfs, bp = savebp, address = saveaddress)
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->flags&F_IGNORE)
+ break;
+ for (cnt = fu->reps; cnt; --cnt)
+ for (pr = fu->nextpr; pr; address += pr->bcnt,
+ bp += pr->bcnt, pr = pr->nextpr) {
+ if (eaddress && address >= eaddress &&
+ !(pr->flags&(F_TEXT|F_BPAD)))
+ bpad(pr);
+ if (cnt == 1 && pr->nospace) {
+ savech = *pr->nospace;
+ *pr->nospace = '\0';
+ }
+ print(pr, bp);
+ if (cnt == 1 && pr->nospace)
+ *pr->nospace = savech;
+ }
+ }
+ if (endfu) {
+ /*
+ * if eaddress not set, error or file size was multiple of
+ * blocksize, and no partial block ever found.
+ */
+ if (!eaddress) {
+ if (!address)
+ return;
+ eaddress = address;
+ }
+ for (pr = endfu->nextpr; pr; pr = pr->nextpr)
+ switch(pr->flags) {
+ case F_ADDRESS:
+ (void)printf(pr->fmt, (int64_t)eaddress);
+ break;
+ case F_TEXT:
+ (void)printf("%s", pr->fmt);
+ break;
+ }
+ }
+}
+
+static char **_argv;
+
+static u_char *
+get(void)
+{
+ static int ateof = 1;
+ static u_char *curp, *savp;
+ ssize_t n, need, nread;
+ u_char *tmpp;
+
+ if (!curp) {
+ curp = xcalloc(1, blocksize);
+ savp = xcalloc(1, blocksize);
+ } else {
+ tmpp = curp;
+ curp = savp;
+ savp = tmpp;
+ address += blocksize;
+ }
+ for (need = blocksize, nread = 0;;) {
+ /*
+ * if read the right number of bytes, or at EOF for one file,
+ * and no other files are available, zero-pad the rest of the
+ * block and set the end flag.
+ */
+ if (!length || (ateof && !next(NULL))) {
+ if (need == blocksize)
+ return(NULL);
+ if (!need && vflag != ALL &&
+ !memcmp(curp, savp, nread)) {
+ if (vflag != DUP)
+ (void)printf("*\n");
+ return(NULL);
+ }
+ if (need > 0)
+ memset((char *)curp + nread, 0, need);
+ eaddress = address + nread;
+ return(curp);
+ }
+ if (fileno(stdin) == -1) {
+ warnx(_("all input file arguments failed"));
+ return(NULL);
+ }
+ n = fread((char *)curp + nread, sizeof(unsigned char),
+ length == -1 ? need : min(length, need), stdin);
+ if (!n) {
+ if (ferror(stdin))
+ warn("%s", _argv[-1]);
+ ateof = 1;
+ continue;
+ }
+ ateof = 0;
+ if (length != -1)
+ length -= n;
+ if (!(need -= n)) {
+ if (vflag == ALL || vflag == FIRST ||
+ memcmp(curp, savp, blocksize)) {
+ if (vflag == DUP || vflag == FIRST)
+ vflag = WAIT;
+ return(curp);
+ }
+ if (vflag == WAIT)
+ (void)printf("*\n");
+ vflag = DUP;
+ address += blocksize;
+ need = blocksize;
+ nread = 0;
+ }
+ else
+ nread += n;
+ }
+}
+
+int next(char **argv)
+{
+ static int done;
+ int statok;
+
+ if (argv) {
+ _argv = argv;
+ return(1);
+ }
+ for (;;) {
+ if (*_argv) {
+ if (!(freopen(*_argv, "r", stdin))) {
+ warn("%s", *_argv);
+ exitval = EXIT_FAILURE;
+ ++_argv;
+ continue;
+ }
+ statok = done = 1;
+ } else {
+ if (done++)
+ return(0);
+ statok = 0;
+ }
+ if (skip)
+ doskip(statok ? *_argv : "stdin", statok);
+ if (*_argv)
+ ++_argv;
+ if (!skip)
+ return(1);
+ }
+ /* NOTREACHED */
+}
+
+static void
+doskip(const char *fname, int statok)
+{
+ struct stat sbuf;
+
+ if (statok) {
+ if (fstat(fileno(stdin), &sbuf))
+ err(EXIT_FAILURE, "%s", fname);
+ if (S_ISREG(sbuf.st_mode) && skip > sbuf.st_size) {
+ /* If size valid and skip >= size */
+ skip -= sbuf.st_size;
+ address += sbuf.st_size;
+ return;
+ }
+ }
+ /* sbuf may be undefined here - do not test it */
+ if (fseek(stdin, skip, SEEK_SET))
+ err(EXIT_FAILURE, "%s", fname);
+ address += skip;
+ skip = 0;
+}
diff --git a/text-utils/hexdump.1 b/text-utils/hexdump.1
new file mode 100644
index 0000000..84ecaf4
--- /dev/null
+++ b/text-utils/hexdump.1
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)hexdump.1 8.2 (Berkeley) 4/18/94
+.\"
+.TH HEXDUMP "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+hexdump \- display file contents in ascii, decimal, hexadecimal, or octal
+.SH SYNOPSIS
+.B hexdump
+[options] file [...]
+.SH DESCRIPTION
+The
+.B hexdump
+utility is a filter which displays the specified files, or
+standard input if no files are specified, in a user-specified
+format.
+.SH OPTIONS
+The \fIlength\fR and \fIoffset\fR arguments may be followed by the multiplicative
+suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB
+(the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes
+KB=1000, MB=1000*1000, and so on for GB, PB, EB, ZB and YB.
+.TP
+.B \-b
+\fIOne-byte octal display\fR. Display the input offset in hexadecimal,
+followed by sixteen space-separated, three-column, zero-filled bytes of input
+data, in octal, per line.
+.TP
+.B \-c
+\fIOne-byte character display\fR. Display the input offset in hexadecimal,
+followed by sixteen space-separated, three-column, space-filled characters of
+input data per line.
+.TP
+.B \-C
+\fICanonical hex+ASCII display\fR. Display the input offset in hexadecimal,
+followed by sixteen space-separated, two-column, hexadecimal bytes, followed
+by the same sixteen bytes in
+.B %_p
+format enclosed in
+.RB ' | '
+characters.
+.TP
+.B \-d
+\fITwo-byte decimal display\fR. Display the input offset in hexadecimal,
+followed by eight space-separated, five-column, zero-filled, two-byte units
+of input data, in unsigned decimal, per line.
+.TP
+.BI \-e \ format_string
+Specify a format string to be used for displaying data.
+.TP
+.BI \-f \ format_file
+Specify a file that contains one or more newline separated format strings.
+Empty lines and lines whose first non-blank character is a hash mark (\&#)
+are ignored.
+.TP
+.BI \-n \ length
+Interpret only
+.I length
+bytes of input.
+.TP
+.B \-o
+\fITwo-byte octal display\fR. Display the input offset in hexadecimal,
+followed by eight space-separated, six-column, zero-filled, two-byte
+quantities of input data, in octal, per line.
+.TP
+.BI \-s \ offset
+Skip
+.I offset
+bytes from the beginning of the input.
+.TP
+.B \-v
+The
+.B \-v
+option causes
+.B hexdump
+to display all input data. Without the
+.B \-v
+option, any number of groups of output lines which would be identical to the
+immediately preceding group of output lines (except for the input offsets),
+are replaced with a line comprised of a single asterisk.
+.TP
+.B \-x
+\fITwo-byte hexadecimal display\fR. Display the input offset in hexadecimal,
+followed by eight space-separated, four-column, zero-filled, two-byte
+quantities of input data, in hexadecimal, per line.
+.PP
+For each input file,
+.B hexdump
+sequentially copies the input to standard output, transforming the data
+according to the format strings specified by the
+.B \-e
+and
+.B \-f
+options, in the order that they were specified.
+.SH FORMATS
+A format string contains any number of format units, separated by whitespace.
+A format unit contains up to three items: an iteration count, a byte count,
+and a format.
+.PP
+The iteration count is an optional positive integer, which defaults to one.
+Each format is applied iteration count times.
+.PP
+The byte count is an optional positive integer. If specified it defines the
+number of bytes to be interpreted by each iteration of the format.
+.PP
+If an iteration count and/or a byte count is specified, a single slash must
+be placed after the iteration count and/or before the byte count to
+disambiguate them. Any whitespace before or after the slash is ignored.
+.PP
+The format is required and must be surrounded by double quote (" ") marks.
+It is interpreted as a fprintf-style format string (see
+.BR fprintf (3),
+with the following exceptions:
+.TP
+1.
+An asterisk (*) may not be used as a field width or precision.
+.TP
+2.
+A byte count or field precision
+.I is
+required for each
+.B s
+conversion character (unlike the
+.BR fprintf (3)
+default which prints the entire string if the precision is unspecified).
+.TP
+3.
+The conversion characters
+.BR h , \ l , \ n , \ p ,
+.RB and \ q
+are not supported.
+.TP
+4.
+The single character escape sequences described in the C standard are
+supported:
+.PP
+.RS 13
+.PD 0
+.TP 21
+NULL
+\e0
+.TP
+<alert character>
+\ea
+.TP
+<backspace>
+\eb
+.TP
+<form-feed>
+\ef
+.TP
+<newline>
+\en
+.TP
+<carriage return>
+\er
+.TP
+<tab>
+\et
+.TP
+<vertical tab>
+\ev
+.PD
+.RE
+.PP
+.SS Conversion strings
+The
+.B hexdump
+utility also supports the following additional conversion strings.
+.TP
+.B \&_a[dox]
+Display the input offset, cumulative across input files, of the next byte to
+be displayed. The appended characters
+.BR d ,
+.BR o ,
+and
+.B x
+specify the display base as decimal, octal or hexadecimal respectively.
+.TP
+.B \&_A[dox]
+Identical to the
+.B \&_a
+conversion string except that it is only performed once, when all of the
+input data has been processed.
+.TP
+.B \&_c
+Output characters in the default character set. Nonprinting characters are
+displayed in three-character, zero-padded octal, except for those
+representable by standard escape notation (see above), which are displayed as
+two-character strings.
+.TP
+.B \&_p
+Output characters in the default character set. Nonprinting characters are
+displayed as a single
+.RB ' \&. '.
+.TP
+.B \&_u
+Output US ASCII characters, with the exception that control characters are
+displayed using the following, lower-case, names. Characters greater than
+0xff, hexadecimal, are displayed as hexadecimal strings.
+.PP
+.nf
+ 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq
+ 006 ack 007 bel 008 bs 009 ht 00A lf 00B vt
+ 00C ff 00D cr 00E so 00F si 010 dle 011 dc1
+ 012 dc2 013 dc3 014 dc4 015 nak 016 syn 017 etb
+ 018 can 019 em 01A sub 01B esc 01C fs 01D gs
+ 01E rs 01F us 0FF del
+.nf
+.SS Counters
+The default and supported byte counts for the conversion characters
+are as follows:
+.TP
+.BR \&%_c , \ \&%_p , \ \&%_u , \ \&%c
+One byte counts only.
+.TP
+.BR \&%d , \ \&%i , \ \&%o , \ \&%u , \ \&%X , \ \&%x
+Four byte default, one, two and four byte counts supported.
+.TP
+.BR \&%E , \ \&%e , \ \&%f , \ \&%G , \ \&%g
+Eight byte default, four byte counts supported.
+.PP
+The amount of data interpreted by each format string is the sum of the data
+required by each format unit, which is the iteration count times the byte
+count, or the iteration count times the number of bytes required by the
+format if the byte count is not specified.
+.PP
+The input is manipulated in
+.IR blocks ,
+where a block is defined as the largest amount of data specified by any
+format string. Format strings interpreting less than an input block's worth
+of data, whose last format unit both interprets some number of bytes and does
+not have a specified iteration count, have the iteration count incremented
+until the entire input block has been processed or there is not enough data
+remaining in the block to satisfy the format string.
+.PP
+If, either as a result of user specification or
+.B hexdump
+modifying the iteration count as described above, an iteration count is
+greater than one, no trailing whitespace characters are output during the
+last iteration.
+.PP
+It is an error to specify a byte count as well as multiple conversion
+characters or strings unless all but one of the conversion characters or
+strings is
+.B \&_a
+or
+.BR \&_A .
+.PP
+If, as a result of the specification of the
+.B \-n
+option or end-of-file being reached, input data only partially satisfies a
+format string, the input block is zero-padded sufficiently to display all
+available data (i.e. any format units overlapping the end of data will
+display some number of the zero bytes).
+.PP
+Further output by such format strings is replaced by an equivalent number of
+spaces. An equivalent number of spaces is defined as the number of spaces
+output by an
+.B s
+conversion character with the same field width and precision as the original
+conversion character or conversion string but with any
+.RB ' \&+ ',
+\' \',
+.RB ' \&# '
+conversion flag characters removed, and referencing a NULL string.
+.PP
+If no format strings are specified, the default display is equivalent
+to specifying the
+.B \-x
+option.
+.SH "EXIT STATUS"
+.B hexdump
+exits 0 on success and >0 if an error occurred.
+.SH EXAMPLES
+Display the input in perusal format:
+.nf
+ "%06.6_ao " 12/1 "%3_u "
+ "\et\et" "%_p "
+ "\en"
+.nf
+.PP
+Implement the \-x option:
+.nf
+ "%07.7_Ax\en"
+ "%07.7_ax " 8/2 "%04x " "\en"
+.nf
+.SH STANDARDS
+The
+.B hexdump
+utility is expected to be IEEE Std 1003.2 ("POSIX.2") compatible.
+.SH AVAILABILITY
+The hexdump command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/hexdump.c b/text-utils/hexdump.c
new file mode 100644
index 0000000..a4a61cf
--- /dev/null
+++ b/text-utils/hexdump.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "hexdump.h"
+
+#include "nls.h"
+#include "c.h"
+#include "closestream.h"
+
+FS *fshead; /* head of format strings */
+ssize_t blocksize; /* data block size */
+int exitval; /* final exit value */
+ssize_t length = -1; /* max bytes to read */
+
+int main(int argc, char **argv)
+{
+ FS *tfs;
+ char *p;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (!(p = strrchr(argv[0], 'o')) || strcmp(p, "od")) {
+ newsyntax(argc, &argv);
+ } else
+ errx(EXIT_FAILURE, _("calling hexdump as od has been deprecated "
+ "in favour to GNU coreutils od."));
+
+ /* figure out the data block size */
+ for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) {
+ tfs->bcnt = block_size(tfs);
+ if (blocksize < tfs->bcnt)
+ blocksize = tfs->bcnt;
+ }
+ /* rewrite the rules, do syntax checking */
+ for (tfs = fshead; tfs; tfs = tfs->nextfs)
+ rewrite(tfs);
+
+ (void)next(argv);
+ display();
+ return exitval;
+}
diff --git a/text-utils/hexdump.h b/text-utils/hexdump.h
new file mode 100644
index 0000000..b9e67a1
--- /dev/null
+++ b/text-utils/hexdump.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hexdump.h 5.4 (Berkeley) 6/1/90
+ */
+
+typedef struct _pr {
+ struct _pr *nextpr; /* next print unit */
+#define F_ADDRESS 0x001 /* print offset */
+#define F_BPAD 0x002 /* blank pad */
+#define F_C 0x004 /* %_c */
+#define F_CHAR 0x008 /* %c */
+#define F_DBL 0x010 /* %[EefGf] */
+#define F_INT 0x020 /* %[di] */
+#define F_P 0x040 /* %_p */
+#define F_STR 0x080 /* %s */
+#define F_U 0x100 /* %_u */
+#define F_UINT 0x200 /* %[ouXx] */
+#define F_TEXT 0x400 /* no conversions */
+ unsigned int flags; /* flag values */
+ int bcnt; /* byte count */
+ char *cchar; /* conversion character */
+ char *fmt; /* printf format */
+ char *nospace; /* no whitespace version */
+} PR;
+
+typedef struct _fu {
+ struct _fu *nextfu; /* next format unit */
+ struct _pr *nextpr; /* next print unit */
+#define F_IGNORE 0x01 /* %_A */
+#define F_SETREP 0x02 /* rep count set, not default */
+ unsigned int flags; /* flag values */
+ int reps; /* repetition count */
+ int bcnt; /* byte count */
+ char *fmt; /* format string */
+} FU;
+
+typedef struct _fs { /* format strings */
+ struct _fs *nextfs; /* linked list of format strings */
+ struct _fu *nextfu; /* linked list of format units */
+ int bcnt;
+} FS;
+
+extern FU *endfu;
+extern FS *fshead; /* head of format strings list */
+extern ssize_t blocksize; /* data block size */
+extern int deprecated; /* od compatibility */
+extern int exitval; /* final exit value */
+extern ssize_t length; /* max bytes to read */
+extern off_t skip; /* bytes to skip */
+
+enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
+extern enum _vflag vflag;
+
+int block_size(FS *);
+void add(const char *);
+void rewrite(FS *);
+void addfile(char *);
+void display(void);
+void __attribute__((__noreturn__)) usage(FILE *out);
+void conv_c(PR *, u_char *);
+void conv_u(PR *, u_char *);
+int next(char **);
+void newsyntax(int, char ***);
diff --git a/text-utils/hexsyntax.c b/text-utils/hexsyntax.c
new file mode 100644
index 0000000..21cc55e
--- /dev/null
+++ b/text-utils/hexsyntax.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <err.h>
+#include <limits.h>
+#include "hexdump.h"
+#include "nls.h"
+#include "strutils.h"
+#include "c.h"
+
+off_t skip; /* bytes to skip */
+
+
+void
+newsyntax(int argc, char ***argvp)
+{
+ int ch;
+ char **argv;
+
+ argv = *argvp;
+ while ((ch = getopt(argc, argv, "bcCde:f:n:os:vxV")) != -1) {
+ switch (ch) {
+ case 'b':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"");
+ break;
+ case 'c':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"");
+ break;
+ case 'C':
+ add("\"%08.8_Ax\n\"");
+ add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" ");
+ add("\" |\" 16/1 \"%_p\" \"|\\n\"");
+ break;
+ case 'd':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"");
+ break;
+ case 'e':
+ add(optarg);
+ break;
+ case 'f':
+ addfile(optarg);
+ break;
+ case 'n':
+ length = strtosize_or_err(optarg, _("failed to parse length"));
+ break;
+ case 'o':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"");
+ break;
+ case 's':
+ skip = strtosize_or_err(optarg, _("failed to parse offset"));
+ break;
+ case 'v':
+ vflag = ALL;
+ break;
+ case 'x':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"");
+ break;
+ case 'V':
+ printf(_("%s from %s\n"),
+ program_invocation_short_name,
+ PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (!fshead) {
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+ }
+
+ *argvp += optind;
+}
+
+void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out, _("\nUsage:\n"
+ " %s [options] file...\n"),
+ program_invocation_short_name);
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -b one-byte octal display\n"
+ " -c one-byte character display\n"
+ " -C canonical hex+ASCII display\n"
+ " -d two-byte decimal display\n"
+ " -o two-byte octal display\n"
+ " -x two-byte hexadecimal display\n"
+ " -e format format string to be used for displaying data\n"
+ " -f format_file file that contains format strings\n"
+ " -n length interpret only length bytes of input\n"
+ " -s offset skip offset bytes from the beginning\n"
+ " -v display without squeezing similar lines\n"
+ " -V output version information and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/text-utils/line.1 b/text-utils/line.1
new file mode 100644
index 0000000..6305f6b
--- /dev/null
+++ b/text-utils/line.1
@@ -0,0 +1,17 @@
+.\" This page is in the public domain
+.TH LINE 1 "July 2002" "util-linux" "User Commands"
+.SH NAME
+line \- read one line
+.SH SYNOPSIS
+.B line
+.SH DESCRIPTION
+The utility
+.B 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)
+.SH AVAILABILITY
+The line command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/text-utils/line.c b/text-utils/line.c
new file mode 100644
index 0000000..636122e
--- /dev/null
+++ b/text-utils/line.c
@@ -0,0 +1,45 @@
+/*
+ * line - read one line
+ *
+ * Gunnar Ritter, Freiburg i. Br., Germany, December 2000.
+ *
+ * Public Domain.
+ */
+
+/*
+ * This command is deprecated. The utility is in maintenance mode,
+ * meaning we keep them in source tree for backward compatibility
+ * only. Do not waste time making this command better, unless the
+ * fix is about security or other very critical issue.
+ *
+ * See Documentation/deprecated.txt for more information.
+ */
+
+#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(void)
+{
+ doline(0);
+ return status;
+}
diff --git a/text-utils/more.1 b/text-utils/more.1
new file mode 100644
index 0000000..479f037
--- /dev/null
+++ b/text-utils/more.1
@@ -0,0 +1,249 @@
+.\" Copyright (c) 1988, 1990 The Regents of the University of California.
+.\" Copyright (c) 1988 Mark Nudleman
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)more.1 5.15 (Berkeley) 7/29/91
+.\"
+.\" Revised: Fri Dec 25 15:27:27 1992 by root
+.\" 25Dec92: Extensive changes made by Rik Faith (faith@cs.unc.edu) to
+.\" conform with the more 5.19 currently in use by the Linux community.
+.\"
+.\" .Dd July 29, 1991 (Modified December 25, 1992)
+.TH MORE "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+more \- file perusal filter for crt viewing
+.SH SYNOPSIS
+.B more
+[options] file [...]
+.SH DESCRIPTION
+.B more
+is a filter for paging through text one screenful at a time. This version is
+especially primitive. Users should realize that
+.BR less (1)
+provides
+.BR more (1)
+emulation plus extensive enhancements.
+.SH OPTIONS
+Command-line options are described below. Options are also taken from the
+environment variable
+.B MORE
+(make sure to precede them with a dash (``-'')) but command line options will
+override them.
+.It Fl num
+.TP
+.B \-number
+This option specifies an integer
+.I number
+which is the screen size (in lines).
+.TP
+.B \-d
+.B more
+will prompt the user with the message "[Press space to continue, 'q' to
+quit.]" and will display "[Press 'h' for instructions.]" instead of ringing
+the bell when an illegal key is pressed.
+.TP
+.B \-l
+.B more
+usually treats
+.B \&^L
+(form feed) as a special character, and will pause after any line that
+contains a form feed. The
+.B \-l
+option will prevent this behavior.
+.TP
+.B -f
+Causes
+.B more
+to count logical, rather than screen lines (i.e., long lines are not folded).
+.TP
+.B \-p
+Do not scroll. Instead, clear the whole screen and then display the text.
+Notice that this option is switched on automatically if the executable is
+named
+.BR page .
+.TP
+.B \-c
+Do not scroll. Instead, paint each screen from the top, clearing the
+remainder of each line as it is displayed.
+.TP
+.B \-s
+Squeeze multiple blank lines into one.
+.TP
+.B \-u
+Suppress underlining.
+.TP
+.B +/
+The
+.B +/
+option specifies a string that will be searched for before each file is
+displayed.
+.TP
+.B +number
+Start at line
+.IR number .
+.SH COMMANDS
+Interactive commands for
+.B more
+are based on
+.BR vi (1).
+Some commands may be preceded by a decimal number, called k in the
+descriptions below. In the following descriptions,
+.B ^X
+means
+.BR control-X .
+.PP
+.RS
+.PD 1
+.TP 10
+.BR h \ or \ ?
+Help: display a summary of these commands. If you forget all the other
+commands, remember this one.
+.TP
+.B SPACE
+Display next k lines of text. Defaults to current screen size.
+.TP
+.B z
+Display next k lines of text. Defaults to current screen size. Argument
+becomes new default.
+.TP
+.B RETURN
+Display next k lines of text. Defaults to 1. Argument becomes new default.
+.TP
+.BR d \ or \ \&^D
+Scroll k lines. Default is current scroll size, initially 11. Argument
+becomes new default.
+.TP
+.BR q \ or \ Q \ or \ INTERRUPT
+Exit.
+.TP
+.B s
+Skip forward k lines of text. Defaults to 1.
+.TP
+.B f
+Skip forward k screenfuls of text. Defaults to 1.
+.TP
+.BR b \ or \ \&^B
+Skip backwards k screenfuls of text. Defaults to 1. Only works with files,
+not pipes.
+.TP
+.B \'
+Go to place where previous search started.
+.TP
+.B =
+Display current line number.
+.TP
+.B \&/pattern
+Search for kth occurrence of regular expression. Defaults to 1.
+.TP
+.B n
+Search for kth occurrence of last regular expression. Defaults to 1.
+.TP
+.BR !command \ or \ :!command
+Execute
+.I command
+in a subshell.
+.TP
+.B v
+Start up an editor at current line. The editor is taken from the environment
+variable
+.B VISUAL
+if defined, or
+.B EDITOR
+if
+.B VISUAL
+is not defined, or defaults
+to
+.B vi
+if neither
+.B VISUAL
+nor
+.B EDITOR
+is defined.
+.TP
+.B \&^L
+Redraw screen.
+.TP
+.B :n
+Go to kth next file. Defaults to 1.
+.TP
+.B :P
+Go to kth previous file. Defaults to 1.
+.TP
+.B :f
+Display current file name and line number.
+.TP
+.B \&.
+Repeat previous command.
+.SH ENVIRONMENT
+More utilizes the following environment variables, if they exist:
+.TP
+.B MORE
+This variable may be set with favored options to
+.BR more .
+.TP
+.B SHELL
+Current shell in use (normally set by the shell at login time).
+.TP
+.B TERM
+Specifies terminal type, used by more to get the terminal
+characteristics necessary to manipulate the screen.
+.TP
+.B VISUAL
+Editor the user is preferring. Used when key command
+.I v
+is pressed.
+.TP
+.B EDITOR
+Editor of choise when
+.B VISUAL
+is not specified.
+.SH SEE ALSO
+.BR vi (1),
+.BR less (1)
+.SH AUTHORS
+Eric Shienbrood, UC Berkeley
+.br
+Modified by Geoff Peck, UCB to add underlining, single spacing
+.br
+Modified by John Foderaro, UCB to add -c and MORE environment variable
+.SH HISTORY
+The
+.B more
+command appeared in 3.0BSD. This man page documents
+.B more
+version 5.19 (Berkeley 6/29/88), which is currently in use in the Linux
+community. Documentation was produced using several other versions of the
+man page, and extensive inspection of the source code.
+.SH AVAILABILITY
+The more command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/more.c b/text-utils/more.c
new file mode 100644
index 0000000..58c99cc
--- /dev/null
+++ b/text-utils/more.c
@@ -0,0 +1,2197 @@
+/*
+ * Copyright (C) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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.c - General purpose tty output filter and file perusal program
+**
+** by Eric Shienbrood, UC Berkeley
+**
+** modified by Geoff Peck, UCB to add underlining, single spacing
+** modified by John Foderaro, UCB to add -c and MORE environment variable
+** modified by Erik Troan <ewt@redhat.com> to be more posix and so compile
+** on linux/axp.
+** modified by Kars de Jong <jongk@cs.utwente.nl> to use terminfo instead
+** of termcap.
+ 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ - added Native Language Support
+ 1999-03-19 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ - more nls translatable strings
+ 1999-05-09 aeb - applied a RedHat patch (setjmp->sigsetjmp); without it
+ a second ^Z would fail.
+ 1999-05-09 aeb - undone Kars' work, so that more works without
+ libcurses (and hence can be in /bin with libcurses being in /usr/lib
+ which may not be mounted). However, when termcap is not present curses
+ can still be used.
+ 2010-10-21 Davidlohr Bueso <dave@gnu.org>
+ - modified mem allocation handling for util-linux
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h> /* for alloca() */
+#include <stdarg.h> /* for va_start() etc */
+#include <sys/param.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <setjmp.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include "strutils.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "closestream.h"
+
+#include <regex.h>
+
+#ifndef XTABS
+#define XTABS TAB3
+#endif /* XTABS */
+
+#define VI "vi" /* found on the user's path */
+
+#define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
+#define Ftell(f) file_pos
+#define Fseek(f,off) (file_pos=off,fseek(f,off,0))
+#define Getc(f) (++file_pos, getc(f))
+#define Ungetc(c,f) (--file_pos, ungetc(c,f))
+#define putcerr(c) fputc(c, stderr)
+#define putserr(s) fputs(s, stderr)
+#define putsout(s) fputs(s, stdout)
+
+#define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
+
+/* some function declarations */
+void initterm(void);
+void kill_line(void);
+void doclear(void);
+void cleareol(void);
+void clreos(void);
+void home(void);
+void more_error (char *mess);
+void do_shell (char *filename);
+int colon (char *filename, int cmd, int nlines);
+int expand (char **outbuf, char *inbuf);
+void argscan(char *s);
+void rdline (register FILE *f);
+void copy_file(register FILE *f);
+void search(char buf[], FILE *file, register int n);
+void skipf (register int nskip);
+void skiplns(register int n, register FILE *f);
+void screen (register FILE *f, register int num_lines);
+int command (char *filename, register FILE *f);
+void erasep (register int col);
+void show (register char ch);
+void set_tty(void);
+void reset_tty(void);
+void ttyin (char buf[], register int nmax, char pchar);
+int number(char *cmd);
+int readch (void);
+int get_line(register FILE *f, int *length);
+void prbuf (register char *s, register int n);
+void execute (char *filename, char *cmd, ...);
+FILE *checkf (char *, int *);
+void prepare_line_buffer(void);
+
+#define TBUFSIZ 1024
+#define LINSIZ 256 /* minimal Line buffer size */
+#define ctrl(letter) (letter & 077)
+#define RUBOUT '\177'
+#define ESC '\033'
+#define QUIT '\034'
+#define SCROLL_LEN 11
+#define LINES_PER_PAGE 24
+#define NUM_COLUMNS 80
+#define TERMINAL_BUF 4096
+#define INIT_BUF 80
+#define SHELL_LINE 1000
+#define COMMAND_BUF 200
+#define REGERR_BUF NUM_COLUMNS
+
+struct termios otty, savetty0;
+long file_pos, file_size;
+int fnum, no_intty, no_tty, slow_tty;
+int dum_opt, dlines;
+void onquit(int), onsusp(int), chgwinsz(int), end_it(int);
+int nscroll = SCROLL_LEN; /* Number of lines scrolled by 'd' */
+int fold_opt = 1; /* Fold long lines */
+int stop_opt = 1; /* Stop after form feeds */
+int ssp_opt = 0; /* Suppress white space */
+int ul_opt = 1; /* Underline as best we can */
+int promptlen;
+int Currline; /* Line we are currently at */
+int startup = 1;
+int firstf = 1;
+int notell = 1;
+int docrterase = 0;
+int docrtkill = 0;
+int bad_so; /* True if overwriting does not turn
+ off standout */
+int inwait, Pause, errors;
+int within; /* true if we are within a file,
+ false if we are between files */
+int hard, dumb, noscroll, hardtabs, clreol, eatnl;
+int catch_susp; /* We should catch the SIGTSTP signal */
+char **fnames; /* The list of file names */
+int nfiles; /* Number of files left to process */
+char *shell; /* The name of the shell to use */
+int shellp; /* A previous shell command exists */
+sigjmp_buf restore;
+char *Line; /* Line buffer */
+size_t LineLen; /* size of Line buffer */
+int Lpp = LINES_PER_PAGE; /* lines per page */
+char *Clear; /* clear screen */
+char *eraseln; /* erase line */
+char *Senter, *Sexit; /* enter and exit standout mode */
+char *ULenter, *ULexit; /* enter and exit underline mode */
+char *chUL; /* underline character */
+char *chBS; /* backspace character */
+char *Home; /* go to home */
+char *cursorm; /* cursor movement */
+char cursorhome[40]; /* contains cursor movement to home */
+char *EodClr; /* clear rest of screen */
+int Mcol = NUM_COLUMNS; /* number of columns */
+int Wrap = 1; /* set if automargins */
+int soglitch; /* terminal has standout mode glitch */
+int ulglitch; /* terminal has underline mode glitch */
+int pstate = 0; /* current UL state */
+static int magic(FILE *, char *);
+char *previousre; /* previous search() buf[] item */
+struct {
+ long chrctr, line;
+} context, screen_start;
+extern char PC; /* pad character */
+
+#ifdef HAVE_NCURSES_H
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#endif /* HAVE_NCURSES_H */
+
+#if defined(HAVE_NCURSES_H) || defined(HAVE_NCURSES_NCURSES_H)
+# include <term.h> /* include after <curses.h> */
+
+#define TERM_AUTO_RIGHT_MARGIN "am"
+#define TERM_CEOL "xhp"
+#define TERM_CLEAR "clear"
+#define TERM_CLEAR_TO_LINE_END "el"
+#define TERM_CLEAR_TO_SCREEN_END "ed"
+#define TERM_COLS "cols"
+#define TERM_CURSOR_ADDRESS "cup"
+#define TERM_EAT_NEW_LINE "xenl"
+#define TERM_ENTER_UNDERLINE "smul"
+#define TERM_EXIT_STANDARD_MODE "rmso"
+#define TERM_EXIT_UNDERLINE "rmul"
+#define TERM_HARD_COPY "hc"
+#define TERM_HOME "home"
+#define TERM_LINE_DOWN "cud1"
+#define TERM_LINES "lines"
+#define TERM_OVER_STRIKE "os"
+#define TERM_PAD_CHAR "pad"
+#define TERM_STANDARD_MODE "smso"
+#define TERM_STD_MODE_GLITCH "xmc"
+#define TERM_UNDERLINE_CHAR "uc"
+#define TERM_UNDERLINE "ul"
+
+static void my_putstring(char *s) {
+ tputs (s, fileno(stdout), putchar); /* putp(s); */
+}
+
+static void my_setupterm(char *term, int fildes, int *errret) {
+ setupterm(term, fildes, errret);
+}
+
+static int my_tgetnum(char *s) {
+ return tigetnum(s);
+}
+
+static int my_tgetflag(char *s) {
+ return tigetflag(s);
+}
+
+static char *my_tgetstr(char *s) {
+ return tigetstr(s);
+}
+
+static char *my_tgoto(char *cap, int col, int row) {
+ return tparm(cap, col, row);
+}
+
+#elif defined(HAVE_LIBTERMCAP) /* !ncurses */
+
+#include <termcap.h>
+
+#define TERM_AUTO_RIGHT_MARGIN "am"
+#define TERM_CEOL "xs"
+#define TERM_CLEAR "cl"
+#define TERM_CLEAR_TO_LINE_END "ce"
+#define TERM_CLEAR_TO_SCREEN_END "cd"
+#define TERM_COLS "co"
+#define TERM_CURSOR_ADDRESS "cm"
+#define TERM_EAT_NEW_LINE "xn"
+#define TERM_ENTER_UNDERLINE "us"
+#define TERM_EXIT_STANDARD_MODE "se"
+#define TERM_EXIT_UNDERLINE "ue"
+#define TERM_HARD_COPY "hc"
+#define TERM_HOME "ho"
+#define TERM_LINE_DOWN "le"
+#define TERM_LINES "li"
+#define TERM_OVER_STRIKE "os"
+#define TERM_PAD_CHAR "pc"
+#define TERM_STANDARD_MODE "so"
+#define TERM_STD_MODE_GLITCH "sg"
+#define TERM_UNDERLINE_CHAR "uc"
+#define TERM_UNDERLINE "ul"
+
+char termbuffer[TERMINAL_BUF];
+char tcbuffer[TERMINAL_BUF];
+char *strbuf = termbuffer;
+
+static void my_putstring(char *s) {
+ tputs (s, fileno(stdout), putchar);
+}
+
+static void my_setupterm(char *term, int fildes, int *errret) {
+ *errret = tgetent(tcbuffer, term);
+}
+
+static int my_tgetnum(char *s) {
+ return tgetnum(s);
+}
+
+static int my_tgetflag(char *s) {
+ return tgetflag(s);
+}
+
+static char *my_tgetstr(char *s) {
+ return tgetstr(s, &strbuf);
+}
+
+static char *my_tgoto(char *cap, int col, int row) {
+ return tgoto(cap, col, row);
+}
+
+#endif /* HAVE_LIBTERMCAP */
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out,
+ _("Usage: %s [options] file...\n\n"),
+ program_invocation_short_name);
+ fprintf(out,
+ _("Options:\n"
+ " -d display help instead of ring bell\n"
+ " -f count logical, rather than screen lines\n"
+ " -l suppress pause after form feed\n"
+ " -p suppress scroll, clean screen and display text\n"
+ " -c suppress scroll, display text and clean line ends\n"
+ " -u suppress underlining\n"
+ " -s squeeze multiple blank lines into one\n"
+ " -NUM specify the number of lines per screenful\n"
+ " +NUM display file beginning from line number NUM\n"
+ " +/STRING display file beginning from search string match\n"
+ " -V output version information and exit\n"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv) {
+ FILE *f;
+ char *s;
+ int ch;
+ int left;
+ int prnames = 0;
+ int initopt = 0;
+ int srchopt = 0;
+ int clearit = 0;
+ int initline = 0;
+ char *initbuf = NULL;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ nfiles = argc;
+ fnames = argv;
+ setlocale(LC_ALL, "");
+ initterm ();
+
+ /* Auto set no scroll on when binary is called page */
+ if (!(strcmp(program_invocation_short_name, "page")))
+ noscroll++;
+
+ prepare_line_buffer();
+
+ nscroll = Lpp/2 - 1;
+ if (nscroll <= 0)
+ nscroll = 1;
+
+ if ((s = getenv("MORE")) != NULL)
+ argscan(s);
+
+ while (--nfiles > 0) {
+ if ((ch = (*++fnames)[0]) == '-') {
+ argscan(*fnames+1);
+ }
+ else if (ch == '+') {
+ s = *fnames;
+ if (*++s == '/') {
+ srchopt++;
+ initbuf = xstrdup(s + 1);
+ }
+ else {
+ initopt++;
+ for (initline = 0; *s != '\0'; s++)
+ if (isdigit (*s))
+ initline = initline*10 + *s -'0';
+ --initline;
+ }
+ }
+ else break;
+ }
+ /* allow clreol only if Home and eraseln and EodClr strings are
+ * defined, and in that case, make sure we are in noscroll mode
+ */
+ if (clreol) {
+ if((Home == NULL) || (*Home == '\0') ||
+ (eraseln == NULL) || (*eraseln == '\0') ||
+ (EodClr == NULL) || (*EodClr == '\0') )
+ clreol = 0;
+ else noscroll = 1;
+ }
+ if (dlines == 0)
+ dlines = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
+ left = dlines;
+ if (nfiles > 1)
+ prnames++;
+ if (!no_intty && nfiles == 0)
+ usage(stderr);
+ else
+ f = stdin;
+ if (!no_tty) {
+ signal(SIGQUIT, onquit);
+ signal(SIGINT, end_it);
+#ifdef SIGWINCH
+ signal(SIGWINCH, chgwinsz);
+#endif /* SIGWINCH */
+ if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
+ signal(SIGTSTP, onsusp);
+ catch_susp++;
+ }
+ stty (fileno(stderr), &otty);
+ }
+ if (no_intty) {
+ if (no_tty)
+ copy_file (stdin);
+ else {
+ if ((ch = Getc (f)) == '\f')
+ doclear();
+ else {
+ Ungetc (ch, f);
+ if (noscroll && (ch != EOF)) {
+ if (clreol)
+ home ();
+ else
+ doclear ();
+ }
+ }
+ if (srchopt)
+ {
+ previousre = xstrdup(initbuf);
+ search (initbuf, stdin, 1);
+ if (noscroll)
+ left--;
+ }
+ else if (initopt)
+ skiplns (initline, stdin);
+ screen (stdin, left);
+ }
+ no_intty = 0;
+ prnames++;
+ firstf = 0;
+ }
+
+ while (fnum < nfiles) {
+ if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
+ context.line = context.chrctr = 0;
+ Currline = 0;
+ if (firstf) sigsetjmp (restore, 1);
+ if (firstf) {
+ firstf = 0;
+ if (srchopt) {
+ previousre = xstrdup(initbuf);
+ search (initbuf, f, 1);
+ if (noscroll)
+ left--;
+ }
+ else if (initopt)
+ skiplns (initline, f);
+ }
+ else if (fnum < nfiles && !no_tty) {
+ sigsetjmp (restore, 1);
+ left = command (fnames[fnum], f);
+ }
+ if (left != 0) {
+ if ((noscroll || clearit) && (file_size != LONG_MAX)) {
+ if (clreol)
+ home ();
+ else
+ doclear ();
+ }
+ if (prnames) {
+ if (bad_so)
+ erasep (0);
+ if (clreol)
+ cleareol ();
+ putsout("::::::::::::::");
+ if (promptlen > 14)
+ erasep (14);
+ putchar('\n');
+ if(clreol) cleareol();
+ puts(fnames[fnum]);
+ if(clreol) cleareol();
+ puts("::::::::::::::");
+ if (left > Lpp - 4)
+ left = Lpp - 4;
+ }
+ if (no_tty)
+ copy_file (f);
+ else {
+ within++;
+ screen(f, left);
+ within = 0;
+ }
+ }
+ sigsetjmp (restore, 1);
+ fflush(stdout);
+ fclose(f);
+ screen_start.line = screen_start.chrctr = 0L;
+ context.line = context.chrctr = 0L;
+ }
+ fnum++;
+ firstf = 0;
+ }
+ free (previousre);
+ free (initbuf);
+ reset_tty ();
+ exit(EXIT_SUCCESS);
+}
+
+void argscan(char *s) {
+ int seen_num = 0;
+
+ while (*s != '\0') {
+ switch (*s) {
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8':
+ case '9':
+ if (!seen_num) {
+ dlines = 0;
+ seen_num = 1;
+ }
+ dlines = dlines*10 + *s - '0';
+ break;
+ case 'd':
+ dum_opt = 1;
+ break;
+ case 'l':
+ stop_opt = 0;
+ break;
+ case 'f':
+ fold_opt = 0;
+ break;
+ case 'p':
+ noscroll++;
+ break;
+ case 'c':
+ clreol++;
+ break;
+ case 's':
+ ssp_opt = 1;
+ break;
+ case 'u':
+ ul_opt = 0;
+ break;
+ case '-': case ' ': case '\t':
+ break;
+ case 'V':
+ printf(_("more (%s)\n"), PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ warnx(_("unknown option -%s"), s);
+ usage(stderr);
+ break;
+ }
+ s++;
+ }
+}
+
+
+/*
+** Check whether the file named by fs is an ASCII file which the user may
+** access. If it is, return the opened file. Otherwise return NULL.
+*/
+
+FILE *
+checkf (fs, clearfirst)
+ register char *fs;
+ int *clearfirst;
+{
+ struct stat stbuf;
+ register FILE *f;
+ int c;
+
+ if (stat (fs, &stbuf) == -1) {
+ (void)fflush(stdout);
+ if (clreol)
+ cleareol ();
+ perror(fs);
+ return((FILE *)NULL);
+ }
+ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
+ printf(_("\n*** %s: directory ***\n\n"), fs);
+ return((FILE *)NULL);
+ }
+ if ((f = Fopen(fs, "r")) == NULL) {
+ (void)fflush(stdout);
+ perror(fs);
+ return((FILE *)NULL);
+ }
+ if (magic(f, fs))
+ return((FILE *)NULL);
+ fcntl(fileno(f), F_SETFD, FD_CLOEXEC );
+ c = Getc(f);
+ *clearfirst = (c == '\f');
+ Ungetc (c, f);
+ if ((file_size = stbuf.st_size) == 0)
+ file_size = LONG_MAX;
+ return(f);
+}
+
+/*
+ * magic --
+ * check for file magic numbers. This code would best be shared with
+ * the file(1) program or, perhaps, more should not try to be so smart.
+ */
+static int
+magic(f, fs)
+ FILE *f;
+ char *fs;
+{
+ signed char twobytes[2];
+
+ /* don't try to look ahead if the input is unseekable */
+ if (fseek(f, 0L, SEEK_SET))
+ return 0;
+
+ if (fread(twobytes, 2, 1, f) == 1) {
+ switch(twobytes[0] + (twobytes[1]<<8)) {
+ case 0407: /* a.out obj */
+ case 0410: /* a.out exec */
+ case 0413: /* a.out demand exec */
+ case 0405:
+ case 0411:
+ case 0177545:
+ case 0x457f: /* simple ELF detection */
+ printf(_("\n******** %s: Not a text file ********\n\n"), fs);
+ (void)fclose(f);
+ return 1;
+ }
+ }
+ (void)fseek(f, 0L, SEEK_SET); /* rewind() not necessary */
+ return 0;
+}
+
+/*
+** Print out the contents of the file f, one screenful at a time.
+*/
+
+#define STOP -10
+
+void screen (register FILE *f, register int num_lines)
+{
+ register int c;
+ register int nchars;
+ int length; /* length of current line */
+ static int prev_len = 1; /* length of previous line */
+
+ for (;;) {
+ while (num_lines > 0 && !Pause) {
+ if ((nchars = get_line (f, &length)) == EOF)
+ {
+ if (clreol)
+ clreos();
+ return;
+ }
+ if (ssp_opt && length == 0 && prev_len == 0)
+ continue;
+ prev_len = length;
+ if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
+ erasep (0);
+ /* must clear before drawing line since tabs on some terminals
+ * do not erase what they tab over.
+ */
+ if (clreol)
+ cleareol ();
+ prbuf (Line, length);
+ if (nchars < promptlen)
+ erasep (nchars); /* erasep () sets promptlen to 0 */
+ else promptlen = 0;
+ /* is this needed?
+ * if (clreol)
+ * cleareol(); * must clear again in case we wrapped *
+ */
+ if (nchars < Mcol || !fold_opt)
+ prbuf("\n", 1); /* will turn off UL if necessary */
+ if (nchars == STOP)
+ break;
+ num_lines--;
+ }
+ if (pstate) {
+ my_putstring (ULexit);
+ pstate = 0;
+ }
+ fflush(stdout);
+ if ((c = Getc(f)) == EOF)
+ {
+ if (clreol)
+ clreos ();
+ return;
+ }
+
+ if (Pause && clreol)
+ clreos ();
+ Ungetc (c, f);
+ sigsetjmp (restore, 1);
+ Pause = 0; startup = 0;
+ if ((num_lines = command (NULL, f)) == 0)
+ return;
+ if (hard && promptlen > 0)
+ erasep (0);
+ if (noscroll && num_lines >= dlines)
+ {
+ if (clreol)
+ home();
+ else
+ doclear ();
+ }
+ screen_start.line = Currline;
+ screen_start.chrctr = Ftell (f);
+ }
+}
+
+/*
+** Come here if a quit signal is received
+*/
+
+void onquit(int dummy __attribute__ ((__unused__)))
+{
+ signal(SIGQUIT, SIG_IGN);
+ if (!inwait) {
+ putchar ('\n');
+ if (!startup) {
+ signal(SIGQUIT, onquit);
+ siglongjmp (restore, 1);
+ }
+ else
+ Pause++;
+ }
+ else if (!dum_opt && notell) {
+ promptlen += fprintf(stderr, _("[Use q or Q to quit]"));
+ notell = 0;
+ }
+ signal(SIGQUIT, onquit);
+}
+
+/*
+** Come here if a signal for a window size change is received
+*/
+
+#ifdef SIGWINCH
+void chgwinsz(int dummy __attribute__ ((__unused__)))
+{
+ struct winsize win;
+
+ signal(SIGWINCH, SIG_IGN);
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
+ if (win.ws_row != 0) {
+ Lpp = win.ws_row;
+ nscroll = Lpp/2 - 1;
+ if (nscroll <= 0)
+ nscroll = 1;
+ dlines = Lpp - 1; /* was: Lpp - (noscroll ? 1 : 2) */
+ }
+ if (win.ws_col != 0)
+ Mcol = win.ws_col;
+ }
+ (void) signal(SIGWINCH, chgwinsz);
+}
+#endif /* SIGWINCH */
+
+/*
+** Clean up terminal state and exit. Also come here if interrupt signal received
+*/
+
+void __attribute__((__noreturn__))
+end_it (int dummy __attribute__ ((__unused__)))
+{
+ reset_tty ();
+ if (clreol) {
+ putchar ('\r');
+ clreos ();
+ fflush (stdout);
+ }
+ else if (!clreol && (promptlen > 0)) {
+ kill_line ();
+ fflush (stdout);
+ }
+ else
+ putcerr('\n');
+ _exit(EXIT_SUCCESS);
+}
+
+void copy_file(register FILE *f) {
+ register int c;
+
+ while ((c = getc(f)) != EOF)
+ putchar(c);
+}
+
+#define ringbell() putcerr('\007')
+
+static void prompt (char *filename)
+{
+ if (clreol)
+ cleareol ();
+ else if (promptlen > 0)
+ kill_line ();
+ if (!hard) {
+ promptlen = 0;
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ promptlen += (2 * soglitch);
+ }
+ if (clreol)
+ cleareol ();
+ promptlen += printf(_("--More--"));
+ if (filename != NULL) {
+ promptlen += printf(_("(Next file: %s)"), filename);
+ } else if (!no_intty) {
+ promptlen += printf("(%d%%)", (int) ((file_pos * 100) / file_size));
+ }
+ if (dum_opt) {
+ promptlen += printf(_("[Press space to continue, 'q' to quit.]"));
+ }
+ if (Senter && Sexit)
+ my_putstring (Sexit);
+ if (clreol)
+ clreos ();
+ fflush(stdout);
+ }
+ else
+ ringbell();
+ inwait++;
+}
+
+void prepare_line_buffer(void)
+{
+ char *nline;
+ size_t nsz = Mcol * 4;
+
+ if (LineLen >= nsz)
+ return;
+
+ if (nsz < LINSIZ)
+ nsz = LINSIZ;
+
+ nline = xrealloc(Line, nsz);
+ Line = nline;
+ LineLen = nsz;
+}
+
+/*
+ * Get a logical line
+ */
+
+int get_line(register FILE *f, int *length)
+{
+ int c;
+ char *p;
+ int column;
+ static int colflg;
+
+#ifdef HAVE_WIDECHAR
+ size_t i;
+ wchar_t wc;
+ int wc_width;
+ mbstate_t state, state_bak; /* Current status of the stream. */
+ char mbc[MB_LEN_MAX]; /* Buffer for one multibyte char. */
+ size_t mblength; /* Byte length of multibyte char. */
+ size_t mbc_pos = 0; /* Position of the MBC. */
+ int use_mbc_buffer_flag = 0; /* If 1, mbc has data. */
+ int break_flag = 0; /* If 1, exit while(). */
+ long file_pos_bak = Ftell (f);
+
+ memset (&state, '\0', sizeof (mbstate_t));
+#endif /* HAVE_WIDECHAR */
+
+ prepare_line_buffer();
+
+ p = Line;
+ column = 0;
+ c = Getc (f);
+ if (colflg && c == '\n') {
+ Currline++;
+ c = Getc (f);
+ }
+ while (p < &Line[LineLen - 1]) {
+#ifdef HAVE_WIDECHAR
+ if (fold_opt && use_mbc_buffer_flag && MB_CUR_MAX > 1) {
+ use_mbc_buffer_flag = 0;
+ state_bak = state;
+ mbc[mbc_pos++] = c;
+process_mbc:
+ mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
+
+ switch (mblength) {
+ case (size_t)-2: /* Incomplete multibyte character. */
+ use_mbc_buffer_flag = 1;
+ state = state_bak;
+ break;
+
+ case (size_t)-1: /* Invalid as a multibyte character. */
+ *p++ = mbc[0];
+ state = state_bak;
+ column++;
+ file_pos_bak++;
+
+ if (column >= Mcol) {
+ Fseek (f, file_pos_bak);
+ } else {
+ memmove (mbc, mbc + 1, --mbc_pos);
+ if (mbc_pos > 0) {
+ mbc[mbc_pos] = '\0';
+ goto process_mbc;
+ }
+ }
+ break;
+
+ default:
+ wc_width = wcwidth (wc);
+
+ if (column + wc_width > Mcol) {
+ Fseek (f, file_pos_bak);
+ break_flag = 1;
+ } else {
+ for (i = 0; i < mbc_pos; i++)
+ *p++ = mbc[i];
+ if (wc_width > 0)
+ column += wc_width;
+ }
+ }
+
+ if (break_flag || column >= Mcol)
+ break;
+
+ c = Getc (f);
+ continue;
+ }
+#endif /* HAVE_WIDECHAR */
+ if (c == EOF) {
+ if (p > Line) {
+ *p = '\0';
+ *length = p - Line;
+ return (column);
+ }
+ *length = p - Line;
+ return (EOF);
+ }
+ if (c == '\n') {
+ Currline++;
+ break;
+ }
+
+ *p++ = c;
+#if 0
+ if (c == '\033') { /* ESC */
+ c = Getc(f);
+ while (c > ' ' && c < '0' && p < &Line[LineLen - 1]) {
+ *p++ = c;
+ c = Getc(f);
+ }
+ if (c >= '0' && c < '\177' && p < &Line[LineLen - 1]) {
+ *p++ = c;
+ c = Getc(f);
+ continue;
+ }
+ }
+#endif /* 0 */
+ if (c == '\t') {
+ if (!hardtabs || (column < promptlen && !hard)) {
+ if (hardtabs && eraseln && !dumb) {
+ column = 1 + (column | 7);
+ my_putstring (eraseln);
+ promptlen = 0;
+ }
+ else {
+ for (--p; p < &Line[LineLen - 1];) {
+ *p++ = ' ';
+ if ((++column & 7) == 0)
+ break;
+ }
+ if (column >= promptlen) promptlen = 0;
+ }
+ } else
+ column = 1 + (column | 7);
+ } else if (c == '\b' && column > 0) {
+ column--;
+ } else if (c == '\r') {
+ int next = Getc(f);
+ if (next == '\n') {
+ p--;
+ Currline++;
+ break;
+ }
+ Ungetc(next,f);
+ column = 0;
+ } else if (c == '\f' && stop_opt) {
+ p[-1] = '^';
+ *p++ = 'L';
+ column += 2;
+ Pause++;
+ } else if (c == EOF) {
+ *length = p - Line;
+ return (column);
+ } else {
+#ifdef HAVE_WIDECHAR
+ if (fold_opt && MB_CUR_MAX > 1) {
+ memset (mbc, '\0', MB_LEN_MAX);
+ mbc_pos = 0;
+ mbc[mbc_pos++] = c;
+ state_bak = state;
+
+ mblength = mbrtowc (&wc, mbc, mbc_pos, &state);
+
+ /* The value of mblength is always less than 2 here. */
+ switch (mblength) {
+ case (size_t)-2:
+ p--;
+ file_pos_bak = Ftell (f) - 1;
+ state = state_bak;
+ use_mbc_buffer_flag = 1;
+ break;
+
+ case (size_t)-1:
+ state = state_bak;
+ column++;
+ break;
+
+ default:
+ wc_width = wcwidth (wc);
+ if (wc_width > 0)
+ column += wc_width;
+ }
+ } else
+#endif /* HAVE_WIDECHAR */
+ {
+ if (isprint(c))
+ column++;
+ }
+ }
+
+ if (column >= Mcol && fold_opt)
+ break;
+ c = Getc (f);
+ }
+ if (column >= Mcol && Mcol > 0) {
+ if (!Wrap) {
+ *p++ = '\n';
+ }
+ }
+ colflg = column == Mcol && fold_opt;
+ if (colflg && eatnl && Wrap) {
+ *p++ = '\n'; /* simulate normal wrap */
+ }
+ *length = p - Line;
+ *p = 0;
+ return (column);
+}
+
+/*
+** Erase the rest of the prompt, assuming we are starting at column col.
+*/
+
+void erasep (register int col)
+{
+
+ if (promptlen == 0)
+ return;
+ if (hard) {
+ putchar ('\n');
+ }
+ else {
+ if (col == 0)
+ putchar ('\r');
+ if (!dumb && eraseln)
+ my_putstring (eraseln);
+ else
+ for (col = promptlen - col; col > 0; col--)
+ putchar (' ');
+ }
+ promptlen = 0;
+}
+
+/*
+** Erase the current line entirely
+*/
+
+void kill_line()
+{
+ erasep(0);
+ if (!eraseln || dumb)
+ putchar('\r');
+}
+
+/*
+ * force clear to end of line
+ */
+void cleareol()
+{
+ my_putstring(eraseln);
+}
+
+void clreos()
+{
+ my_putstring(EodClr);
+}
+
+/* Print a buffer of n characters */
+
+void prbuf (register char *s, register int n)
+{
+ register char c; /* next output character */
+ register int state; /* next output char's UL state */
+#define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
+
+ while (--n >= 0)
+ if (!ul_opt)
+ putchar (*s++);
+ else {
+ if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
+ s++;
+ continue;
+ }
+ if ((state = wouldul(s, n)) != 0) {
+ c = (*s == '_')? s[2] : *s ;
+ n -= 2;
+ s += 3;
+ } else
+ c = *s++;
+ if (state != pstate) {
+ if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
+ state = 1;
+ else
+ my_putstring(state ? ULenter : ULexit);
+ }
+ if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
+#ifdef HAVE_WIDECHAR
+ {
+ wchar_t wc;
+ size_t mblength;
+ mbstate_t mbstate;
+ memset (&mbstate, '\0', sizeof (mbstate_t));
+ s--; n++;
+ mblength = mbrtowc (&wc, s, n, &mbstate);
+ if (mblength == (size_t) -2 || mblength == (size_t) -1)
+ mblength = 1;
+ while (mblength--)
+ putchar (*s++);
+ n += mblength;
+ }
+#else
+ putchar(c);
+#endif /* HAVE_WIDECHAR */
+ if (state && *chUL) {
+ putsout(chBS);
+ my_putstring(chUL);
+ }
+ pstate = state;
+ }
+}
+
+/*
+** Clear the screen
+*/
+void
+doclear()
+{
+ if (Clear && !hard) {
+ my_putstring(Clear);
+
+ /* Put out carriage return so that system doesn't
+ ** get confused by escape sequences when expanding tabs
+ */
+ putchar ('\r');
+ promptlen = 0;
+ }
+}
+
+/*
+ * Go to home position
+ */
+void
+home()
+{
+ my_putstring(Home);
+}
+
+static int lastcmd, lastarg, lastp;
+static int lastcolon;
+char shell_line[SHELL_LINE];
+
+/*
+** Read a command and do it. A command consists of an optional integer
+** argument followed by the command character. Return the number of lines
+** to display in the next screenful. If there is nothing more to display
+** in the current file, zero is returned.
+*/
+
+int command (char *filename, register FILE *f)
+{
+ register int nlines;
+ register int retval = 0;
+ register int c;
+ char colonch;
+ int done;
+ char comchar, cmdbuf[INIT_BUF];
+
+#define ret(val) retval=val;done++;break
+
+ done = 0;
+ if (!errors)
+ prompt (filename);
+ else
+ errors = 0;
+ for (;;) {
+ nlines = number (&comchar);
+ lastp = colonch = 0;
+ if (comchar == '.') { /* Repeat last command */
+ lastp++;
+ comchar = lastcmd;
+ nlines = lastarg;
+ if (lastcmd == ':')
+ colonch = lastcolon;
+ }
+ lastcmd = comchar;
+ lastarg = nlines;
+ if ((cc_t) comchar == otty.c_cc[VERASE]) {
+ kill_line ();
+ prompt (filename);
+ continue;
+ }
+ switch (comchar) {
+ case ':':
+ retval = colon (filename, colonch, nlines);
+ if (retval >= 0)
+ done++;
+ break;
+ case 'b':
+ case ctrl('B'):
+ {
+ register int initline;
+
+ if (no_intty) {
+ ringbell();
+ return (-1);
+ }
+
+ if (nlines == 0) nlines++;
+
+ putchar ('\r');
+ erasep (0);
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ if (nlines != 1)
+ printf(_("...back %d pages"), nlines);
+ else
+ putsout(_("...back 1 page"));
+ if (clreol)
+ cleareol ();
+ putchar('\n');
+
+ initline = Currline - dlines * (nlines + 1);
+ if (! noscroll)
+ --initline;
+ if (initline < 0) initline = 0;
+ Fseek(f, 0L);
+ Currline = 0; /* skiplns() will make Currline correct */
+ skiplns(initline, f);
+ if (! noscroll) {
+ ret(dlines + 1);
+ }
+ else {
+ ret(dlines);
+ }
+ }
+ case ' ':
+ case 'z':
+ if (nlines == 0) nlines = dlines;
+ else if (comchar == 'z') dlines = nlines;
+ ret (nlines);
+ case 'd':
+ case ctrl('D'):
+ if (nlines != 0) nscroll = nlines;
+ ret (nscroll);
+ case 'q':
+ case 'Q':
+ end_it (0);
+ case 's':
+ case 'f':
+ case ctrl('F'):
+ if (nlines == 0) nlines++;
+ if (comchar == 'f')
+ nlines *= dlines;
+ putchar ('\r');
+ erasep (0);
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ if (nlines == 1)
+ putsout(_("...skipping one line"));
+ else
+ printf(_("...skipping %d lines"), nlines);
+
+ if (clreol)
+ cleareol ();
+ putchar('\n');
+
+ while (nlines > 0) {
+ while ((c = Getc (f)) != '\n')
+ if (c == EOF) {
+ retval = 0;
+ done++;
+ goto endsw;
+ }
+ Currline++;
+ nlines--;
+ }
+ ret (dlines);
+ case '\n':
+ if (nlines != 0)
+ dlines = nlines;
+ else
+ nlines = 1;
+ ret (nlines);
+ case '\f':
+ if (!no_intty) {
+ doclear ();
+ Fseek (f, screen_start.chrctr);
+ Currline = screen_start.line;
+ ret (dlines);
+ }
+ else {
+ ringbell();
+ break;
+ }
+ case '\'':
+ if (!no_intty) {
+ kill_line ();
+ putsout(_("\n***Back***\n\n"));
+ Fseek (f, context.chrctr);
+ Currline = context.line;
+ ret (dlines);
+ }
+ else {
+ ringbell();
+ break;
+ }
+ case '=':
+ kill_line ();
+ promptlen = printf("%d", Currline);
+ fflush (stdout);
+ break;
+ case 'n':
+ if (!previousre) {
+ more_error (_("No previous regular expression"));
+ break;
+ }
+ lastp++;
+ /* fallthrough */
+ case '/':
+ if (nlines == 0) nlines++;
+ kill_line ();
+ putchar('/');
+ promptlen = 1;
+ fflush (stdout);
+ if (lastp) {
+ putcerr('\r');
+ search (previousre, f, nlines);
+ }
+ else {
+ ttyin (cmdbuf, sizeof(cmdbuf)-2, '/');
+ putcerr('\r');
+ free (previousre);
+ previousre = xstrdup(cmdbuf);
+ search (cmdbuf, f, nlines);
+ }
+ ret (dlines-1);
+ case '!':
+ do_shell (filename);
+ break;
+ case '?':
+ case 'h':
+ if (noscroll) doclear();
+ putsout(_("\n"
+"Most commands optionally preceded by integer argument k. "
+"Defaults in brackets.\n"
+"Star (*) indicates argument becomes new default.\n"));
+ puts("---------------------------------------"
+ "----------------------------------------");
+ putsout(_(
+"<space> Display next k lines of text [current screen size]\n"
+"z Display next k lines of text [current screen size]*\n"
+"<return> Display next k lines of text [1]*\n"
+"d or ctrl-D Scroll k lines [current scroll size, initially 11]*\n"
+"q or Q or <interrupt> Exit from more\n"
+"s Skip forward k lines of text [1]\n"
+"f Skip forward k screenfuls of text [1]\n"
+"b or ctrl-B Skip backwards k screenfuls of text [1]\n"
+"' Go to place where previous search started\n"
+"= Display current line number\n"
+"/<regular expression> Search for kth occurrence of regular expression [1]\n"
+"n Search for kth occurrence of last r.e [1]\n"
+"!<cmd> or :!<cmd> Execute <cmd> in a subshell\n"
+"v Start up /usr/bin/vi at current line\n"
+"ctrl-L Redraw screen\n"
+":n Go to kth next file [1]\n"
+":p Go to kth previous file [1]\n"
+":f Display current file name and line number\n"
+". Repeat previous command\n"));
+ puts("---------------------------------------"
+ "----------------------------------------");
+ prompt(filename);
+ break;
+ case 'v': /* This case should go right before default */
+ if (!no_intty) {
+ /*
+ * Earlier: call vi +n file. This also works for emacs.
+ * POSIX: call vi -c n file (when editor is vi or ex).
+ */
+ char *editor, *p;
+ int n = (Currline - dlines <= 0 ? 1 :
+ Currline - (dlines + 1) / 2);
+ int split = 0;
+
+ editor = getenv("VISUAL");
+ if (editor == NULL || *editor == '\0')
+ editor = getenv("EDITOR");
+ if (editor == NULL || *editor == '\0')
+ editor = VI;
+
+ p = strrchr(editor, '/');
+ if (p)
+ p++;
+ else
+ p = editor;
+ if (!strcmp(p, "vi") || !strcmp(p, "ex")) {
+ sprintf(cmdbuf, "-c %d", n);
+ split = 1;
+ } else {
+ sprintf(cmdbuf, "+%d", n);
+ }
+
+ kill_line();
+ printf("%s %s %s", editor, cmdbuf, fnames[fnum]);
+ if (split) {
+ cmdbuf[2] = 0;
+ execute(filename, editor, editor, cmdbuf,
+ cmdbuf+3, fnames[fnum], (char *)0);
+ } else
+ execute(filename, editor, editor,
+ cmdbuf, fnames[fnum], (char *)0);
+ break;
+ }
+ /* fall through */
+ default:
+ if (dum_opt) {
+ kill_line ();
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ promptlen = printf(_("[Press 'h' for instructions.]"))
+ + 2 * soglitch;
+ my_putstring (Sexit);
+ }
+ else
+ promptlen = printf(_("[Press 'h' for instructions.]"));
+ fflush (stdout);
+ }
+ else
+ ringbell();
+ break;
+ }
+ if (done) break;
+ }
+ putchar ('\r');
+endsw:
+ inwait = 0;
+ notell++;
+ return (retval);
+}
+
+static char ch;
+
+/*
+ * Execute a colon-prefixed command.
+ * Returns <0 if not a command that should cause
+ * more of the file to be printed.
+ */
+
+int colon (char *filename, int cmd, int nlines) {
+ if (cmd == 0)
+ ch = readch ();
+ else
+ ch = cmd;
+ lastcolon = ch;
+ switch (ch) {
+ case 'f':
+ kill_line ();
+ if (!no_intty)
+ promptlen = printf(_("\"%s\" line %d"), fnames[fnum], Currline);
+ else
+ promptlen = printf(_("[Not a file] line %d"), Currline);
+ fflush (stdout);
+ return (-1);
+ case 'n':
+ if (nlines == 0) {
+ if (fnum >= nfiles - 1)
+ end_it (0);
+ nlines++;
+ }
+ putchar ('\r');
+ erasep (0);
+ skipf (nlines);
+ return (0);
+ case 'p':
+ if (no_intty) {
+ ringbell();
+ return (-1);
+ }
+ putchar ('\r');
+ erasep (0);
+ if (nlines == 0)
+ nlines++;
+ skipf (-nlines);
+ return (0);
+ case '!':
+ do_shell (filename);
+ return (-1);
+ case 'q':
+ case 'Q':
+ end_it (0);
+ default:
+ ringbell();
+ return (-1);
+ }
+}
+
+/*
+** Read a decimal number from the terminal. Set cmd to the non-digit which
+** terminates the number.
+*/
+
+int number(char *cmd)
+{
+ register int i;
+
+ i = 0; ch = otty.c_cc[VKILL];
+ for (;;) {
+ ch = readch ();
+ if (isdigit(ch))
+ i = i*10 + ch - '0';
+ else if ((cc_t) ch == otty.c_cc[VKILL])
+ i = 0;
+ else {
+ *cmd = ch;
+ break;
+ }
+ }
+ return (i);
+}
+
+void do_shell (char *filename)
+{
+ char cmdbuf[COMMAND_BUF];
+ int rc;
+ char *expanded;
+
+ kill_line ();
+ putchar('!');
+ fflush (stdout);
+ promptlen = 1;
+ if (lastp)
+ putsout(shell_line);
+ else {
+ ttyin (cmdbuf, sizeof(cmdbuf)-2, '!');
+ expanded = 0;
+ rc = expand (&expanded, cmdbuf);
+ if (expanded) {
+ if (strlen(expanded) < sizeof(shell_line))
+ strcpy(shell_line, expanded);
+ else
+ rc = -1;
+ free(expanded);
+ }
+ if (rc < 0) {
+ putserr(_(" Overflow\n"));
+ prompt (filename);
+ return;
+ } else if (rc > 0) {
+ kill_line ();
+ promptlen = printf("!%s", shell_line);
+ }
+ }
+ fflush (stdout);
+ putcerr('\n');
+ promptlen = 0;
+ shellp = 1;
+ execute (filename, shell, shell, "-c", shell_line, 0);
+}
+
+/*
+** Search for nth occurrence of regular expression contained in buf in the file
+*/
+
+void search(char buf[], FILE *file, register int n)
+{
+ long startline = Ftell (file);
+ register long line1 = startline;
+ register long line2 = startline;
+ register long line3 = startline;
+ register int lncount;
+ int saveln, rv, rc;
+ regex_t re;
+
+ context.line = saveln = Currline;
+ context.chrctr = startline;
+ lncount = 0;
+ if ((rc = regcomp (&re, buf, REG_NOSUB)) != 0) {
+ char s[REGERR_BUF];
+ regerror (rc, &re, s, sizeof s);
+ more_error (s);
+ }
+ while (!feof (file)) {
+ line3 = line2;
+ line2 = line1;
+ line1 = Ftell (file);
+ rdline (file);
+ lncount++;
+ if ((rv = regexec (&re, Line, 0, NULL, 0)) == 0) {
+ if (--n == 0) {
+ if (lncount > 3 || (lncount > 1 && no_intty))
+ {
+ putchar('\n');
+ if (clreol)
+ cleareol ();
+ putsout(_("...skipping\n"));
+ }
+ if (!no_intty) {
+ Currline -= (lncount >= 3 ? 3 : lncount);
+ Fseek (file, line3);
+ if (noscroll) {
+ if (clreol) {
+ home ();
+ cleareol ();
+ }
+ else
+ doclear ();
+ }
+ }
+ else {
+ kill_line ();
+ if (noscroll) {
+ if (clreol) {
+ home ();
+ cleareol ();
+ }
+ else
+ doclear ();
+ }
+ puts(Line);
+ }
+ break;
+ }
+ }
+ }
+ if (feof (file)) {
+ if (!no_intty) {
+ Currline = saveln;
+ Fseek (file, startline);
+ }
+ else {
+ putsout(_("\nPattern not found\n"));
+ end_it (0);
+ }
+ more_error (_("Pattern not found"));
+ free (previousre);
+ previousre = NULL;
+ }
+}
+
+/*VARARGS2*/
+void execute (char *filename, char *cmd, ...)
+{
+ int id;
+ int n;
+ va_list argp;
+ char * arg;
+ char ** args;
+ int argcount;
+
+ fflush (stdout);
+ reset_tty ();
+ for (n = 10; (id = fork ()) < 0 && n > 0; n--)
+ sleep (5);
+ if (id == 0) {
+ if (!isatty(0)) {
+ close(0);
+ open("/dev/tty", 0);
+ }
+
+ va_start(argp, cmd);
+ arg = va_arg(argp, char *);
+ argcount = 0;
+ while (arg) {
+ argcount++;
+ arg = va_arg(argp, char *);
+ }
+ va_end(argp);
+
+ args = alloca(sizeof(char *) * (argcount + 1));
+ args[argcount] = NULL;
+
+ va_start(argp, cmd);
+ arg = va_arg(argp, char *);
+ argcount = 0;
+ while (arg) {
+ args[argcount] = arg;
+ argcount++;
+ arg = va_arg(argp, char *);
+ }
+ va_end(argp);
+
+ execvp (cmd, args);
+ putserr(_("exec failed\n"));
+ exit (EXIT_FAILURE);
+ }
+ if (id > 0) {
+ signal (SIGINT, SIG_IGN);
+ signal (SIGQUIT, SIG_IGN);
+ if (catch_susp)
+ signal(SIGTSTP, SIG_DFL);
+ while (wait(0) > 0);
+ signal (SIGINT, end_it);
+ signal (SIGQUIT, onquit);
+ if (catch_susp)
+ signal(SIGTSTP, onsusp);
+ } else
+ putserr(_("can't fork\n"));
+ set_tty ();
+ puts("------------------------");
+ prompt (filename);
+}
+/*
+** Skip n lines in the file f
+*/
+
+void skiplns (register int n, register FILE *f)
+{
+ register int c;
+
+ while (n > 0) {
+ while ((c = Getc (f)) != '\n')
+ if (c == EOF)
+ return;
+ n--;
+ Currline++;
+ }
+}
+
+/*
+** Skip nskip files in the file list (from the command line). Nskip may be
+** negative.
+*/
+
+void skipf (register int nskip)
+{
+ if (nskip == 0) return;
+ if (nskip > 0) {
+ if (fnum + nskip > nfiles - 1)
+ nskip = nfiles - fnum - 1;
+ }
+ else if (within)
+ ++fnum;
+ fnum += nskip;
+ if (fnum < 0)
+ fnum = 0;
+ puts(_("\n...Skipping "));
+ if (clreol)
+ cleareol ();
+ if (nskip > 0)
+ putsout(_("...Skipping to file "));
+ else
+ putsout(_("...Skipping back to file "));
+ puts(fnames[fnum]);
+ if (clreol)
+ cleareol ();
+ putchar('\n');
+ --fnum;
+}
+
+/*----------------------------- Terminal I/O -------------------------------*/
+
+void initterm()
+{
+ int ret;
+ char *padstr;
+ char *term;
+ struct winsize win;
+
+#ifdef do_SIGTTOU
+retry:
+#endif /* do_SIGTTOU */
+ no_tty = tcgetattr(fileno(stdout), &otty);
+ if (!no_tty) {
+ docrterase = (otty.c_cc[VERASE] != 255);
+ docrtkill = (otty.c_cc[VKILL] != 255);
+#ifdef do_SIGTTOU
+ {
+ int tgrp;
+ /*
+ * Wait until we're in the foreground before we save the
+ * the terminal modes.
+ */
+ if ((tgrp = tcgetpgrp(fileno(stdout))) < 0) {
+ perror("tcgetpgrp");
+ exit(EXIT_FAILURE);
+ }
+ if (tgrp != getpgrp(0)) {
+ kill(0, SIGTTOU);
+ goto retry;
+ }
+ }
+#endif /* do_SIGTTOU */
+ if ((term = getenv("TERM")) == 0) {
+ dumb++; ul_opt = 0;
+ }
+ my_setupterm(term, 1, &ret);
+ if (ret <= 0) {
+ dumb++; ul_opt = 0;
+ }
+ else {
+#ifdef TIOCGWINSZ
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
+#endif /* TIOCGWINSZ */
+ Lpp = my_tgetnum(TERM_LINES);
+ Mcol = my_tgetnum(TERM_COLS);
+#ifdef TIOCGWINSZ
+ } else {
+ if ((Lpp = win.ws_row) == 0)
+ Lpp = my_tgetnum(TERM_LINES);
+ if ((Mcol = win.ws_col) == 0)
+ Mcol = my_tgetnum(TERM_COLS);
+ }
+#endif /* TIOCGWINSZ */
+ if ((Lpp <= 0) || my_tgetflag(TERM_HARD_COPY)) {
+ hard++; /* Hard copy terminal */
+ Lpp = LINES_PER_PAGE;
+ }
+
+ if (my_tgetflag(TERM_EAT_NEW_LINE))
+ eatnl++; /* Eat newline at last column + 1; dec, concept */
+ if (Mcol <= 0)
+ Mcol = NUM_COLUMNS;
+
+ Wrap = my_tgetflag(TERM_AUTO_RIGHT_MARGIN);
+ bad_so = my_tgetflag (TERM_CEOL);
+ eraseln = my_tgetstr(TERM_CLEAR_TO_LINE_END);
+ Clear = my_tgetstr(TERM_CLEAR);
+ Senter = my_tgetstr(TERM_STANDARD_MODE);
+ Sexit = my_tgetstr(TERM_EXIT_STANDARD_MODE);
+ if ((soglitch = my_tgetnum(TERM_STD_MODE_GLITCH)) < 0)
+ soglitch = 0;
+
+ /*
+ * Set up for underlining: some terminals don't need it;
+ * others have start/stop sequences, still others have an
+ * underline char sequence which is assumed to move the
+ * cursor forward one character. If underline sequence
+ * isn't available, settle for standout sequence.
+ */
+
+ if (my_tgetflag(TERM_UNDERLINE) || my_tgetflag(TERM_OVER_STRIKE))
+ ul_opt = 0;
+ if ((chUL = my_tgetstr(TERM_UNDERLINE_CHAR)) == NULL )
+ chUL = "";
+ if (((ULenter = my_tgetstr(TERM_ENTER_UNDERLINE)) == NULL ||
+ (ULexit = my_tgetstr(TERM_EXIT_UNDERLINE)) == NULL) && !*chUL) {
+ if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
+ ULenter = "";
+ ULexit = "";
+ } else
+ ulglitch = soglitch;
+ } else {
+ ulglitch = 0;
+ }
+
+ if ((padstr = my_tgetstr(TERM_PAD_CHAR)) != NULL)
+ PC = *padstr;
+ Home = my_tgetstr(TERM_HOME);
+ if (Home == 0 || *Home == '\0') {
+ if ((cursorm = my_tgetstr(TERM_CURSOR_ADDRESS)) != NULL) {
+ const char *t = (const char *)my_tgoto(cursorm, 0, 0);
+ xstrncpy(cursorhome, t, sizeof(cursorhome));
+ Home = cursorhome;
+ }
+ }
+ EodClr = my_tgetstr(TERM_CLEAR_TO_SCREEN_END);
+ if ((chBS = my_tgetstr(TERM_LINE_DOWN)) == NULL)
+ chBS = "\b";
+
+ }
+ if ((shell = getenv("SHELL")) == NULL)
+ shell = "/bin/sh";
+ }
+ no_intty = tcgetattr(fileno(stdin), &otty);
+ tcgetattr(fileno(stderr), &otty);
+ savetty0 = otty;
+ slow_tty = cfgetispeed(&otty) < B1200;
+ hardtabs = (otty.c_oflag & TABDLY) != XTABS;
+ if (!no_tty) {
+ otty.c_lflag &= ~(ICANON|ECHO);
+ otty.c_cc[VMIN] = 1;
+ otty.c_cc[VTIME] = 0;
+ }
+}
+
+int readch () {
+ unsigned char c;
+
+ errno = 0;
+ if (read (fileno(stderr), &c, 1) <= 0) {
+ if (errno != EINTR)
+ end_it(0);
+ else
+ c = otty.c_cc[VKILL];
+ }
+ return (c);
+}
+
+static char *BS = "\b";
+static char *BSB = "\b \b";
+static char *CARAT = "^";
+#define ERASEONECOLUMN \
+ if (docrterase) \
+ putserr(BSB); \
+ else \
+ putserr(BS);
+
+void ttyin (char buf[], register int nmax, char pchar) {
+ char *sp;
+ int c;
+ int slash = 0;
+ int maxlen;
+
+ sp = buf;
+ maxlen = 0;
+ while (sp - buf < nmax) {
+ if (promptlen > maxlen) maxlen = promptlen;
+ c = readch ();
+ if (c == '\\') {
+ slash++;
+ }
+ else if (((cc_t) c == otty.c_cc[VERASE]) && !slash) {
+ if (sp > buf) {
+#ifdef HAVE_WIDECHAR
+ if (MB_CUR_MAX > 1)
+ {
+ wchar_t wc;
+ size_t pos = 0, mblength;
+ mbstate_t state, state_bak;
+
+ memset (&state, '\0', sizeof (mbstate_t));
+
+ while (1) {
+ state_bak = state;
+ mblength = mbrtowc (&wc, buf + pos, sp - buf, &state);
+
+ state = (mblength == (size_t)-2
+ || mblength == (size_t)-1) ? state_bak : state;
+ mblength = (mblength == (size_t)-2
+ || mblength == (size_t)-1
+ || mblength == 0) ? 1 : mblength;
+
+ if (buf + pos + mblength >= sp)
+ break;
+
+ pos += mblength;
+ }
+
+ if (mblength == 1) {
+ ERASEONECOLUMN
+ }
+ else {
+ int wc_width;
+ wc_width = wcwidth (wc);
+ wc_width = (wc_width < 1) ? 1 : wc_width;
+ while (wc_width--) {
+ ERASEONECOLUMN
+ }
+ }
+
+ while (mblength--) {
+ --promptlen;
+ --sp;
+ }
+ }
+ else
+#endif /* HAVE_WIDECHAR */
+ {
+ --promptlen;
+ ERASEONECOLUMN
+ --sp;
+ }
+
+ if ((*sp < ' ' && *sp != '\n') || *sp == RUBOUT) {
+ --promptlen;
+ ERASEONECOLUMN
+ }
+ continue;
+ }
+ else {
+ if (!eraseln) promptlen = maxlen;
+ siglongjmp (restore, 1);
+ }
+ }
+ else if (((cc_t) c == otty.c_cc[VKILL]) && !slash) {
+ if (hard) {
+ show (c);
+ putchar ('\n');
+ putchar (pchar);
+ }
+ else {
+ putchar ('\r');
+ putchar (pchar);
+ if (eraseln)
+ erasep (1);
+ else if (docrtkill)
+ while (promptlen-- > 1)
+ putserr(BSB);
+ promptlen = 1;
+ }
+ sp = buf;
+ fflush (stdout);
+ continue;
+ }
+ if (slash && ((cc_t) c == otty.c_cc[VKILL]
+ || (cc_t) c == otty.c_cc[VERASE])) {
+ ERASEONECOLUMN
+ --sp;
+ }
+ if (c != '\\')
+ slash = 0;
+ *sp++ = c;
+ if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
+ c += (c == RUBOUT) ? -0100 : 0100;
+ putserr(CARAT);
+ promptlen++;
+ }
+ if (c != '\n' && c != ESC) {
+ putcerr(c);
+ promptlen++;
+ }
+ else
+ break;
+ }
+ *--sp = '\0';
+ if (!eraseln) promptlen = maxlen;
+ if (sp - buf >= nmax - 1)
+ more_error (_("Line too long"));
+}
+
+/* return: 0 - unchanged, 1 - changed, -1 - overflow (unchanged) */
+int expand (char **outbuf, char *inbuf) {
+ char *inpstr;
+ char *outstr;
+ char c;
+ char *temp;
+ int changed = 0;
+ int tempsz, xtra, offset;
+
+ xtra = strlen (fnames[fnum]) + strlen (shell_line) + 1;
+ tempsz = 200 + xtra;
+ temp = xmalloc(tempsz);
+ inpstr = inbuf;
+ outstr = temp;
+ while ((c = *inpstr++) != '\0'){
+ offset = outstr-temp;
+ if (tempsz-offset-1 < xtra) {
+ tempsz += 200 + xtra;
+ temp = xrealloc(temp, tempsz);
+ outstr = temp + offset;
+ }
+ switch (c) {
+ case '%':
+ if (!no_intty) {
+ strcpy (outstr, fnames[fnum]);
+ outstr += strlen (fnames[fnum]);
+ changed++;
+ } else
+ *outstr++ = c;
+ break;
+ case '!':
+ if (!shellp)
+ more_error (_("No previous command to substitute for"));
+ strcpy (outstr, shell_line);
+ outstr += strlen (shell_line);
+ changed++;
+ break;
+ case '\\':
+ if (*inpstr == '%' || *inpstr == '!') {
+ *outstr++ = *inpstr++;
+ break;
+ }
+ default:
+ *outstr++ = c;
+ }
+ }
+ *outstr++ = '\0';
+ *outbuf = temp;
+ return (changed);
+}
+
+void show (char c) {
+ if ((c < ' ' && c != '\n' && c != ESC) || c == RUBOUT) {
+ c += (c == RUBOUT) ? -0100 : 0100;
+ putserr(CARAT);
+ promptlen++;
+ }
+ putcerr(c);
+ promptlen++;
+}
+
+void more_error (char *mess)
+{
+ if (clreol)
+ cleareol ();
+ else
+ kill_line ();
+ promptlen += strlen (mess);
+ if (Senter && Sexit) {
+ my_putstring (Senter);
+ putsout(mess);
+ my_putstring (Sexit);
+ }
+ else
+ putsout(mess);
+ fflush(stdout);
+ errors++;
+ siglongjmp (restore, 1);
+}
+
+
+void set_tty () {
+ otty.c_lflag &= ~(ICANON|ECHO);
+ otty.c_cc[VMIN] = 1; /* read at least 1 char */
+ otty.c_cc[VTIME] = 0; /* no timeout */
+ stty(fileno(stderr), &otty);
+}
+
+static int
+ourputch(int c) {
+ return putc(c, stdout);
+}
+
+void
+reset_tty () {
+ if (no_tty)
+ return;
+ if (pstate) {
+ tputs(ULexit, fileno(stdout), ourputch); /* putchar - if that isn't a macro */
+ fflush(stdout);
+ pstate = 0;
+ }
+ otty.c_lflag |= ICANON|ECHO;
+ otty.c_cc[VMIN] = savetty0.c_cc[VMIN];
+ otty.c_cc[VTIME] = savetty0.c_cc[VTIME];
+ stty(fileno(stderr), &savetty0);
+}
+
+void rdline (register FILE *f)
+{
+ register int c;
+ register char *p;
+
+ prepare_line_buffer();
+
+ p = Line;
+ while ((c = Getc (f)) != '\n' && c != EOF && (size_t) (p - Line) < LineLen - 1)
+ *p++ = c;
+ if (c == '\n')
+ Currline++;
+ *p = '\0';
+}
+
+/* Come here when we get a suspend signal from the terminal */
+
+void onsusp (int dummy __attribute__ ((__unused__)))
+{
+ sigset_t signals, oldmask;
+
+ /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
+ signal(SIGTTOU, SIG_IGN);
+ reset_tty ();
+ fflush (stdout);
+ signal(SIGTTOU, SIG_DFL);
+ /* Send the TSTP signal to suspend our process group */
+ signal(SIGTSTP, SIG_DFL);
+
+ /* unblock SIGTSTP or we won't be able to suspend ourself */
+ sigemptyset(&signals);
+ sigaddset(&signals, SIGTSTP);
+ sigprocmask(SIG_UNBLOCK, &signals, &oldmask);
+
+ kill (0, SIGTSTP);
+ /* Pause for station break */
+
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ /* We're back */
+ signal (SIGTSTP, onsusp);
+ set_tty ();
+ if (inwait)
+ siglongjmp (restore, 1);
+}
diff --git a/text-utils/parse.c b/text-utils/parse.c
new file mode 100644
index 0000000..5f1e2bd
--- /dev/null
+++ b/text-utils/parse.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "hexdump.h"
+#include "nls.h"
+#include "xalloc.h"
+
+static void escape(char *p1);
+static void badcnt(const char *s);
+static void badsfmt(void);
+static void badfmt(const char *fmt);
+static void badconv(const char *ch);
+
+FU *endfu; /* format at end-of-data */
+
+void addfile(char *name)
+{
+ char *p;
+ FILE *fp;
+ int ch;
+ char buf[2048 + 1];
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err(EXIT_FAILURE, _("can't read %s"), name);
+ while (fgets(buf, sizeof(buf), fp)) {
+ if ((p = strchr(buf, '\n')) == NULL) {
+ warnx(_("line too long"));
+ while ((ch = getchar()) != '\n' && ch != EOF);
+ continue;
+ }
+ *p = '\0';
+ for (p = buf; *p && isspace((unsigned char)*p); ++p);
+ if (!*p || *p == '#')
+ continue;
+ add(p);
+ }
+ (void)fclose(fp);
+}
+
+void add(const char *fmt)
+{
+ const char *p;
+ static FS **nextfs = NULL;
+ FS *tfs;
+ FU *tfu, **nextfu;
+ const char *savep;
+
+ /* Start new linked list of format units. */
+ tfs = xcalloc(1, sizeof(FS));
+ if (!fshead)
+ fshead = tfs;
+ else if (nextfs)
+ *nextfs = tfs;
+
+ nextfs = &tfs->nextfs;
+ nextfu = &tfs->nextfu;
+
+ /* Take the format string and break it up into format units. */
+ for (p = fmt;;) {
+ /* Skip leading white space. */
+ for (; isspace((unsigned char)*p); ++p);
+ if (!*p)
+ break;
+
+ /* Allocate a new format unit and link it in. */
+ tfu = xcalloc(1, sizeof(FU));
+ *nextfu = tfu;
+ nextfu = &tfu->nextfu;
+ tfu->reps = 1;
+
+ /* If leading digit, repetition count. */
+ if (isdigit((unsigned char)*p)) {
+ for (savep = p; isdigit((unsigned char)*p); ++p);
+ if (!isspace((unsigned char)*p) && *p != '/')
+ badfmt(fmt);
+ /* may overwrite either white space or slash */
+ tfu->reps = atoi(savep);
+ tfu->flags = F_SETREP;
+ /* skip trailing white space */
+ for (++p; isspace((unsigned char)*p); ++p);
+ }
+
+ /* Skip slash and trailing white space. */
+ if (*p == '/')
+ while (isspace((unsigned char)*++p));
+
+ /* byte count */
+ if (isdigit((unsigned char)*p)) {
+ for (savep = p; isdigit((unsigned char)*p); ++p);
+ if (!isspace((unsigned char)*p))
+ badfmt(fmt);
+ tfu->bcnt = atoi(savep);
+ /* skip trailing white space */
+ for (++p; isspace((unsigned char)*p); ++p);
+ }
+
+ /* format */
+ if (*p != '"')
+ badfmt(fmt);
+ for (savep = ++p; *p != '"';)
+ if (*p++ == 0)
+ badfmt(fmt);
+ tfu->fmt = xmalloc(p - savep + 1);
+ (void) strncpy(tfu->fmt, savep, p - savep);
+ tfu->fmt[p - savep] = '\0';
+ escape(tfu->fmt);
+ p++;
+ }
+}
+
+static const char *spec = ".#-+ 0123456789";
+
+int block_size(FS *fs)
+{
+ FU *fu;
+ int bcnt, cursize;
+ char *fmt;
+ int prec;
+
+ /* figure out the data block size needed for each format unit */
+ for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->bcnt) {
+ cursize += fu->bcnt * fu->reps;
+ continue;
+ }
+ for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+ if (*fmt != '%')
+ continue;
+ /*
+ * skip any special chars -- save precision in
+ * case it's a %s format.
+ */
+ while (strchr(spec + 1, *++fmt));
+ if (*fmt == '.' && isdigit((unsigned char)*++fmt)) {
+ prec = atoi(fmt);
+ while (isdigit((unsigned char)*++fmt));
+ }
+ switch(*fmt) {
+ case 'c':
+ bcnt += 1;
+ break;
+ case 'd': case 'i': case 'o': case 'u':
+ case 'x': case 'X':
+ bcnt += 4;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ bcnt += 8;
+ break;
+ case 's':
+ bcnt += prec;
+ break;
+ case '_':
+ switch(*++fmt) {
+ case 'c': case 'p': case 'u':
+ bcnt += 1;
+ break;
+ }
+ }
+ }
+ cursize += bcnt * fu->reps;
+ }
+ return(cursize);
+}
+
+void rewrite(FS *fs)
+{
+ enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+ PR *pr, **nextpr;
+ FU *fu;
+ char *p1, *p2;
+ char savech, *fmtp, cs[3];
+ int nconv, prec;
+
+ nextpr = NULL;
+ prec = 0;
+
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ /*
+ * Break each format unit into print units; each
+ * conversion character gets its own.
+ */
+ for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+ pr = xcalloc(1, sizeof(PR));
+ if (!fu->nextpr)
+ fu->nextpr = pr;
+ else if (nextpr)
+ *nextpr = pr;
+
+ /* Skip preceding text and up to the next % sign. */
+ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
+
+ /* Only text in the string. */
+ if (!*p1) {
+ pr->fmt = fmtp;
+ pr->flags = F_TEXT;
+ break;
+ }
+
+ /*
+ * Get precision for %s -- if have a byte count, don't
+ * need it.
+ */
+ if (fu->bcnt) {
+ sokay = USEBCNT;
+ /* skip to conversion character */
+ for (++p1; strchr(spec, *p1); ++p1);
+ } else {
+ /* skip any special chars, field width */
+ while (strchr(spec + 1, *++p1));
+ if (*p1 == '.' &&
+ isdigit((unsigned char)*++p1)) {
+ sokay = USEPREC;
+ prec = atoi(p1);
+ while (isdigit((unsigned char)*++p1));
+ } else
+ sokay = NOTOKAY;
+ }
+
+ p2 = p1 + 1; /* Set end pointer. */
+ cs[0] = *p1; /* Set conversion string. */
+ cs[1] = 0;
+
+ /*
+ * Figure out the byte count for each conversion;
+ * rewrite the format as necessary, set up blank-
+ * padding for end of data.
+ */
+ switch(cs[0]) {
+ case 'c':
+ pr->flags = F_CHAR;
+ switch(fu->bcnt) {
+ case 0: case 1:
+ pr->bcnt = 1;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 'd': case 'i':
+ pr->flags = F_INT;
+ goto isint;
+ case 'o': case 'u': case 'x': case 'X':
+ pr->flags = F_UINT;
+isint: cs[2] = '\0';
+ cs[1] = cs[0];
+ cs[0] = 'q';
+ switch(fu->bcnt) {
+ case 0: case 4:
+ pr->bcnt = 4;
+ break;
+ case 1:
+ pr->bcnt = 1;
+ break;
+ case 2:
+ pr->bcnt = 2;
+ break;
+ case 8:
+ pr->bcnt = 8;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ pr->flags = F_DBL;
+ switch(fu->bcnt) {
+ case 0: case 8:
+ pr->bcnt = 8;
+ break;
+ case 4:
+ pr->bcnt = 4;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 's':
+ pr->flags = F_STR;
+ switch(sokay) {
+ case NOTOKAY:
+ badsfmt();
+ case USEBCNT:
+ pr->bcnt = fu->bcnt;
+ break;
+ case USEPREC:
+ pr->bcnt = prec;
+ break;
+ }
+ break;
+ case '_':
+ ++p2;
+ switch(p1[1]) {
+ case 'A':
+ endfu = fu;
+ fu->flags |= F_IGNORE;
+ /* FALLTHROUGH */
+ case 'a':
+ pr->flags = F_ADDRESS;
+ ++p2;
+ switch(p1[2]) {
+ case 'd': case 'o': case'x':
+ cs[0] = 'q';
+ cs[1] = p1[2];
+ cs[2] = '\0';
+ break;
+ default:
+ p1[3] = '\0';
+ badconv(p1);
+ }
+ break;
+ case 'c':
+ pr->flags = F_C;
+ /* cs[0] = 'c'; set in conv_c */
+ goto isint2;
+ case 'p':
+ pr->flags = F_P;
+ cs[0] = 'c';
+ goto isint2;
+ case 'u':
+ pr->flags = F_U;
+ /* cs[0] = 'c'; set in conv_u */
+isint2: switch(fu->bcnt) {
+ case 0: case 1:
+ pr->bcnt = 1;
+ break;
+ default:
+ p1[2] = '\0';
+ badcnt(p1);
+ }
+ break;
+ default:
+ p1[2] = '\0';
+ badconv(p1);
+ }
+ break;
+ default:
+ p1[1] = '\0';
+ badconv(p1);
+ }
+
+ /*
+ * Copy to PR format string, set conversion character
+ * pointer, update original.
+ */
+ savech = *p2;
+ p1[0] = '\0';
+ pr->fmt = xmalloc(strlen(fmtp) + strlen(cs) + 1);
+ (void)strcpy(pr->fmt, fmtp);
+ (void)strcat(pr->fmt, cs);
+ *p2 = savech;
+ pr->cchar = pr->fmt + (p1 - fmtp);
+ fmtp = p2;
+
+ /* Only one conversion character if byte count */
+ if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
+ errx(EXIT_FAILURE,
+ _("byte count with multiple conversion characters"));
+ }
+ /*
+ * If format unit byte count not specified, figure it out
+ * so can adjust rep count later.
+ */
+ if (!fu->bcnt)
+ for (pr = fu->nextpr; pr; pr = pr->nextpr)
+ fu->bcnt += pr->bcnt;
+ }
+ /*
+ * If the format string interprets any data at all, and it's
+ * not the same as the blocksize, and its last format unit
+ * interprets any data at all, and has no iteration count,
+ * repeat it as necessary.
+ *
+ * If rep count is greater than 1, no trailing whitespace
+ * gets output from the last iteration of the format unit.
+ */
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (!fu->nextfu && fs->bcnt < blocksize &&
+ !(fu->flags&F_SETREP) && fu->bcnt)
+ fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
+ if (fu->reps > 1) {
+ for (pr = fu->nextpr;; pr = pr->nextpr)
+ if (!pr->nextpr)
+ break;
+ for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+ p2 = isspace((unsigned char)*p1) ? p1 : NULL;
+ if (p2)
+ pr->nospace = p2;
+ }
+ }
+}
+
+
+static void escape(char *p1)
+{
+ char *p2;
+
+ /* alphabetic escape sequences have to be done in place */
+ for (p2 = p1;; ++p1, ++p2) {
+ if (!*p1) {
+ *p2 = *p1;
+ break;
+ }
+ if (*p1 == '\\')
+ switch(*++p1) {
+ case 'a':
+ /* *p2 = '\a'; */
+ *p2 = '\007';
+ break;
+ case 'b':
+ *p2 = '\b';
+ break;
+ case 'f':
+ *p2 = '\f';
+ break;
+ case 'n':
+ *p2 = '\n';
+ break;
+ case 'r':
+ *p2 = '\r';
+ break;
+ case 't':
+ *p2 = '\t';
+ break;
+ case 'v':
+ *p2 = '\v';
+ break;
+ default:
+ *p2 = *p1;
+ break;
+ }
+ }
+}
+
+static void badcnt(const char *s)
+{
+ errx(EXIT_FAILURE, _("bad byte count for conversion character %s"), s);
+}
+
+static void badsfmt(void)
+{
+ errx(EXIT_FAILURE, _("%%s requires a precision or a byte count"));
+}
+
+static void badfmt(const char *fmt)
+{
+ errx(EXIT_FAILURE, _("bad format {%s}"), fmt);
+}
+
+static void badconv(const char *ch)
+{
+ errx(EXIT_FAILURE, _("bad conversion character %%%s"), ch);
+}
diff --git a/text-utils/pg.1 b/text-utils/pg.1
new file mode 100644
index 0000000..93b0209
--- /dev/null
+++ b/text-utils/pg.1
@@ -0,0 +1,232 @@
+.\" @(#)pg.1 1.7 (gritter) 4/25/01
+.TH PG 1 "April 2001" "util-linux" "User Commands"
+.SH NAME
+pg \- browse pagewise through text files
+.SH SYNOPSIS
+.B pg
+.RB [ \-\fInumber\fP ]
+.RB [ \-p
+.IR string ]
+.RB [ \-cefnrs ]
+.RB [ +\fIline\fP ]
+.RB [ +/\fIpattern\fP/ ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B 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,
+.B pg
+reads from standard input.
+If standard output is not a terminal,
+.B 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,
+.B pg
+stores the data in a buffer file while reading
+to make navigation possible.
+.SH OPTIONS
+.B 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
+Do not pause and display
+.SM (EOF)
+at the end of a file.
+.TP
+.B \-f
+Do not split long lines.
+.TP
+.B \-n
+Without this option, commands must be terminated by a newline character.
+With this option,
+.B pg
+advances once a command letter is entered.
+.TP
+.BI \-p \ string
+Instead of the normal prompt
+.IR : ,
+.I string
+is displayed.
+If
+.I string
+contains
+.IR %d ,
+its first occurrence 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 preceded 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
+occurrence 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
+occurrence 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
+.B pg
+reads from the
+input file or writes on the terminal,
+.B pg
+will immediately display the prompt.
+In all other situations these keys will terminate
+.BR pg .
+.SH "ENVIRONMENT VARIABLES"
+The following environment variables
+affect the behaviour of
+.BR 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
+.BR ! " 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
+.B pg
+expects the terminal tabulators to be set every eight positions.
+.PP
+Files that include
+.SM NUL
+characters cannot be displayed by
+.BR pg .
+.SH AVAILABILITY
+The pg command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/text-utils/pg.c b/text-utils/pg.c
new file mode 100644
index 0000000..9ade866
--- /dev/null
+++ b/text-utils/pg.c
@@ -0,0 +1,1783 @@
+/*
+ * 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 */
+
+#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 <libgen.h>
+
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#include <ncurses/ncurses.h>
+#endif
+
+#include <term.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "all-io.h"
+#include "closestream.h"
+
+#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 */
+
+char *copyright;
+const char *helpscreen = 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");
+
+#ifndef HAVE_FSEEKO
+ static int fseeko(FILE *f, off_t off, int whence) {
+ return fseek(f, (long) off, whence);
+ }
+ static off_t 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);
+}
+
+/*
+ * Usage message and similar routines.
+ */
+static void usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] [+line] [+/pattern/] [files]\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -number lines per page\n"), out);
+ fputs(_(" -c clear screen before displaying\n"), out);
+ fputs(_(" -e do not pause at end of a file\n"), out);
+ fputs(_(" -f do not split long lines\n"), out);
+ fputs(_(" -n terminate command with new line\n"), out);
+ fputs(_(" -p <prompt> specify prompt\n"), out);
+ fputs(_(" -r disallow shell escape\n"), out);
+ fputs(_(" -s print messages to stdout\n"), out);
+ fputs(_(" +number start at the given line\n"), out);
+ fputs(_(" +/pattern/ start at the line containing pattern\n"), out);
+ fputs(_(" -h display this help and exit\n"), out);
+ fputs(_(" -V output version information and exit\n"), out);
+ fprintf(out, USAGE_MAN_TAIL("pg(1)"));
+ quit(out == stderr ? 2 : 0);
+}
+
+static void
+needarg(char *s)
+{
+ fprintf(stderr, _("%s: option requires an argument -- %s\n"),
+ progname, s);
+ usage(stderr);
+}
+
+static void
+invopt(char *s)
+{
+ fprintf(stderr, _("%s: illegal option -- %s\n"), progname, s);
+ usage(stderr);
+}
+
+#ifdef HAVE_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;
+
+ ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) ); /* reset shift state */
+ 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';
+ ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) );
+ return nwcs - n;
+}
+#endif
+
+/*
+ * Helper function for tputs().
+ */
+static int
+outcap(int i)
+{
+ char c = i;
+ return write_all(1, &c, 1) == 0 ? 1 : -1;
+}
+
+/*
+ * Write messages to terminal.
+ */
+static void
+mesg(char *message)
+{
+ if (ontty == 0)
+ return;
+ if (*message != '\n' && sflag)
+ vidputs(A_STANDOUT, outcap);
+ write_all(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 HAVE_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)
+{
+ size_t 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:
+ if (iswprint(*p))
+ pos += wcwidth(*p);
+ else
+ pos += wcwidth(L'?');
+ }
+ 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)) == (size_t) -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 HAVE_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 = xmalloc(ttycols + 2);
+ memset(buf, ' ', ttycols + 2);
+ buf[0] = '\r';
+ buf[ttycols + 1] = '\r';
+ write_all(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:
+#ifdef PG_BELL
+ if (bell)
+ tputs(bell, 1, outcap);
+#endif /* PG_BELL */
+ 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 = xmalloc(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_all(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_all(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 HAVE_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 HAVE_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 HAVE_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 HAVE_WIDECHAR
+ if (MB_CUR_MAX > 1) {
+ makeprint_for_mb(s, l);
+ return;
+ }
+#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_all(1, b, sz);
+ if (ferror(f)) {
+ pgerror(errno, name);
+ exitstatus++;
+ }
+ return;
+ }
+ if ((fpos = 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) {
+ fseeko(find, line * sizeof pos, SEEK_SET);
+ if (fread(&pos, sizeof pos, 1, find) == 0)
+ tmperr(find, "index");
+ fseeko(find, (off_t)0, SEEK_END);
+ fseeko(fbuf, pos, SEEK_SET);
+ if (fgets(b, READBUF, fbuf) == NULL)
+ tmperr(fbuf, "buffer");
+ } else if (eofline == 0) {
+ fseeko(find, (off_t)0, SEEK_END);
+ do {
+ if (!nobuf)
+ fseeko(fbuf, (off_t)0, SEEK_END);
+ pos = ftello(fbuf);
+ if ((sig = setjmp(jmpenv)) != 0) {
+ /*
+ * We got a signal.
+ */
+ canjump = 0;
+ my_sigrelse(sig);
+ fseeko(fbuf, pos, SEEK_SET);
+ *b = '\0';
+ dline = pagelen;
+ break;
+ } else {
+ if (nobuf)
+ fseeko(f, fpos, SEEK_SET);
+ canjump = 1;
+ p = fgets(b, READBUF, f);
+ if (nobuf)
+ if ((fpos = 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_all(&pos, sizeof pos, 1, find);
+ if (!fflag) {
+ oldpos = pos;
+ p = b;
+ while (*(p = endline(ttycols,
+ p))
+ != '\0') {
+ pos = oldpos + (p - b);
+ fwrite_all(&pos,
+ sizeof pos,
+ 1, find);
+ fline++;
+ bline++;
+ }
+ }
+ fline++;
+ }
+ } while (line > bline++);
+ } else {
+ /*
+ * eofline != 0
+ */
+ eof = 1;
+ }
+ if (search == FORWARD && remembered == 1) {
+ 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_all(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) {
+ fseeko(find, --line * sizeof pos,
+ SEEK_SET);
+ if(fread(&pos, sizeof pos, 1,find)==0)
+ tmperr(find, "index");
+ fseeko(find, (off_t)0, SEEK_END);
+ 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.
+ */
+ fseeko(find, (off_t)0, SEEK_END);
+ for (;;) {
+ if (!nobuf)
+ fseeko(fbuf,(off_t)0,SEEK_END);
+ pos = ftello(fbuf);
+ if (fgets(b, READBUF, f) == NULL) {
+ eofline = fline;
+ break;
+ }
+ if (!nobuf)
+ fputs(b, fbuf);
+ fwrite_all(&pos, sizeof pos, 1, find);
+ if (!fflag) {
+ oldpos = pos;
+ p = b;
+ while (*(p = endline(ttycols,
+ p))
+ != '\0') {
+ pos = oldpos + (p - b);
+ fwrite_all(&pos,
+ sizeof pos,
+ 1, find);
+ fline++;
+ bline++;
+ }
+ }
+ fline++;
+ bline++;
+ }
+ fseeko(fbuf, (off_t)0, SEEK_SET);
+ while ((sz = fread(b, sizeof *b, READBUF,
+ fbuf)) != 0) {
+ /*
+ * No error check for compat.
+ */
+ fwrite_all(b, sizeof *b, sz, save);
+ }
+ fclose(save);
+ 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_all(1, cmd.cmdline,
+ strlen(cmd.cmdline));
+ write_all(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!
+ */
+ const char *help = _(helpscreen);
+ write_all(1, copyright, strlen(copyright));
+ write_all(1, help, strlen(help));
+ 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]);
+ xasprintf(&copyright,
+ _("%s %s Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.\n"),
+ program_invocation_short_name, PACKAGE_VERSION);
+
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ 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;
+ case 'h':
+ usage(stdout);
+ case 'V':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ 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_all(1, "::::::::::::::\n", 15);
+ write_all(1, argv[arg], strlen(argv[arg]));
+ write_all(1, "\n::::::::::::::\n", 16);
+ }
+ pgfile(input, argv[arg]);
+ if (input != stdin)
+ fclose(input);
+ }
+ }
+ quit(exitstatus);
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/text-utils/rev.1 b/text-utils/rev.1
new file mode 100644
index 0000000..b9b5f48
--- /dev/null
+++ b/text-utils/rev.1
@@ -0,0 +1,60 @@
+.\" Copyright (c) 1985, 1992 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)rev.1 6.3 (Berkeley) 3/21/92
+.\" Modified for Linux by Charles Hannum (mycroft@gnu.ai.mit.edu)
+.\" and Brian Koehmstedt (bpk@gnu.ai.mit.edu)
+.\"
+.TH REV "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+rev \- reverse lines of a file or files
+.SH SYNOPSIS
+.B rev
+[options] [file ...]
+.SH DESCRIPTION
+The
+.B rev
+utility copies the specified files to standard output, reversing the order of
+characters in every line. If no files are specified, standard input is read.
+.SH OPTIONS
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Output help and exit.
+.SH AVAILABILITY
+The rev command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
+.SH SEE ALSO
+.BR tac (1)
diff --git a/text-utils/rev.c b/text-utils/rev.c
new file mode 100644
index 0000000..a44f9c6
--- /dev/null
+++ b/text-utils/rev.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 1987, 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified for Linux by Charles Hannum (mycroft@gnu.ai.mit.edu)
+ * and Brian Koehmstedt (bpk@gnu.ai.mit.edu)
+ *
+ * Wed Sep 14 22:26:00 1994: Patch from bjdouma <bjdouma@xs4all.nl> to handle
+ * last line that has no newline correctly.
+ * 3-Jun-1998: Patched by Nicolai Langfeldt to work better on Linux:
+ * Handle any-length-lines. Code copied from util-linux' setpwnam.c
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ * July 2010 - Davidlohr Bueso <dave@gnu.org>
+ * Fixed memory leaks (including Linux signal handling)
+ * Added some memory allocation error handling
+ * Lowered the default buffer size to 256, instead of 512 bytes
+ * Changed tab indentation to 8 chars for better reading the code
+ */
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "c.h"
+#include "closestream.h"
+
+wchar_t *buf;
+
+static void sig_handler(int signo __attribute__ ((__unused__)))
+{
+ free(buf);
+ _exit(EXIT_SUCCESS);
+}
+
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
+{
+ fprintf(out, _("Usage: %s [options] [file ...]\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _("\nOptions:\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n"));
+
+ fprintf(out, _("\nFor more information see rev(1).\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ char *filename = "stdin";
+ wchar_t *t;
+ size_t len, bufsiz = BUFSIZ;
+ FILE *fp = stdin;
+ int ch, rval = EXIT_SUCCESS;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
+ static const struct option longopts[] = {
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ while ((ch = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1)
+ switch(ch) {
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ buf = xmalloc(bufsiz * sizeof(wchar_t));
+
+ do {
+ if (*argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ warn(_("cannot open %s"), *argv );
+ rval = EXIT_FAILURE;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+
+ while (fgetws(buf, bufsiz, fp)) {
+ len = wcslen(buf);
+
+ /* This is my hack from setpwnam.c -janl */
+ while (buf[len-1] != '\n' && !feof(fp)) {
+ /* Extend input buffer if it failed getting the whole line */
+ /* So now we double the buffer size */
+ bufsiz *= 2;
+
+ buf = xrealloc(buf, bufsiz * sizeof(wchar_t));
+
+ /* And fill the rest of the buffer */
+ if (!fgetws(&buf[len], bufsiz/2, fp))
+ break;
+
+ len = wcslen(buf);
+ }
+
+ t = buf + len - 1 - (*(buf+len-1)=='\r' || *(buf+len-1)=='\n');
+ for ( ; t >= buf; --t) {
+ if (*t != 0)
+ putwchar(*t);
+ }
+ putwchar('\n');
+ }
+
+ fflush(fp);
+ if (ferror(fp)) {
+ warn("%s", filename);
+ rval = EXIT_FAILURE;
+ }
+ if (fclose(fp))
+ rval = EXIT_FAILURE;
+ } while(*argv);
+
+ free(buf);
+ return rval;
+}
+
diff --git a/text-utils/tailf.1 b/text-utils/tailf.1
new file mode 100644
index 0000000..d64d2ca
--- /dev/null
+++ b/text-utils/tailf.1
@@ -0,0 +1,68 @@
+.\" tailf.1 --
+.\" Created: Thu Jan 11 16:43:10 1996 by faith@acm.org
+.\" Copyright 1996, 2003 Rickard E. Faith (faith@acm.org)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.TH TAILF 1 "February 2003" "util-linux" "User Commands"
+.SH NAME
+tailf \- follow the growth of a log file
+.SH SYNOPSIS
+.B tailf
+[\fIOPTION\fR] \fIfile\fR
+.SH DESCRIPTION
+.B tailf
+will print out the last 10 lines of a file and then wait for the file to
+grow. It is similar to
+.B tail -f
+but does not access the file when it is not growing. This has the side
+effect of not updating the access time for the file, so a filesystem flush
+does not occur periodically when no log activity is happening.
+.PP
+.B tailf
+is extremely useful for monitoring log files on a laptop when logging is
+infrequent and the user desires that the hard disk spin down to conserve
+battery life.
+.PP
+Mandatory arguments to long options are mandatory for short options too.
+.TP
+\fB\-n\fR, \fB\-\-lines\fR=\fIN\fR, \fB\-N\fR
+output the last
+.I N
+lines, instead of the last 10.
+.TP
+\fB\-V\fR, \fB\-\-version
+Output version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help
+Display help and exit.
+
+.SH AUTHOR
+This program was originally written by Rik Faith (faith@acm.org) and may be freely
+distributed under the terms of the X11/MIT License. There is ABSOLUTELY
+NO WARRANTY for this program.
+
+The latest inotify based implementation was written by Karel Zak (kzak@redhat.com).
+.SH "SEE ALSO"
+.BR tail "(1), " less "(1)"
+.SH AVAILABILITY
+The tailf command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/text-utils/tailf.c b/text-utils/tailf.c
new file mode 100644
index 0000000..9571645
--- /dev/null
+++ b/text-utils/tailf.c
@@ -0,0 +1,292 @@
+/* tailf.c -- tail a log file and then follow it
+ * Created: Tue Jan 9 15:49:21 1996 by faith@acm.org
+ * Copyright 1996, 2003 Rickard E. Faith (faith@acm.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * less -F and tail -f cause a disk access every five seconds. This
+ * program avoids this problem by waiting for the file size to change.
+ * Hence, the file is not accessed, and the access time does not need to be
+ * flushed back to disk. This is sort of a "stealth" tail.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#ifdef HAVE_INOTIFY_INIT
+#include <sys/inotify.h>
+#endif
+
+#include "nls.h"
+#include "xalloc.h"
+#include "usleep.h"
+#include "strutils.h"
+#include "c.h"
+#include "closestream.h"
+
+#define DEFAULT_LINES 10
+
+static void
+tailf(const char *filename, int lines)
+{
+ char *buf, *p;
+ int head = 0;
+ int tail = 0;
+ FILE *str;
+ int i;
+
+ if (!(str = fopen(filename, "r")))
+ err(EXIT_FAILURE, _("cannot open %s"), filename);
+
+ buf = xmalloc((lines ? lines : 1) * BUFSIZ);
+ p = buf;
+ while (fgets(p, BUFSIZ, str)) {
+ if (++tail >= lines) {
+ tail = 0;
+ head = 1;
+ }
+ p = buf + (tail * BUFSIZ);
+ }
+
+ if (head) {
+ for (i = tail; i < lines; i++)
+ fputs(buf + (i * BUFSIZ), stdout);
+ for (i = 0; i < tail; i++)
+ fputs(buf + (i * BUFSIZ), stdout);
+ } else {
+ for (i = head; i < tail; i++)
+ fputs(buf + (i * BUFSIZ), stdout);
+ }
+
+ fflush(stdout);
+ free(buf);
+ fclose(str);
+}
+
+static void
+roll_file(const char *filename, off_t *size)
+{
+ char buf[BUFSIZ];
+ int fd;
+ struct stat st;
+ off_t pos;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), filename);
+
+ if (fstat(fd, &st) == -1)
+ err(EXIT_FAILURE, _("stat failed %s"), filename);
+
+ if (st.st_size == *size) {
+ close(fd);
+ return;
+ }
+
+ if (lseek(fd, *size, SEEK_SET) != (off_t)-1) {
+ ssize_t rc, wc;
+
+ while ((rc = read(fd, buf, sizeof(buf))) > 0) {
+ wc = write(STDOUT_FILENO, buf, rc);
+ if (rc != wc)
+ warnx(_("incomplete write to \"%s\" (written %zd, expected %zd)\n"),
+ filename, wc, rc);
+ }
+ fflush(stdout);
+ }
+
+ pos = lseek(fd, 0, SEEK_CUR);
+
+ /* If we've successfully read something, use the file position, this
+ * avoids data duplication. If we read nothing or hit an error, reset
+ * to the reported size, this handles truncated files.
+ */
+ *size = (pos != -1 && pos != *size) ? pos : st.st_size;
+
+ close(fd);
+}
+
+static void
+watch_file(const char *filename, off_t *size)
+{
+ do {
+ roll_file(filename, size);
+ usleep(250000);
+ } while(1);
+}
+
+
+#ifdef HAVE_INOTIFY_INIT
+
+#define EVENTS (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
+#define NEVENTS 4
+
+static int
+watch_file_inotify(const char *filename, off_t *size)
+{
+ char buf[ NEVENTS * sizeof(struct inotify_event) ];
+ int fd, ffd, e;
+ ssize_t len;
+
+ fd = inotify_init();
+ if (fd == -1)
+ return 0;
+
+ ffd = inotify_add_watch(fd, filename, EVENTS);
+ if (ffd == -1) {
+ if (errno == ENOSPC)
+ errx(EXIT_FAILURE, _("%s: cannot add inotify watch "
+ "(limit of inotify watches was reached)."),
+ filename);
+
+ err(EXIT_FAILURE, _("%s: cannot add inotify watch."), filename);
+ }
+
+ while (ffd >= 0) {
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ if (len < 0)
+ err(EXIT_FAILURE,
+ _("%s: cannot read inotify events"), filename);
+
+ for (e = 0; e < len; ) {
+ struct inotify_event *ev = (struct inotify_event *) &buf[e];
+
+ if (ev->mask & IN_MODIFY)
+ roll_file(filename, size);
+ else {
+ close(ffd);
+ ffd = -1;
+ break;
+ }
+ e += sizeof(struct inotify_event) + ev->len;
+ }
+ }
+ close(fd);
+ return 1;
+}
+
+#endif /* HAVE_INOTIFY_INIT */
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out,
+ _("\nUsage:\n"
+ " %s [option] file\n"),
+ program_invocation_short_name);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -n, --lines NUMBER output the last NUMBER lines\n"
+ " -NUMBER same as `-n NUMBER'\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/* parses -N option */
+static long old_style_option(int *argc, char **argv)
+{
+ int i = 1, nargs = *argc;
+ long lines = -1;
+
+ while(i < nargs) {
+ if (argv[i][0] == '-' && isdigit(argv[i][1])) {
+ lines = strtol_or_err(argv[i] + 1,
+ _("failed to parse number of lines"));
+ nargs--;
+ if (nargs - i)
+ memmove(argv + i, argv + i + 1,
+ sizeof(char *) * (nargs - i));
+ } else
+ i++;
+ }
+ *argc = nargs;
+ return lines;
+}
+
+int main(int argc, char **argv)
+{
+ const char *filename;
+ long lines;
+ int ch;
+ struct stat st;
+ off_t size = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ static const struct option longopts[] = {
+ { "lines", required_argument, 0, 'n' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ lines = old_style_option(&argc, argv);
+ if (lines < 0)
+ lines = DEFAULT_LINES;
+
+ while ((ch = getopt_long(argc, argv, "n:N:Vh", longopts, NULL)) != -1)
+ switch((char)ch) {
+ case 'n':
+ case 'N':
+ lines = strtol_or_err(optarg,
+ _("failed to parse number of lines"));
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (argc == optind)
+ errx(EXIT_FAILURE, _("no input file specified"));
+
+ filename = argv[optind];
+
+ if (stat(filename, &st) != 0)
+ err(EXIT_FAILURE, _("stat failed %s"), filename);
+
+ size = st.st_size;;
+ tailf(filename, lines);
+
+#ifdef HAVE_INOTIFY_INIT
+ if (!watch_file_inotify(filename, &size))
+#endif
+ watch_file(filename, &size);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/text-utils/ul.1 b/text-utils/ul.1
new file mode 100644
index 0000000..d6089d0
--- /dev/null
+++ b/text-utils/ul.1
@@ -0,0 +1,113 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ul.1 8.1 (Berkeley) 6/6/93
+.\"
+.TH UL "1" "September 2011" "util-linux" "User Commands"
+.SH NAME
+ul \- do underlining
+.SH SYNOPSIS
+.B ul
+[options] [file ...]
+.SH DESCRIPTION
+.B ul
+reads the named files (or standard input if none are given) and translates
+occurrences of underscores to the sequence which indicates underlining for
+the terminal in use, as specified by the environment variable
+.BR TERM .
+The
+.I terminfo
+database is read to determine the appropriate sequences for underlining. If
+the terminal is incapable of underlining but is capable of a standout mode,
+then that is used instead. If the terminal can overstrike, or handles
+underlining automatically,
+.B ul
+degenerates to
+.BR cat (1).
+If the terminal cannot underline, underlining is ignored.
+.SH OPTIONS
+.TP
+\fB\-i\fR, \fB\-\-indicated\fR
+Underlining is indicated by a separate line containing appropriate dashes
+`\-'; this is useful when you want to look at the underlining which is
+present in an
+.B nroff
+output stream on a crt-terminal.
+.TP
+\fB\-t\fR, \fB\-\-terminal\fR \fIterminal\fR
+.It Fl t Ar terminal
+Overrides the
+.I terminal
+type specified in the environment with
+.BR TERM .
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display a help text and exit.
+.SH ENVIRONMENT
+The following environment variable is used:
+.TP
+.B TERM
+The
+.B TERM
+variable is used to relate a tty device with its device capability
+description (see
+.BR terminfo (5)).
+.B TERM
+is set at login time, either by the default terminal type specified in
+.I /etc/ttys
+or as set during the login process by the user in their
+.B login
+file (see
+.BR setenv (1)).
+.SH SEE ALSO
+.BR colcrt (1),
+.BR login (1),
+.BR man (1),
+.BR nroff (1),
+.BR setenv (1),
+.BR terminfo (5)
+.SH BUGS
+.B Nroff
+usually outputs a series of backspaces and underlines intermixed with the
+text to indicate underlining. No attempt is made to optimize the backward
+motion.
+.SH HISTORY
+The
+.B ul
+command appeared in 3.0BSD.
+.SH AVAILABILITY
+The ul command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/text-utils/ul.c b/text-utils/ul.c
new file mode 100644
index 0000000..3c7bf50
--- /dev/null
+++ b/text-utils/ul.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * modified by Kars de Jong <jongk@cs.utwente.nl>
+ * to use terminfo instead of termcap.
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * added Native Language Support
+ * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
+ * modified to work correctly in multi-byte locales
+ */
+
+#include <stdio.h>
+#include <unistd.h> /* for getopt(), isatty() */
+#include <string.h> /* for memset(), strcpy() */
+#include <term.h> /* for setupterm() */
+#include <stdlib.h> /* for getenv() */
+#include <limits.h> /* for INT_MAX */
+#include <signal.h> /* for signal() */
+#include <errno.h>
+#include <getopt.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "widechar.h"
+#include "c.h"
+#include "closestream.h"
+
+#ifdef HAVE_WIDECHAR
+/* Output an ASCII character as a wide character */
+static int put1wc(int c)
+{
+ if (putwchar(c) == WEOF)
+ return EOF;
+ else
+ return c;
+}
+#define putwp(s) tputs(s, STDOUT_FILENO, put1wc)
+#else
+#define putwp(s) putp(s)
+#endif
+
+static void usage(FILE *out);
+static int handle_escape(FILE * f);
+static void filter(FILE *f);
+static void flushln(void);
+static void overstrike(void);
+static void iattr(void);
+static void initbuf(void);
+static void fwd(void);
+static void reverse(void);
+static void initinfo(void);
+static void outc(wint_t c, int width);
+static void setmode(int newmode);
+static void setcol(int newcol);
+static void needcol(int col);
+static void sig_handler(int signo);
+static void print_out(char *line);
+
+#define IESC '\033'
+#define SO '\016'
+#define SI '\017'
+#define HFWD '9'
+#define HREV '8'
+#define FREV '7'
+
+#define NORMAL 000
+#define ALTSET 001 /* Reverse */
+#define SUPERSC 002 /* Dim */
+#define SUBSC 004 /* Dim | Ul */
+#define UNDERL 010 /* Ul */
+#define BOLD 020 /* Bold */
+
+int must_use_uc, must_overstrike;
+char *CURS_UP,
+ *CURS_RIGHT,
+ *CURS_LEFT,
+ *ENTER_STANDOUT,
+ *EXIT_STANDOUT,
+ *ENTER_UNDERLINE,
+ *EXIT_UNDERLINE,
+ *ENTER_DIM,
+ *ENTER_BOLD,
+ *ENTER_REVERSE,
+ *UNDER_CHAR,
+ *EXIT_ATTRIBUTES;
+
+struct CHAR {
+ char c_mode;
+ wchar_t c_char;
+ int c_width;
+};
+
+struct CHAR *obuf;
+int obuflen;
+int col, maxcol;
+int mode;
+int halfpos;
+int upln;
+int iflag;
+
+static void __attribute__((__noreturn__))
+usage(FILE *out)
+{
+ fprintf(out, _(
+ "\nUsage:\n"
+ " %s [options] [file...]\n"), program_invocation_short_name);
+
+ fprintf(out, _(
+ "\nOptions:\n"
+ " -t, --terminal TERMINAL override the TERM environment variable\n"
+ " -i, --indicated underlining is indicated via a separate line\n"
+ " -V, --version output version information and exit\n"
+ " -h, --help display this help and exit\n\n"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv)
+{
+ int c, ret, tflag = 0;
+ char *termtype;
+ FILE *f;
+
+ static const struct option longopts[] = {
+ { "terminal", required_argument, 0, 't' },
+ { "indicated", no_argument, 0, 'i' },
+ { "version", no_argument, 0, 'V' },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
+ termtype = getenv("TERM");
+
+ /*
+ * FIXME: why terminal type is lpr when command begins with c and has
+ * no terminal? If this behavior can be explained please insert
+ * reference or remove the code. In case this truly is desired command
+ * behavior this should be mentioned in manual page.
+ */
+ if (termtype == NULL || (argv[0][0] == 'c' && !isatty(STDOUT_FILENO)))
+ termtype = "lpr";
+
+ while ((c = getopt_long(argc, argv, "it:T:Vh", longopts, NULL)) != -1)
+ switch (c) {
+
+ case 't':
+ case 'T':
+ /* for nroff compatibility */
+ termtype = optarg;
+ tflag = 1;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'V':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ setupterm(termtype, STDOUT_FILENO, &ret);
+ switch (ret) {
+
+ case 1:
+ break;
+
+ default:
+ warnx(_("trouble reading terminfo"));
+ /* fall through to ... */
+
+ case 0:
+ if (tflag)
+ warnx(_("terminal `%s' is not known, defaulting to `dumb'"),
+ termtype);
+ setupterm("dumb", STDOUT_FILENO, (int *)0);
+ break;
+ }
+ initinfo();
+ if ((tigetflag("os") && ENTER_BOLD==NULL ) ||
+ (tigetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+ must_overstrike = 1;
+ initbuf();
+ if (optind == argc)
+ filter(stdin);
+ else
+ for (; optind < argc; optind++) {
+ f = fopen(argv[optind],"r");
+ if (!f)
+ err(EXIT_FAILURE, _("cannot open %s"),
+ argv[optind]);
+ filter(f);
+ fclose(f);
+ }
+ free(obuf);
+ return EXIT_SUCCESS;
+}
+
+static int handle_escape(FILE * f)
+{
+ wint_t c;
+
+ switch (c = getwc(f)) {
+ case HREV:
+ if (halfpos == 0) {
+ mode |= SUPERSC;
+ halfpos--;
+ } else if (halfpos > 0) {
+ mode &= ~SUBSC;
+ halfpos--;
+ } else {
+ halfpos = 0;
+ reverse();
+ }
+ return 0;
+ case HFWD:
+ if (halfpos == 0) {
+ mode |= SUBSC;
+ halfpos++;
+ } else if (halfpos < 0) {
+ mode &= ~SUPERSC;
+ halfpos++;
+ } else {
+ halfpos = 0;
+ fwd();
+ }
+ return 0;
+ case FREV:
+ reverse();
+ return 0;
+ default:
+ /* unknown escape */
+ ungetwc(c, f);
+ return 1;
+ }
+}
+
+static void filter(FILE *f)
+{
+ wint_t c;
+ int i, w;
+
+ while ((c = getwc(f)) != WEOF)
+ switch (c) {
+
+ case '\b':
+ setcol(col - 1);
+ continue;
+
+ case '\t':
+ setcol((col+8) & ~07);
+ continue;
+
+ case '\r':
+ setcol(0);
+ continue;
+
+ case SO:
+ mode |= ALTSET;
+ continue;
+
+ case SI:
+ mode &= ~ALTSET;
+ continue;
+
+ case IESC:
+ if(handle_escape(f)) {
+ c = getwc(f);
+ errx(EXIT_FAILURE,
+ _("unknown escape sequence in input: %o, %o"),
+ IESC, c);
+ }
+ continue;
+
+ case '_':
+ if (obuf[col].c_char || obuf[col].c_width < 0) {
+ while(col > 0 && obuf[col].c_width < 0)
+ col--;
+ w = obuf[col].c_width;
+ for (i = 0; i < w; i++)
+ obuf[col++].c_mode |= UNDERL | mode;
+ setcol(col);
+ continue;
+ }
+ obuf[col].c_char = '_';
+ obuf[col].c_width = 1;
+ /* fall through */
+ case ' ':
+ setcol(col + 1);
+ continue;
+
+ case '\n':
+ flushln();
+ continue;
+
+ case '\f':
+ flushln();
+ putwchar('\f');
+ continue;
+
+ default:
+ if (!iswprint(c))
+ /* non printable */
+ continue;
+ w = wcwidth(c);
+ needcol(col + w);
+ if (obuf[col].c_char == '\0') {
+ obuf[col].c_char = c;
+ for (i = 0; i < w; i++)
+ obuf[col+i].c_mode = mode;
+ obuf[col].c_width = w;
+ for (i = 1; i < w; i++)
+ obuf[col+i].c_width = -1;
+ } else if (obuf[col].c_char == '_') {
+ obuf[col].c_char = c;
+ for (i = 0; i < w; i++)
+ obuf[col+i].c_mode |= UNDERL|mode;
+ obuf[col].c_width = w;
+ for (i = 1; i < w; i++)
+ obuf[col+i].c_width = -1;
+ } else if ((wint_t) obuf[col].c_char == c) {
+ for (i = 0; i < w; i++)
+ obuf[col+i].c_mode |= BOLD|mode;
+ } else {
+ w = obuf[col].c_width;
+ for (i = 0; i < w; i++)
+ obuf[col+i].c_mode = mode;
+ }
+ setcol(col + w);
+ continue;
+ }
+ if (maxcol)
+ flushln();
+}
+
+static void flushln(void)
+{
+ int lastmode;
+ int i;
+ int hadmodes = 0;
+
+ lastmode = NORMAL;
+ for (i = 0; i < maxcol; i++) {
+ if (obuf[i].c_mode != lastmode) {
+ hadmodes++;
+ setmode(obuf[i].c_mode);
+ lastmode = obuf[i].c_mode;
+ }
+ if (obuf[i].c_char == '\0') {
+ if (upln) {
+ print_out(CURS_RIGHT);
+ } else
+ outc(' ', 1);
+ } else
+ outc(obuf[i].c_char, obuf[i].c_width);
+ if (obuf[i].c_width > 1)
+ i += obuf[i].c_width - 1;
+ }
+ if (lastmode != NORMAL) {
+ setmode(0);
+ }
+ if (must_overstrike && hadmodes)
+ overstrike();
+ putwchar('\n');
+ if (iflag && hadmodes)
+ iattr();
+ fflush(stdout);
+ if (upln)
+ upln--;
+ initbuf();
+}
+
+/*
+ * For terminals that can overstrike, overstrike underlines and bolds.
+ * We don't do anything with halfline ups and downs, or Greek.
+ */
+static void overstrike(void)
+{
+ register int i;
+#ifdef __GNUC__
+ register wchar_t *lbuf = __builtin_alloca((maxcol + 1) * sizeof(wchar_t));
+#else
+ wchar_t lbuf[BUFSIZ];
+#endif
+ register wchar_t *cp = lbuf;
+ int hadbold=0;
+
+ /* Set up overstrike buffer */
+ for (i = 0; i < maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL:
+ default:
+ *cp++ = ' ';
+ break;
+ case UNDERL:
+ *cp++ = '_';
+ break;
+ case BOLD:
+ *cp++ = obuf[i].c_char;
+ if (obuf[i].c_width > 1)
+ i += obuf[i].c_width - 1;
+ hadbold=1;
+ break;
+ }
+ putwchar('\r');
+ for (*cp = ' '; *cp == ' '; cp--)
+ *cp = 0;
+ for (cp = lbuf; *cp; cp++)
+ putwchar(*cp);
+ if (hadbold) {
+ putwchar('\r');
+ for (cp = lbuf; *cp; cp++)
+ putwchar(*cp == '_' ? ' ' : *cp);
+ putwchar('\r');
+ for (cp = lbuf; *cp; cp++)
+ putwchar(*cp == '_' ? ' ' : *cp);
+ }
+}
+
+static void iattr(void)
+{
+ register int i;
+#ifdef __GNUC__
+ register char *lbuf = __builtin_alloca((maxcol+1)*sizeof(char));
+#else
+ char lbuf[BUFSIZ];
+#endif
+ register char *cp = lbuf;
+
+ for (i = 0; i < maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL: *cp++ = ' '; break;
+ case ALTSET: *cp++ = 'g'; break;
+ case SUPERSC: *cp++ = '^'; break;
+ case SUBSC: *cp++ = 'v'; break;
+ case UNDERL: *cp++ = '_'; break;
+ case BOLD: *cp++ = '!'; break;
+ default: *cp++ = 'X'; break;
+ }
+ for (*cp = ' '; *cp == ' '; cp--)
+ *cp = 0;
+ for (cp = lbuf; *cp; cp++)
+ putwchar(*cp);
+ putwchar('\n');
+}
+
+static void initbuf(void)
+{
+ if (obuf == NULL) {
+ /* First time. */
+ obuflen = BUFSIZ;
+ obuf = xmalloc(sizeof(struct CHAR) * obuflen);
+ }
+
+ /* assumes NORMAL == 0 */
+ memset(obuf, 0, sizeof(struct CHAR) * obuflen);
+ setcol(0);
+ maxcol = 0;
+ mode &= ALTSET;
+}
+
+static void fwd(void)
+{
+ int oldcol, oldmax;
+
+ oldcol = col;
+ oldmax = maxcol;
+ flushln();
+ setcol(oldcol);
+ maxcol = oldmax;
+}
+
+static void reverse(void)
+{
+ upln++;
+ fwd();
+ print_out(CURS_UP);
+ print_out(CURS_UP);
+ upln++;
+}
+
+static void initinfo(void)
+{
+ CURS_UP = tigetstr("cuu1");
+ CURS_RIGHT = tigetstr("cuf1");
+ CURS_LEFT = tigetstr("cub1");
+ if (CURS_LEFT == NULL)
+ CURS_LEFT = "\b";
+
+ ENTER_STANDOUT = tigetstr("smso");
+ EXIT_STANDOUT = tigetstr("rmso");
+ ENTER_UNDERLINE = tigetstr("smul");
+ EXIT_UNDERLINE = tigetstr("rmul");
+ ENTER_DIM = tigetstr("dim");
+ ENTER_BOLD = tigetstr("bold");
+ ENTER_REVERSE = tigetstr("rev");
+ EXIT_ATTRIBUTES = tigetstr("sgr0");
+
+ if (!ENTER_BOLD && ENTER_REVERSE)
+ ENTER_BOLD = ENTER_REVERSE;
+ if (!ENTER_BOLD && ENTER_STANDOUT)
+ ENTER_BOLD = ENTER_STANDOUT;
+ if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
+ ENTER_UNDERLINE = ENTER_STANDOUT;
+ EXIT_UNDERLINE = EXIT_STANDOUT;
+ }
+ if (!ENTER_DIM && ENTER_STANDOUT)
+ ENTER_DIM = ENTER_STANDOUT;
+ if (!ENTER_REVERSE && ENTER_STANDOUT)
+ ENTER_REVERSE = ENTER_STANDOUT;
+ if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
+ EXIT_ATTRIBUTES = EXIT_STANDOUT;
+
+ /*
+ * Note that we use REVERSE for the alternate character set,
+ * not the as/ae capabilities. This is because we are modelling
+ * the model 37 teletype (since that's what nroff outputs) and
+ * the typical as/ae is more of a graphics set, not the greek
+ * letters the 37 has.
+ */
+
+ UNDER_CHAR = tigetstr("uc");
+ must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
+}
+
+static int curmode = 0;
+
+static void outc(wint_t c, int width) {
+ int i;
+
+ putwchar(c);
+ if (must_use_uc && (curmode&UNDERL)) {
+ for (i = 0; i < width; i++)
+ print_out(CURS_LEFT);
+ for (i = 0; i < width; i++)
+ print_out(UNDER_CHAR);
+ }
+}
+
+static void setmode(int newmode)
+{
+ if (!iflag) {
+ if (curmode != NORMAL && newmode != NORMAL)
+ setmode(NORMAL);
+ switch (newmode) {
+ case NORMAL:
+ switch (curmode) {
+ case NORMAL:
+ break;
+ case UNDERL:
+ print_out(EXIT_UNDERLINE);
+ break;
+ default:
+ /* This includes standout */
+ print_out(EXIT_ATTRIBUTES);
+ break;
+ }
+ break;
+ case ALTSET:
+ print_out(ENTER_REVERSE);
+ break;
+ case SUPERSC:
+ /*
+ * This only works on a few terminals.
+ * It should be fixed.
+ */
+ print_out(ENTER_UNDERLINE);
+ print_out(ENTER_DIM);
+ break;
+ case SUBSC:
+ print_out(ENTER_DIM);
+ break;
+ case UNDERL:
+ print_out(ENTER_UNDERLINE);
+ break;
+ case BOLD:
+ print_out(ENTER_BOLD);
+ break;
+ default:
+ /*
+ * We should have some provision here for multiple modes
+ * on at once. This will have to come later.
+ */
+ print_out(ENTER_STANDOUT);
+ break;
+ }
+ }
+ curmode = newmode;
+}
+
+static void setcol(int newcol) {
+ col = newcol;
+
+ if (col < 0)
+ col = 0;
+ else if (col > maxcol)
+ needcol(col);
+}
+
+static void needcol(int acol) {
+ maxcol = acol;
+
+ /* If col >= obuflen, expand obuf until obuflen > col. */
+ while (acol >= obuflen) {
+ /* Paranoid check for obuflen == INT_MAX. */
+ if (obuflen == INT_MAX)
+ errx(EXIT_FAILURE, _("Input line too long."));
+
+ /* Similar paranoia: double only up to INT_MAX. */
+ if (obuflen < (INT_MAX / 2))
+ obuflen *= 2;
+ else
+ obuflen = INT_MAX;
+
+ /* Now we can try to expand obuf. */
+ obuf = xrealloc(obuf, sizeof(struct CHAR) * obuflen);
+ }
+}
+
+static void sig_handler(int signo __attribute__ ((__unused__)))
+{
+ _exit(EXIT_SUCCESS);
+}
+
+static void print_out(char *line)
+{
+ if (line == NULL)
+ return;
+
+ putwp(line);
+}