col: import from plan 9, by popular demand

TBR=rsc
https://codereview.appspot.com/158240043
This commit is contained in:
Russ Cox 2014-10-20 20:41:41 -04:00
parent fd3496d3ef
commit 72197f89d4
2 changed files with 352 additions and 0 deletions

57
man/man1/col.1 Normal file
View file

@ -0,0 +1,57 @@
.TH COL 1
.SH NAME
col \- column alignment
.SH SYNOPSIS
.B col
[
.B -bfx
]
.SH DESCRIPTION
.I Col
overlays lines to expunge reverse line feeds
(ESC-7)
and half line feeds (ESC-9 and ESC-8)
as produced by
.I nroff
for .2C in
.IR ms (6)
or
.IR man (6)
and for
.IR tbl (1).
.I Col
is a pure filter.
It normally emits only full line feeds;
option
.B -f
(fine) allows half line feeds too.
Option
.B -b
removes backspaces, printing just one of each pile of overstruck
characters.
.I Col
normally converts white space to tabs;
option
.B -x
overrides this feature.
Other escaped characters and non-printing characters are ignored.
.SH EXAMPLES
.TP
.L
tbl file | nroff -ms | col | p
Format some tables for printing on typewriters;
use
.I col
to remove reverse line feeds, and
paginate the output.
.SH SOURCE
.B \*9/src/cmd/col.c
.SH SEE ALSO
.IR pr (1)
.SH BUGS
.I Col
can't back up more than 128 lines or
handle more than 800 characters per line,
and understands
.L VT
(013) as reverse line feed.

295
src/cmd/col.c Normal file
View file

@ -0,0 +1,295 @@
/* col - eliminate reverse line feeds */
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
enum {
ESC = '\033',
RLF = '\013',
PL = 256,
LINELN = 800,
Tabstop = 8, /* must be power of 2 */
};
static int bflag, xflag, fflag;
static int cp, lp;
static int half;
static int ll, llh, mustwr;
static int pcp = 0;
static char *page[PL];
static char *line;
static char lbuff[LINELN];
static Biobuf bin, bout;
void emit(char *s, int lineno);
void incr(void), decr(void);
void outc(Rune);
static void
usage(void)
{
fprint(2, "usage: %s [-bfx]\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
int i, lno;
long ch;
Rune c;
ARGBEGIN{
case 'b':
bflag++;
break;
case 'f':
fflag++;
break;
case 'x':
xflag++;
break;
default:
usage();
}ARGEND;
for (ll=0; ll < PL; ll++)
page[ll] = nil;
cp = 0;
ll = 0;
mustwr = PL;
line = lbuff;
Binit(&bin, 0, OREAD);
Binit(&bout, 1, OWRITE);
while ((ch = Bgetrune(&bin)) != Beof) {
c = ch;
switch (c) {
case '\n':
incr();
incr();
cp = 0;
break;
case '\0':
break;
case ESC:
c = Bgetrune(&bin);
switch (c) {
case '7': /* reverse full line feed */
decr();
decr();
break;
case '8': /* reverse half line feed */
if (fflag)
decr();
else
if (--half < -1) {
decr();
decr();
half += 2;
}
break;
case '9': /* forward half line feed */
if (fflag)
incr();
else
if (++half > 0) {
incr();
incr();
half -= 2;
}
break;
}
break;
case RLF:
decr();
decr();
break;
case '\r':
cp = 0;
break;
case '\t':
cp = (cp + Tabstop) & -Tabstop;
break;
case '\b':
if (cp > 0)
cp--;
break;
case ' ':
cp++;
break;
default:
if (!isascii(c) || isprint(c)) {
outc(c);
cp++;
}
break;
}
}
for (i=0; i < PL; i++) {
lno = (mustwr+i) % PL;
if (page[lno] != 0)
emit(page[lno], mustwr+i-PL);
}
emit(" ", (llh + 1) & -2);
exits(0);
}
void
outc(Rune c)
{
if (lp > cp) {
line = lbuff;
lp = 0;
}
while (lp < cp) {
switch (*line) {
case '\0':
*line = ' ';
lp++;
break;
case '\b':
lp--;
break;
default:
lp++;
break;
}
line++;
}
while (*line == '\b')
line += 2;
if (bflag || *line == '\0' || *line == ' ')
cp += runetochar(line, &c) - 1;
else {
char c1, c2, c3;
c1 = *++line;
*line++ = '\b';
c2 = *line;
*line++ = c;
while (c1) {
c3 = *line;
*line++ = c1;
c1 = c2;
c2 = c3;
}
lp = 0;
line = lbuff;
}
}
void
store(int lno)
{
lno %= PL;
if (page[lno] != nil)
free(page[lno]);
page[lno] = malloc((unsigned)strlen(lbuff) + 2);
if (page[lno] == nil)
sysfatal("out of memory");
strcpy(page[lno], lbuff);
}
void
fetch(int lno)
{
char *p;
lno %= PL;
p = lbuff;
while (*p)
*p++ = '\0';
line = lbuff;
lp = 0;
if (page[lno])
strcpy(line, page[lno]);
}
void
emit(char *s, int lineno)
{
int ncp;
char *p;
static int cline = 0;
if (*s) {
while (cline < lineno - 1) {
Bputc(&bout, '\n');
pcp = 0;
cline += 2;
}
if (cline != lineno) {
Bputc(&bout, ESC);
Bputc(&bout, '9');
cline++;
}
if (pcp)
Bputc(&bout, '\r');
pcp = 0;
p = s;
while (*p) {
ncp = pcp;
while (*p++ == ' ')
if ((++ncp & 7) == 0 && !xflag) {
pcp = ncp;
Bputc(&bout, '\t');
}
if (!*--p)
break;
while (pcp < ncp) {
Bputc(&bout, ' ');
pcp++;
}
Bputc(&bout, *p);
if (*p++ == '\b')
pcp--;
else
pcp++;
}
}
}
void
incr(void)
{
int lno;
store(ll++);
if (ll > llh)
llh = ll;
lno = ll % PL;
if (ll >= mustwr && page[lno]) {
emit(page[lno], ll - PL);
mustwr++;
free(page[lno]);
page[lno] = nil;
}
fetch(ll);
}
void
decr(void)
{
if (ll > mustwr - PL) {
store(ll--);
fetch(ll);
}
}