Add col command

col is used to display troff documents in ttys, removing the reverse
line feeds generated by .2C in ms. This implementation keeps the limit
of 256 lines of 800 characteres of the original implementation.
This commit is contained in:
Roberto E. Vargas Caballero 2015-03-03 10:52:34 +01:00 committed by sin
parent c8f2b068f6
commit 443de0a859
4 changed files with 319 additions and 0 deletions

View File

@ -82,6 +82,7 @@ BIN =\
cksum\ cksum\
cmp\ cmp\
cols\ cols\
col\
comm\ comm\
cp\ cp\
cron\ cron\

1
README
View File

@ -19,6 +19,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support,
=* cksum yes none =* cksum yes none
=* cmp yes none =* cmp yes none
#* cols non-posix none #* cols non-posix none
col yes none
=* comm yes none =* comm yes none
=*| cp yes none (-i) =*| cp yes none (-i)
=* cron non-posix none =* cron non-posix none

90
col.1 Normal file
View File

@ -0,0 +1,90 @@
.TH COL 1 col
.SH NAME
col - filter reverse line-feeds
.SH SYPNOSIS
.B col
.RB [ \-p ]
.RB [ \-b ]
.RB [ \-f ]
.RB [ \-x ]
.RB [ \-l
.IR num ]
.SH DESCRIPTION
.PP
The
.B col
utility filter all the reverse (and half reverse) line feeds,
as they are produced by nroff with .2C of ms(6) or by tbl(1).
.B col
also replaces spaces by tabs when it is possible.
The control sequences managed by
.B col
are:
.TP
.B ESC-7
reverse line-feed
.TP
.B ESC-8
reverse half-line-feed
.TP
.B ESC-9
forward half-line-feed
.TP
.B VT
vertical-tab
.TP
.B SP
Space
.TP
.B TAB
horizontal tab
.TP
.B RETURN
Carriage return
.TP
.B NL
New line
.PP
All the other control codes and escape sequences are removed.
.B Col
transform all the spaces into tabulators.
.SH OPTIONS
.TP
.B \-p
Print unknown escape sequences to the output.
.TP
.B \-b
Do not print backspaces in output,
and Print only the last overstriked character in the output.
.TP
.B \-f
Allow forward half line feeds in the output.
.TP
.B \-x
Do not convert spaces in tabulators.
.TP
.BI \-l " num"
Increment to
.I num
the number of lines buffered for
.B col.
.SH BUGS
.PP
.B Col
only process text with a maximum of 256 lines with 800 characteres per line,
although the number of lines can be modified with
.B \-l
option.
When the number of lines is bigger,
the buffer is flushed to the output,
so new reverse line feeds can not operate in the flushed lines.
This implementation ignores SI and SO selection character sets,
because it is supposed it will work only with UTF-8 strings,
although the UTF-8 support is missed.
.SH SEE ALSO
.BR nroff (1),
.BR ms (6),
.BR tbl(1).

227
col.c Normal file
View File

@ -0,0 +1,227 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "util.h"
#define NLINES 256
#define NCOLS 800
char **buff;
int obackspace, onotabs, ohalfline, oescape;
unsigned nline, ncol, nchar, nspaces, maxline, bs;
size_t pagsize = NLINES;
static void
usage(void)
{
enprintf(2, "usage: %s [-p][-l num][-b][-f][-x]\n", argv0);
}
static void
flush(void)
{
int c;
unsigned i, j;
for (i = 0; i < maxline; ++i) {
for (j = 0; j < NCOLS && (c = buff[i][j]) != '\0'; ++j)
putchar(c);
putchar('\n');
}
bs = nchar = nline = ncol = 0;
}
static void
forward(unsigned n)
{
unsigned lim;
for (lim = ncol + n; ncol != lim && nchar < NCOLS-1; ++nchar) {
switch (buff[nline][nchar]) {
case '\b':
--ncol;
break;
case '\0':
buff[nline][nchar] = ' ';
/* FALLTHROUGH */
default:
++ncol;
break;
}
}
}
static void
linefeed(int up, int rcarriage)
{
unsigned oncol = ncol;
nspaces = 0;
if (up > 0) {
if (nline == pagsize-1) {
flush();
} else {
if (++nline > maxline)
maxline = nline;
}
} else {
if (nline > 0)
--nline;
}
bs = 0;
if (rcarriage) {
forward(oncol);
nchar = ncol = 0;
}
}
static void
newchar(int c)
{
char *cp;
forward(nspaces);
nspaces = 0;
switch (c) {
case ' ':
forward(1);
break;
case '\r':
nchar = ncol = 0;
break;
case '\t':
forward(8 - ncol%8);
break;
case '\b':
if (ncol > 0)
--ncol;
if (nchar > 0)
--nchar;
bs = 1;
break;
default:
cp = &buff[nline][nchar];
if (*cp != '\0' && *cp != ' ' && bs && !obackspace) {
if (nchar != NCOLS-3) {
memmove(cp + 3, cp + 1, NCOLS - nchar - 2);
cp[1] = '\b';
nchar += 2;
}
}
if (nchar != NCOLS-1) {
for (cp = buff[nline]; cp < &buff[nline][nchar]; ++cp) {
if (*cp == '\0')
*cp = ' ';
}
buff[nline][nchar++] = c;
++ncol;
}
bs = 0;
}
}
static void
col(void)
{
int c;
while ((c = getchar()) != EOF) {
switch (c) {
case '\x1b':
switch (c = getchar()) {
case '8': /* reverse half-line-feed */
case '7': /* reverse line-feed */
linefeed(-1, 0);
continue;
case '9': /* forward half-line-feed */
if (ohalfline)
break;
linefeed(1, 0);
continue;
}
if (!oescape)
continue;
newchar('\x1b');
if (c != EOF)
newchar(c);
break;
case '\v':
linefeed(-1, 0);
break;
case ' ':
if (!onotabs) {
if (++nspaces != 8)
continue;
c = '\t';
nspaces = 0;
}
/* FALLTHROUGH */
case '\r':
case '\b':
case '\t':
newchar(c);
break;
case '\n':
linefeed(1, 1);
break;
default:
if (!iscntrl(c))
newchar(c);
break;
}
}
}
static void
allocbuf(void)
{
char **bp;
buff = emalloc(sizeof(*buff) * pagsize);
for (bp = buff; bp < &buff[pagsize]; ++bp)
*bp = emalloc(NCOLS);
}
int
main(int argc, char *argv[])
{
ARGBEGIN {
case 'b':
obackspace = 1;
break;
case 'f':
ohalfline = 1;
break;
case 'l':
pagsize = estrtonum(EARGF(usage()), 0, SIZE_MAX);
break;
case 'p':
oescape = 1;
break;
case 'x':
onotabs = 1;
break;
default:
usage();
} ARGEND;
if (argc > 0)
usage();
allocbuf();
col();
flush();
if (ferror(stdin))
enprintf(1, "error reading input");
if (ferror(stdout))
enprintf(2, "error writing output");
return 0;
}