MFC: r282305, r282309, r282342, r282669, r282722
r282305: col: fixing 25 year old bug Makes col(1) respect POSIX again for escape sequences as decribed in its manpage The bug was introduced in CSRG in 1990 r282309: Use defines to improve clarity r282342: Capsicumize col(1) r282669: Fix about ten integer overflows and underflows and a handful of logic errors in line number handling. Submitted by: schwarze at OpenBSD Discussed with: schwarze at OpenBSD Obtained from: OpenBSD r282722: For half and reverse line feeds, recognize both SUSv2-style escape-digit and BSD-style escape-control-char sequences in the input stream. Submitted by: schwarze at OpenBSD Discussed with: schwarze at OpenBSD Obtained from: OpenBSD
This commit is contained in:
parent
05da681425
commit
cafa3c95e7
@ -31,7 +31,7 @@
|
||||
.\" @(#)col.1 8.1 (Berkeley) 6/29/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 4, 2004
|
||||
.Dd May 10, 2015
|
||||
.Dt COL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -82,18 +82,33 @@ recognized and interpreted by itself, which are listed below.
|
||||
Output multiple spaces instead of tabs.
|
||||
.El
|
||||
.Pp
|
||||
The control sequences for carriage motion that
|
||||
In the input stream,
|
||||
.Nm
|
||||
understands and their decimal values are listed in the following
|
||||
table:
|
||||
understands both the escape sequences of the form escape-digit
|
||||
mandated by
|
||||
.St -susv2
|
||||
and the traditional
|
||||
.Bx
|
||||
format escape-control-character.
|
||||
The control sequences for carriage motion and their ASCII values
|
||||
are as follows:
|
||||
.Pp
|
||||
.Bl -tag -width "carriage return" -compact
|
||||
.It ESC\-BELL
|
||||
reverse line feed (escape then bell).
|
||||
.It ESC\-7
|
||||
reverse line feed (escape then 7)
|
||||
reverse line feed (escape then 7).
|
||||
.It ESC\-BACKSPACE
|
||||
half reverse line feed (escape then backspace).
|
||||
.It ESC\-8
|
||||
half reverse line feed (escape then 8)
|
||||
half reverse line feed (escape then 8).
|
||||
.It ESC\-TAB
|
||||
half forward line feed (escape than tab).
|
||||
.It ESC\-9
|
||||
half forward line feed (escape then 9)
|
||||
half forward line feed (escape then 9).
|
||||
In
|
||||
.Fl f
|
||||
mode, this sequence may also occur in the output stream.
|
||||
.It backspace
|
||||
moves back one column (8); ignored in the first column
|
||||
.It carriage return
|
||||
|
@ -45,11 +45,15 @@ static char sccsid[] = "@(#)col.c 8.5 (Berkeley) 5/4/95";
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/capability.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
@ -63,9 +67,9 @@ __FBSDID("$FreeBSD$");
|
||||
#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 */
|
||||
#define RLF '7' /* ESC-7 reverse line feed */
|
||||
#define RHLF '8' /* ESC-8 reverse half-line feed */
|
||||
#define FHLF '9' /* ESC-9 forward half-line feed */
|
||||
|
||||
/* build up at least this many lines before flushing them out */
|
||||
#define BUFFER_MARGIN 32
|
||||
@ -92,6 +96,7 @@ struct line_str {
|
||||
int l_max_col; /* max column in the line */
|
||||
};
|
||||
|
||||
static void addto_lineno(int *, int);
|
||||
static LINE *alloc_line(void);
|
||||
static void dowarn(int);
|
||||
static void flush_line(LINE *);
|
||||
@ -104,7 +109,7 @@ static CSET last_set; /* char_set of last char printed */
|
||||
static LINE *lines;
|
||||
static int compress_spaces; /* if doing space -> tab conversion */
|
||||
static int fine; /* if `fine' resolution (half lines) */
|
||||
static int max_bufd_lines; /* max # lines to keep in memory */
|
||||
static int max_bufd_lines; /* max # of half lines to keep in memory */
|
||||
static int nblank_lines; /* # blanks after last flushed line */
|
||||
static int no_backspaces; /* if not to output any backspaces */
|
||||
static int pass_unknown_seqs; /* pass unknown control sequences */
|
||||
@ -129,10 +134,26 @@ main(int argc, char **argv)
|
||||
int this_line; /* line l points to */
|
||||
int nflushd_lines; /* number of lines that were flushed */
|
||||
int adjust, opt, warned, width;
|
||||
const char *errstr;
|
||||
cap_rights_t rights;
|
||||
unsigned long cmd;
|
||||
|
||||
(void)setlocale(LC_CTYPE, "");
|
||||
|
||||
max_bufd_lines = 128;
|
||||
cap_rights_init(&rights, CAP_FSTAT, CAP_READ);
|
||||
if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS)
|
||||
err(1, "unable to limit rights for stdin");
|
||||
cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE, CAP_IOCTL);
|
||||
if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS)
|
||||
err(1, "unable to limit rights for stdout");
|
||||
cmd = TIOCGETA; /* required by isatty(3) in printf(3) */
|
||||
if (cap_ioctls_limit(STDOUT_FILENO, &cmd, 1) < 0 && errno != ENOSYS)
|
||||
err(1, "unable to limit ioctls for stdout");
|
||||
|
||||
if (cap_enter() < 0 && errno != ENOSYS)
|
||||
err(1, "unable to enter capability mode");
|
||||
|
||||
max_bufd_lines = 256;
|
||||
compress_spaces = 1; /* compress spaces into tabs */
|
||||
while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
|
||||
switch (opt) {
|
||||
@ -146,8 +167,11 @@ main(int argc, char **argv)
|
||||
compress_spaces = 1;
|
||||
break;
|
||||
case 'l': /* buffered line count */
|
||||
if ((max_bufd_lines = atoi(optarg)) <= 0)
|
||||
errx(1, "bad -l argument %s", optarg);
|
||||
max_bufd_lines = strtonum(optarg, 1,
|
||||
(INT_MAX - BUFFER_MARGIN) / 2, &errstr) * 2;
|
||||
if (errstr != NULL)
|
||||
errx(1, "bad -l argument, %s: %s", errstr,
|
||||
optarg);
|
||||
break;
|
||||
case 'p': /* pass unknown control sequences */
|
||||
pass_unknown_seqs = 1;
|
||||
@ -163,9 +187,6 @@ main(int argc, char **argv)
|
||||
if (optind != argc)
|
||||
usage();
|
||||
|
||||
/* this value is in half lines */
|
||||
max_bufd_lines *= 2;
|
||||
|
||||
adjust = cur_col = extra_lines = warned = 0;
|
||||
cur_line = max_line = nflushd_lines = this_line = 0;
|
||||
cur_set = last_set = CS_NORMAL;
|
||||
@ -184,20 +205,31 @@ main(int argc, char **argv)
|
||||
continue;
|
||||
case ESC: /* just ignore EOF */
|
||||
switch(getwchar()) {
|
||||
/*
|
||||
* In the input stream, accept both the
|
||||
* XPG5 sequences ESC-digit and the
|
||||
* traditional BSD sequences ESC-ctrl.
|
||||
*/
|
||||
case '\007':
|
||||
/* FALLTHROUGH */
|
||||
case RLF:
|
||||
cur_line -= 2;
|
||||
addto_lineno(&cur_line, -2);
|
||||
break;
|
||||
case '\010':
|
||||
/* FALLTHROUGH */
|
||||
case RHLF:
|
||||
cur_line--;
|
||||
addto_lineno(&cur_line, -1);
|
||||
break;
|
||||
case '\011':
|
||||
/* FALLTHROUGH */
|
||||
case FHLF:
|
||||
cur_line++;
|
||||
addto_lineno(&cur_line, 1);
|
||||
if (cur_line > max_line)
|
||||
max_line = cur_line;
|
||||
}
|
||||
continue;
|
||||
case NL:
|
||||
cur_line += 2;
|
||||
addto_lineno(&cur_line, 2);
|
||||
if (cur_line > max_line)
|
||||
max_line = cur_line;
|
||||
cur_col = 0;
|
||||
@ -216,7 +248,7 @@ main(int argc, char **argv)
|
||||
++cur_col;
|
||||
continue;
|
||||
case VT:
|
||||
cur_line -= 2;
|
||||
addto_lineno(&cur_line, -2);
|
||||
continue;
|
||||
}
|
||||
if (iswspace(ch)) {
|
||||
@ -229,58 +261,61 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Must stuff ch in a line - are we at the right one? */
|
||||
if (cur_line != this_line - adjust) {
|
||||
if (cur_line + adjust != this_line) {
|
||||
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++)
|
||||
/* round up to next line */
|
||||
adjust = !fine && (cur_line & 1);
|
||||
|
||||
if (cur_line + adjust < this_line) {
|
||||
while (cur_line + adjust < this_line &&
|
||||
l->l_prev != NULL) {
|
||||
l = l->l_prev;
|
||||
if (nmove) {
|
||||
this_line--;
|
||||
}
|
||||
if (cur_line + adjust < this_line) {
|
||||
if (nflushd_lines == 0) {
|
||||
/*
|
||||
* Allow backup past first
|
||||
* line if nothing has been
|
||||
* flushed yet.
|
||||
*/
|
||||
for (; nmove < 0; nmove++) {
|
||||
while (cur_line + adjust
|
||||
< this_line) {
|
||||
lnew = alloc_line();
|
||||
l->l_prev = lnew;
|
||||
lnew->l_next = l;
|
||||
l = lines = lnew;
|
||||
extra_lines++;
|
||||
this_line--;
|
||||
}
|
||||
} else {
|
||||
if (!warned++)
|
||||
dowarn(cur_line);
|
||||
cur_line -= nmove;
|
||||
cur_line = this_line - adjust;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* may need to allocate here */
|
||||
for (; nmove > 0 && l->l_next; nmove--)
|
||||
while (cur_line + adjust > this_line) {
|
||||
if (l->l_next == NULL) {
|
||||
l->l_next = alloc_line();
|
||||
l->l_next->l_prev = l;
|
||||
}
|
||||
l = l->l_next;
|
||||
for (; nmove > 0; nmove--) {
|
||||
lnew = alloc_line();
|
||||
lnew->l_prev = l;
|
||||
l->l_next = lnew;
|
||||
l = lnew;
|
||||
this_line++;
|
||||
}
|
||||
}
|
||||
this_line = cur_line + adjust;
|
||||
nmove = this_line - nflushd_lines;
|
||||
if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
|
||||
nflushd_lines += nmove - max_bufd_lines;
|
||||
flush_lines(nmove - max_bufd_lines);
|
||||
if (this_line > nflushd_lines &&
|
||||
this_line - nflushd_lines >=
|
||||
max_bufd_lines + BUFFER_MARGIN) {
|
||||
if (extra_lines) {
|
||||
flush_lines(extra_lines);
|
||||
extra_lines = 0;
|
||||
}
|
||||
flush_lines(this_line - nflushd_lines -
|
||||
max_bufd_lines);
|
||||
nflushd_lines = this_line - max_bufd_lines;
|
||||
}
|
||||
}
|
||||
/* grow line's buffer? */
|
||||
@ -311,25 +346,23 @@ main(int argc, char **argv)
|
||||
}
|
||||
if (ferror(stdin))
|
||||
err(1, NULL);
|
||||
if (max_line == 0)
|
||||
exit(0); /* no lines, so just exit */
|
||||
if (extra_lines)
|
||||
flush_lines(extra_lines);
|
||||
|
||||
/* 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);
|
||||
flush_lines(this_line - nflushd_lines + 1);
|
||||
|
||||
/* make sure we leave things in a sane state */
|
||||
if (last_set != CS_NORMAL)
|
||||
PUTC('\017');
|
||||
PUTC(SI);
|
||||
|
||||
/* flush out the last few blank lines */
|
||||
nblank_lines = max_line - this_line;
|
||||
if (max_line > this_line)
|
||||
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();
|
||||
exit(0);
|
||||
}
|
||||
@ -346,7 +379,8 @@ flush_lines(int nflush)
|
||||
flush_blanks();
|
||||
flush_line(l);
|
||||
}
|
||||
nblank_lines++;
|
||||
if (l->l_line || l->l_next)
|
||||
nblank_lines++;
|
||||
if (l->l_line)
|
||||
(void)free(l->l_line);
|
||||
free_line(l);
|
||||
@ -377,8 +411,8 @@ flush_blanks(void)
|
||||
for (i = nb; --i >= 0;)
|
||||
PUTC('\n');
|
||||
if (half) {
|
||||
PUTC('\033');
|
||||
PUTC('9');
|
||||
PUTC(ESC);
|
||||
PUTC(FHLF);
|
||||
if (!nb)
|
||||
PUTC('\r');
|
||||
}
|
||||
@ -480,10 +514,10 @@ flush_line(LINE *l)
|
||||
if (c->c_set != last_set) {
|
||||
switch (c->c_set) {
|
||||
case CS_NORMAL:
|
||||
PUTC('\017');
|
||||
PUTC(SI);
|
||||
break;
|
||||
case CS_ALTERNATE:
|
||||
PUTC('\016');
|
||||
PUTC(SO);
|
||||
}
|
||||
last_set = c->c_set;
|
||||
}
|
||||
@ -498,6 +532,23 @@ flush_line(LINE *l)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment or decrement a line number, checking for overflow.
|
||||
* Stop one below INT_MAX such that the adjust variable is safe.
|
||||
*/
|
||||
void
|
||||
addto_lineno(int *lno, int offset)
|
||||
{
|
||||
if (offset > 0) {
|
||||
if (*lno >= INT_MAX - offset)
|
||||
errx(1, "too many lines");
|
||||
} else {
|
||||
if (*lno < INT_MIN - offset)
|
||||
errx(1, "too many reverse line feeds");
|
||||
}
|
||||
*lno += offset;
|
||||
}
|
||||
|
||||
#define NALLOC 64
|
||||
|
||||
static LINE *line_freelist;
|
||||
|
Loading…
Reference in New Issue
Block a user