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:
bapt 2015-05-15 08:45:55 +00:00
parent 05da681425
commit cafa3c95e7
2 changed files with 128 additions and 62 deletions

View File

@ -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

View File

@ -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;