blob: 1b758efec074c7165289f7cf5f9ccc153c110978 [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <bio.h>
/*
* Deroff command -- strip troff, eqn, and tbl sequences from
* a file. Has three flags argument, -w, to cause output one word per line
* rather than in the original format.
* -mm (or -ms) causes the corresponding macro's to be interpreted
* so that just sentences are output
* -ml also gets rid of lists.
* -i causes deroff to ignore .so and .nx commands.
* Deroff follows .so and .nx commands, removes contents of macro
* definitions, equations (both .EQ ... .EN and $...$),
* Tbl command sequences, and Troff backslash vconstructions.
*
* All input is through the C macro; the most recently read character is in c.
*/
/*
#define C ((c = Bgetrune(infile)) < 0?\
eof():\
((c == ldelim) && (filesp == files)?\
skeqn():\
(c == '\n'?\
(linect++,c):\
c)))
#define C1 ((c = Bgetrune(infile)) == Beof?\
eof():\
(c == '\n'?\
(linect++,c):\
c))
*/
/* lose those macros! */
#define C fC()
#define C1 fC1()
#define SKIP while(C != '\n')
#define SKIP1 while(C1 != '\n')
#define SKIP_TO_COM SKIP;\
SKIP;\
pc=c;\
while(C != '.' || pc != '\n' || C > 'Z')\
pc=c
#define YES 1
#define NO 0
#define MS 0
#define MM 1
#define ONE 1
#define TWO 2
#define NOCHAR -2
#define EXTENDED -1 /* All runes above 0x7F */
#define SPECIAL 0
#define APOS 1
#define PUNCT 2
#define DIGIT 3
#define LETTER 4
int linect = 0;
int wordflag= NO;
int underscoreflag = NO;
int msflag = NO;
int iflag = NO;
int mac = MM;
int disp = 0;
int inmacro = NO;
int intable = NO;
int eqnflag = 0;
#define MAX_ASCII 0X80
char chars[MAX_ASCII]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
Rune line[30000];
Rune* lp;
long c;
long pc;
int ldelim = NOCHAR;
int rdelim = NOCHAR;
char** argv;
char fname[50];
Biobuf* files[15];
Biobuf**filesp;
Biobuf* infile;
char* devnull = "/dev/null";
Biobuf *infile;
Biobuf bout;
long skeqn(void);
Biobuf* opn(char *p);
int eof(void);
int charclass(int);
void getfname(void);
void fatal(char *s, char *p);
void usage(void);
void work(void);
void putmac(Rune *rp, int vconst);
void regline(int macline, int vconst);
void putwords(void);
void comline(void);
void macro(void);
void eqn(void);
void tbl(void);
void stbl(void);
void sdis(char a1, char a2);
void sce(void);
void backsl(void);
char* copys(char *s);
void refer(int c1);
void inpic(void);
int
fC(void)
{
c = Bgetrune(infile);
if(c < 0)
return eof();
if(c == ldelim && filesp == files)
return skeqn();
if(c == '\n')
linect++;
return c;
}
int
fC1(void)
{
c = Bgetrune(infile);
if(c == Beof)
return eof();
if(c == '\n')
linect++;
return c;
}
void
main(int argc, char *av[])
{
int i;
char *f;
argv = av;
Binit(&bout, 1, OWRITE);
ARGBEGIN{
case 'w':
wordflag = YES;
break;
case '_':
wordflag = YES;
underscoreflag = YES;
break;
case 'm':
msflag = YES;
if(f = ARGF())
switch(*f)
{
case 'm': mac = MM; break;
case 's': mac = MS; break;
case 'l': disp = 1; break;
default: usage();
}
else
usage();
break;
case 'i':
iflag = YES;
break;
default:
usage();
}ARGEND
if(*argv)
infile = opn(*argv++);
else{
infile = malloc(sizeof(Biobuf));
Binit(infile, 0, OREAD);
}
files[0] = infile;
filesp = &files[0];
for(i='a'; i<='z' ; ++i)
chars[i] = LETTER;
for(i='A'; i<='Z'; ++i)
chars[i] = LETTER;
for(i='0'; i<='9'; ++i)
chars[i] = DIGIT;
chars['\''] = APOS;
chars['&'] = APOS;
chars['\b'] = APOS;
chars['.'] = PUNCT;
chars[','] = PUNCT;
chars[';'] = PUNCT;
chars['?'] = PUNCT;
chars[':'] = PUNCT;
work();
}
long
skeqn(void)
{
while(C1 != rdelim)
if(c == '\\')
c = C1;
else if(c == '"')
while(C1 != '"')
if(c == '\\')
C1;
if (msflag)
eqnflag = 1;
return(c = ' ');
}
Biobuf*
opn(char *p)
{
Biobuf *fd;
while ((fd = Bopen(p, OREAD)) == 0) {
if(msflag || p == devnull)
fatal("Cannot open file %s - quitting\n", p);
else {
fprint(2, "Deroff: Cannot open file %s - continuing\n", p);
p = devnull;
}
}
linect = 0;
return(fd);
}
int
eof(void)
{
if(Bfildes(infile) != 0)
Bterm(infile);
if(filesp > files)
infile = *--filesp;
else
if(*argv)
infile = opn(*argv++);
else
exits(0);
return(C);
}
void
getfname(void)
{
char *p;
Rune r;
Dir *dir;
struct chain
{
struct chain* nextp;
char* datap;
} *q;
static struct chain *namechain= 0;
while(C == ' ')
;
for(p = fname; (r=c) != '\n' && r != ' ' && r != '\t' && r != '\\'; C)
p += runetochar(p, &r);
*p = '\0';
while(c != '\n')
C;
if(!strcmp(fname, "/sys/lib/tmac/tmac.cs")
|| !strcmp(fname, "/sys/lib/tmac/tmac.s")) {
fname[0] = '\0';
return;
}
dir = dirstat(fname);
if(dir!=nil && ((dir->mode & DMDIR) || dir->type != 'M')) {
free(dir);
fname[0] = '\0';
return;
}
free(dir);
/*
* see if this name has already been used
*/
for(q = namechain; q; q = q->nextp)
if( !strcmp(fname, q->datap)) {
fname[0] = '\0';
return;
}
q = (struct chain*)malloc(sizeof(struct chain));
q->nextp = namechain;
q->datap = copys(fname);
namechain = q;
}
void
usage(void)
{
fprint(2,"usage: deroff [-nw_pi] [-m (m s l)] [file ...] \n");
exits("usage");
}
void
fatal(char *s, char *p)
{
fprint(2, "deroff: ");
fprint(2, s, p);
exits(s);
}
void
work(void)
{
for(;;) {
eqnflag = 0;
if(C == '.' || c == '\'')
comline();
else
regline(NO, TWO);
}
}
void
regline(int macline, int vconst)
{
line[0] = c;
lp = line;
for(;;) {
if(c == '\\') {
*lp = ' ';
backsl();
if(c == '%') /* no blank for hyphenation char */
lp--;
}
if(c == '\n')
break;
if(intable && c=='T') {
*++lp = C;
if(c=='{' || c=='}') {
lp[-1] = ' ';
*lp = C;
}
} else {
if(msflag == 1 && eqnflag == 1) {
eqnflag = 0;
*++lp = 'x';
}
*++lp = C;
}
}
*lp = '\0';
if(lp != line) {
if(wordflag)
putwords();
else
if(macline)
putmac(line,vconst);
else
Bprint(&bout, "%S\n", line);
}
}
void
putmac(Rune *rp, int vconst)
{
Rune *t;
int found;
Rune last;
found = 0;
last = 0;
while(*rp) {
while(*rp == ' ' || *rp == '\t')
Bputrune(&bout, *rp++);
for(t = rp; *t != ' ' && *t != '\t' && *t != '\0'; t++)
;
if(*rp == '\"')
rp++;
if(t > rp+vconst && charclass(*rp) == LETTER
&& charclass(rp[1]) == LETTER) {
while(rp < t)
if(*rp == '\"')
rp++;
else
Bputrune(&bout, *rp++);
last = t[-1];
found++;
} else
if(found && charclass(*rp) == PUNCT && rp[1] == '\0')
Bputrune(&bout, *rp++);
else {
last = t[-1];
rp = t;
}
}
Bputc(&bout, '\n');
if(msflag && charclass(last) == PUNCT)
Bprint(&bout, " %C\n", last);
}
/*
* break into words for -w option
*/
void
putwords(void)
{
Rune *p, *p1;
int i, nlet;
for(p1 = line;;) {
/*
* skip initial specials ampersands and apostrophes
*/
while((i = charclass(*p1)) != EXTENDED && i < DIGIT)
if(*p1++ == '\0')
return;
nlet = 0;
for(p = p1; (i = charclass(*p)) != SPECIAL || (underscoreflag && *p=='_'); p++)
if(i == LETTER || (underscoreflag && *p == '_'))
nlet++;
/*
* MDM definition of word
*/
if(nlet > 1) {
/*
* delete trailing ampersands and apostrophes
*/
while(*--p == '\'' || *p == '&'
|| charclass(*p) == PUNCT)
;
while(p1 <= p)
Bputrune(&bout, *p1++);
Bputc(&bout, '\n');
} else
p1 = p;
}
}
void
comline(void)
{
long c1, c2;
while(C==' ' || c=='\t')
;
comx:
if((c1=c) == '\n')
return;
c2 = C;
if(c1=='.' && c2!='.')
inmacro = NO;
if(msflag && c1 == '['){
refer(c2);
return;
}
if(c2 == '\n')
return;
if(c1 == '\\' && c2 == '\"')
SKIP;
else
if (filesp==files && c1=='E' && c2=='Q')
eqn();
else
if(filesp==files && c1=='T' && (c2=='S' || c2=='C' || c2=='&')) {
if(msflag)
stbl();
else
tbl();
}
else
if(c1=='T' && c2=='E')
intable = NO;
else if (!inmacro &&
((c1 == 'd' && c2 == 'e') ||
(c1 == 'i' && c2 == 'g') ||
(c1 == 'a' && c2 == 'm')))
macro();
else
if(c1=='s' && c2=='o') {
if(iflag)
SKIP;
else {
getfname();
if(fname[0]) {
if(infile = opn(fname))
*++filesp = infile;
else infile = *filesp;
}
}
}
else
if(c1=='n' && c2=='x')
if(iflag)
SKIP;
else {
getfname();
if(fname[0] == '\0')
exits(0);
if(Bfildes(infile) != 0)
Bterm(infile);
infile = *filesp = opn(fname);
}
else
if(c1 == 't' && c2 == 'm')
SKIP;
else
if(c1=='h' && c2=='w')
SKIP;
else
if(msflag && c1 == 'T' && c2 == 'L') {
SKIP_TO_COM;
goto comx;
}
else
if(msflag && c1=='N' && c2 == 'R')
SKIP;
else
if(msflag && c1 == 'A' && (c2 == 'U' || c2 == 'I')){
if(mac==MM)SKIP;
else {
SKIP_TO_COM;
goto comx;
}
} else
if(msflag && c1=='F' && c2=='S') {
SKIP_TO_COM;
goto comx;
}
else
if(msflag && (c1=='S' || c1=='N') && c2=='H') {
SKIP_TO_COM;
goto comx;
} else
if(c1 == 'U' && c2 == 'X') {
if(wordflag)
Bprint(&bout, "UNIX\n");
else
Bprint(&bout, "UNIX ");
} else
if(msflag && c1=='O' && c2=='K') {
SKIP_TO_COM;
goto comx;
} else
if(msflag && c1=='N' && c2=='D')
SKIP;
else
if(msflag && mac==MM && c1=='H' && (c2==' '||c2=='U'))
SKIP;
else
if(msflag && mac==MM && c2=='L') {
if(disp || c1=='R')
sdis('L', 'E');
else {
SKIP;
Bprint(&bout, " .");
}
} else
if(!msflag && c1=='P' && c2=='S') {
inpic();
} else
if(msflag && (c1=='D' || c1=='N' || c1=='K'|| c1=='P') && c2=='S') {
sdis(c1, 'E');
} else
if(msflag && (c1 == 'K' && c2 == 'F')) {
sdis(c1,'E');
} else
if(msflag && c1=='n' && c2=='f')
sdis('f','i');
else
if(msflag && c1=='c' && c2=='e')
sce();
else {
if(c1=='.' && c2=='.') {
if(msflag) {
SKIP;
return;
}
while(C == '.')
;
}
inmacro++;
if(c1 <= 'Z' && msflag)
regline(YES,ONE);
else {
if(wordflag)
C;
regline(YES,TWO);
}
inmacro--;
}
}
void
macro(void)
{
if(msflag) {
do {
SKIP1;
} while(C1 != '.' || C1 != '.' || C1 == '.');
if(c != '\n')
SKIP;
return;
}
SKIP;
inmacro = YES;
}
void
sdis(char a1, char a2)
{
int c1, c2;
int eqnf;
int lct;
if(a1 == 'P'){
while(C1 == ' ')
;
if(c == '<') {
SKIP1;
return;
}
}
lct = 0;
eqnf = 1;
if(c != '\n')
SKIP1;
for(;;) {
while(C1 != '.')
if(c == '\n')
continue;
else
SKIP1;
if((c1=C1) == '\n')
continue;
if((c2=C1) == '\n') {
if(a1 == 'f' && (c1 == 'P' || c1 == 'H'))
return;
continue;
}
if(c1==a1 && c2 == a2) {
SKIP1;
if(lct != 0){
lct--;
continue;
}
if(eqnf)
Bprint(&bout, " .");
Bputc(&bout, '\n');
return;
} else
if(a1 == 'L' && c2 == 'L') {
lct++;
SKIP1;
} else
if(a1 == 'D' && c1 == 'E' && c2 == 'Q') {
eqn();
eqnf = 0;
} else
if(a1 == 'f') {
if((mac == MS && c2 == 'P') ||
(mac == MM && c1 == 'H' && c2 == 'U')){
SKIP1;
return;
}
SKIP1;
}
else
SKIP1;
}
}
void
tbl(void)
{
while(C != '.')
;
SKIP;
intable = YES;
}
void
stbl(void)
{
while(C != '.')
;
SKIP_TO_COM;
if(c != 'T' || C != 'E') {
SKIP;
pc = c;
while(C != '.' || pc != '\n' || C != 'T' || C != 'E')
pc = c;
}
}
void
eqn(void)
{
long c1, c2;
int dflg;
char last;
last = 0;
dflg = 1;
SKIP;
for(;;) {
if(C1 == '.' || c == '\'') {
while(C1==' ' || c=='\t')
;
if(c=='E' && C1=='N') {
SKIP;
if(msflag && dflg) {
Bputc(&bout, 'x');
Bputc(&bout, ' ');
if(last) {
Bputc(&bout, last);
Bputc(&bout, '\n');
}
}
return;
}
} else
if(c == 'd') {
if(C1=='e' && C1=='l')
if(C1=='i' && C1=='m') {
while(C1 == ' ')
;
if((c1=c)=='\n' || (c2=C1)=='\n' ||
(c1=='o' && c2=='f' && C1=='f')) {
ldelim = NOCHAR;
rdelim = NOCHAR;
} else {
ldelim = c1;
rdelim = c2;
}
}
dflg = 0;
}
if(c != '\n')
while(C1 != '\n') {
if(chars[c] == PUNCT)
last = c;
else
if(c != ' ')
last = 0;
}
}
}
/*
* skip over a complete backslash vconstruction
*/
void
backsl(void)
{
int bdelim;
sw:
switch(C1)
{
case '"':
SKIP1;
return;
case 's':
if(C1 == '\\')
backsl();
else {
while(C1>='0' && c<='9')
;
Bungetrune(infile);
c = '0';
}
lp--;
return;
case 'f':
case 'n':
case '*':
if(C1 != '(')
return;
case '(':
if(msflag) {
if(C == 'e') {
if(C1 == 'm') {
*lp = '-';
return;
}
} else
if(c != '\n')
C1;
return;
}
if(C1 != '\n')
C1;
return;
case '$':
C1; /* discard argument number */
return;
case 'b':
case 'x':
case 'v':
case 'h':
case 'w':
case 'o':
case 'l':
case 'L':
if((bdelim=C1) == '\n')
return;
while(C1!='\n' && c!=bdelim)
if(c == '\\')
backsl();
return;
case '\\':
if(inmacro)
goto sw;
default:
return;
}
}
char*
copys(char *s)
{
char *t, *t0;
if((t0 = t = malloc((strlen(s)+1))) == 0)
fatal("Cannot allocate memory", (char*)0);
while(*t++ = *s++)
;
return(t0);
}
void
sce(void)
{
int n = 1;
while (C != '\n' && !('0' <= c && c <= '9'))
;
if (c != '\n') {
for (n = c-'0';'0' <= C && c <= '9';)
n = n*10 + c-'0';
}
while(n) {
if(C == '.') {
if(C == 'c') {
if(C == 'e') {
while(C == ' ')
;
if(c == '0') {
SKIP;
break;
} else
SKIP;
} else
SKIP;
} else
if(c == 'P' || C == 'P') {
if(c != '\n')
SKIP;
break;
} else
if(c != '\n')
SKIP;
} else {
SKIP;
n--;
}
}
}
void
refer(int c1)
{
int c2;
if(c1 != '\n')
SKIP;
c2 = 0;
for(;;) {
if(C != '.')
SKIP;
else {
if(C != ']')
SKIP;
else {
while(C != '\n')
c2 = c;
if(charclass(c2) == PUNCT)
Bprint(&bout, " %C",c2);
return;
}
}
}
}
void
inpic(void)
{
int c1;
Rune *p1;
/* SKIP1;*/
while(C1 != '\n')
if(c == '<'){
SKIP1;
return;
}
p1 = line;
c = '\n';
for(;;) {
c1 = c;
if(C1 == '.' && c1 == '\n') {
if(C1 != 'P' || C1 != 'E') {
if(c != '\n'){
SKIP1;
c = '\n';
}
continue;
}
SKIP1;
return;
} else
if(c == '\"') {
while(C1 != '\"') {
if(c == '\\') {
if(C1 == '\"')
continue;
Bungetrune(infile);
backsl();
} else
*p1++ = c;
}
*p1++ = ' ';
} else
if(c == '\n' && p1 != line) {
*p1 = '\0';
if(wordflag)
putwords();
else
Bprint(&bout, "%S\n\n", line);
p1 = line;
}
}
}
int
charclass(int c)
{
if(c < MAX_ASCII)
return chars[c];
switch(c){
case 0x2013: case 0x2014: /* en dash, em dash */
return SPECIAL;
}
return EXTENDED;
}