commit 72197f89d47444e3c62e1aa0df8bd7059c29104d from: Russ Cox date: Tue Oct 21 00:41:41 2014 UTC col: import from plan 9, by popular demand TBR=rsc https://codereview.appspot.com/158240043 commit - fd3496d3ef1b3f27c670c25278cbb0304f74a706 commit + 72197f89d47444e3c62e1aa0df8bd7059c29104d blob - /dev/null blob + 0d9513cf4a298c98ee61034d7564edacc94a88fa (mode 644) --- /dev/null +++ man/man1/col.1 @@ -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. blob - /dev/null blob + 5897e17c3868ce3b67d8f2949040d31387b08904 (mode 644) --- /dev/null +++ src/cmd/col.c @@ -0,0 +1,295 @@ +/* col - eliminate reverse line feeds */ +#include +#include +#include +#include + +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); + } +}