char *xxxvers = "\nDeroff Version 1.02 24 July 1978\n"; #include /* Deroff command -- strip troff, eqn, and Tbl sequences from a file. Has one flag argument, -w, to cause output one word per line rather than in the original format. Deroff follows .so and .nx commands, removes contents of macro definitions, equations (both .EQ ... .EN and $...$), Tbl command sequences, and Troff backslash constructions. All input is through the C macro; the most recently read character is in c. */ #define C ( (c=getc(infile)) == EOF ? eof() : ((c==ldelim)&&(filesp==files) ? skeqn() : c) ) #define C1 ( (c=getc(infile)) == EOF ? eof() : c) #define SKIP while(C != '\n') #define YES 1 #define NO 0 #define NOCHAR -2 #define SPECIAL 0 #define APOS 1 #define DIGIT 2 #define LETTER 3 int wordflag = NO; int inmacro = NO; int intable = NO; char chars[128]; /* SPECIAL, APOS, DIGIT, or LETTER */ char line[512]; char *lp; int c; int ldelim = NOCHAR; int rdelim = NOCHAR; int argc; char **argv; char fname[50]; FILE *files[15]; FILE **filesp; FILE *infile; char *calloc(); main(ac, av) int ac; char **av; { register int i; register char *p; static char onechar[2] = "X"; FILE *opn(); argc = ac - 1; argv = av + 1; while(argc>0 && argv[0][0]=='-' && argv[0][1]!='\0') { for(p=argv[0]+1; *p; ++p) switch(*p) { case 'w': wordflag = YES; break; default: onechar[0] = *p; fatal("Invalid flag %s\n", onechar); } --argc; ++argv; } if(argc == 0) infile = stdin; else { infile = opn(argv[0]); --argc; ++argv; } 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; work(); } skeqn() { while((c = getc(infile)) != rdelim) if(c == EOF) c = eof(); else if(c == '"') while( (c = getc(infile)) != '"') if(c == EOF) c = eof(); else if(c == '\\') if((c = getc(infile)) == EOF) c = eof(); return(c = ' '); } FILE *opn(p) register char *p; { FILE *fd; if(p[0]=='-' && p[1]=='\0') fd = stdin; else if( (fd = fopen(p, "r")) == NULL) fatal("Cannot open file %s\n", p); return(fd); } eof() { if(infile != stdin) fclose(infile); if(filesp > files) infile = *--filesp; else if(argc > 0) { infile = opn(argv[0]); --argc; ++argv; } else exit(0); return(C); } getfname() { register char *p; struct chain { struct chain *nextp; char *datap; } *chainblock; register struct chain *q; static struct chain *namechain = NULL; char *copys(); while(C == ' ') ; for(p = fname ; (*p=c)!= '\n' && c!=' ' && c!='\t' && c!='\\' ; ++p) C; *p = '\0'; while(c != '\n') C; /* 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 *) calloc(1, sizeof(*chainblock)); q->nextp = namechain; q->datap = copys(fname); namechain = q; } fatal(s,p) char *s, *p; { fprintf(stderr, "Deroff: "); fprintf(stderr, s, p); exit(1); } work() { for( ;; ) { if(C == '.' || c == '\'') comline(); else regline(NO); } } regline(macline) int macline; { line[0] = c; lp = line; for( ; ; ) { if(c == '\\') { *lp = ' '; backsl(); } if(c == '\n') break; if(intable && c=='T') { *++lp = C; if(c=='{' || c=='}') { lp[-1] = ' '; *lp = C; } } else *++lp = C; } *lp = '\0'; if(line[0] != '\0') if(wordflag) putwords(macline); else if(macline) putmac(line); else puts(line); } putmac(s) register char *s; { register char *t; while(*s) { while(*s==' ' || *s=='\t') putchar(*s++); for(t = s ; *t!=' ' && *t!='\t' && *t!='\0' ; ++t) ; if(t>s+2 && chars[ s[0] ]==LETTER && chars[ s[1] ]==LETTER) while(s < t) putchar(*s++); else s = t; } putchar('\n'); } putwords(macline) /* break into words for -w option */ int macline; { register char *p, *p1; int i, nlet; for(p1 = line ; ;) { /* skip initial specials ampersands and apostrophes */ while( chars[*p1] < DIGIT) if(*p1++ == '\0') return; nlet = 0; for(p = p1 ; (i=chars[*p]) != SPECIAL ; ++p) if(i == LETTER) ++nlet; if( (!macline && nlet>1) /* MDM definition of word */ || (macline && nlet>2 && chars[ p1[0] ]==LETTER && chars[ p1[1] ]==LETTER) ) { /* delete trailing ampersands and apostrophes */ while(p[-1]=='\'' || p[-1]=='&') --p; while(p1 < p) putchar(*p1++); putchar('\n'); } else p1 = p; } } comline() { register int c1, c2; while(C==' ' || c=='\t') ; if( (c1=c) == '\n') return; c2 = C; if(c1=='.' && c2!='.') inmacro = NO; if(c2 == '\n') return; if(c1=='E' && c2=='Q' && filesp==files) eqn(); else if(c1=='T' && (c2=='S' || c2=='C' || c2=='&') && filesp==files) tbl(); else if(c1=='T' && c2=='E') intable = NO; else if(!inmacro && c1=='d' && c2=='e') macro(); else if(!inmacro && c1=='i' && c2=='g') macro(); else if(!inmacro && c1=='a' && c2 == 'm') macro(); else if(c1=='s' && c2=='o') { getfname(); if( fname[0] ) infile = *++filesp = opn( fname ); } else if(c1=='n' && c2=='x') { getfname(); if(fname[0] == '\0') exit(0); if(infile != stdin) fclose(infile); infile = *filesp = opn(fname); } else if(c1=='h' && c2=='w') { SKIP; } else { if(c1=='.' && c2=='.') while(C == '.') ; ++inmacro; regline(YES); --inmacro; } } macro() { /* do { SKIP; } while(C!='.' || C!='.' || C=='.'); /* look for .. */ SKIP; inmacro = YES; } tbl() { while(C != '.'); SKIP; intable = YES; } eqn() { register int c1, c2; SKIP; for( ;;) { if(C == '.' || c == '\'') { while(C==' ' || c=='\t') ; if(c=='E' && C=='N') { SKIP; return; } } else if(c == 'd') /* look for delim */ { if(C=='e' && C=='l') if( C=='i' && C=='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; } } } if(c != '\n') SKIP; } } backsl() /* skip over a complete backslash construction */ { int bdelim; sw: switch(C) { case '"': SKIP; return; case 's': if(C == '\\') backsl(); else { while(C>='0' && c<='9') ; ungetc(c,infile); c = '0'; } --lp; return; case 'f': case 'n': case '*': if(C != '(') return; case '(': if(C != '\n') C; return; case '$': C; /* discard argument number */ return; case 'b': case 'x': case 'v': case 'h': case 'w': case 'o': case 'l': case 'L': if( (bdelim=C) == '\n') return; while(C!='\n' && c!=bdelim) if(c == '\\') backsl(); return; case '\\': if(inmacro) goto sw; default: return; } } char *copys(s) register char *s; { register char *t, *t0; if( (t0 = t = calloc( strlen(s)+1, sizeof(*t) ) ) == NULL) fatal("Cannot allocate memory", (char *) NULL); while( *t++ = *s++ ) ; return(t0); }