diff options
Diffstat (limited to 'text-utils')
-rw-r--r-- | text-utils/Makemodule.am | 81 | ||||
-rw-r--r-- | text-utils/col.1 | 161 | ||||
-rw-r--r-- | text-utils/col.c | 576 | ||||
-rw-r--r-- | text-utils/colcrt.1 | 102 | ||||
-rw-r--r-- | text-utils/colcrt.c | 324 | ||||
-rw-r--r-- | text-utils/colrm.1 | 75 | ||||
-rw-r--r-- | text-utils/colrm.c | 194 | ||||
-rw-r--r-- | text-utils/column.1 | 77 | ||||
-rw-r--r-- | text-utils/column.c | 417 | ||||
-rw-r--r-- | text-utils/conv.c | 109 | ||||
-rw-r--r-- | text-utils/display.c | 348 | ||||
-rw-r--r-- | text-utils/hexdump.1 | 324 | ||||
-rw-r--r-- | text-utils/hexdump.c | 82 | ||||
-rw-r--r-- | text-utils/hexdump.h | 93 | ||||
-rw-r--r-- | text-utils/hexsyntax.c | 142 | ||||
-rw-r--r-- | text-utils/line.1 | 17 | ||||
-rw-r--r-- | text-utils/line.c | 45 | ||||
-rw-r--r-- | text-utils/more.1 | 249 | ||||
-rw-r--r-- | text-utils/more.c | 2197 | ||||
-rw-r--r-- | text-utils/parse.c | 495 | ||||
-rw-r--r-- | text-utils/pg.1 | 232 | ||||
-rw-r--r-- | text-utils/pg.c | 1783 | ||||
-rw-r--r-- | text-utils/rev.1 | 60 | ||||
-rw-r--r-- | text-utils/rev.c | 177 | ||||
-rw-r--r-- | text-utils/tailf.1 | 68 | ||||
-rw-r--r-- | text-utils/tailf.c | 292 | ||||
-rw-r--r-- | text-utils/ul.1 | 113 | ||||
-rw-r--r-- | text-utils/ul.c | 672 |
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(©right, + _("%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); +} |