/* $OpenBSD: deroff.c,v 1.13 2015/10/09 01:37:07 deraadt Exp $ */ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (C) Caldera International Inc. 2001-2002. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code and documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed or owned by Caldera * International, Inc. * 4. Neither the name of Caldera International, Inc. nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include /* * Deroff command -- strip troff, eqn, and Tbl sequences from * a file. Has two 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. * 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 Cget macro; * the most recently read character is in c. * * Modified by Robert Henry to process -me and -man macros. */ #define Cget ( (c=getc(infile)) == EOF ? eof() : ((c==ldelim)&&(filesp==files) ? skeqn() : c) ) #define C1get ( (c=getc(infile)) == EOF ? eof() : c) #ifdef DEBUG # define C _C() # define C1 _C1() #else /* not DEBUG */ # define C Cget # define C1 C1get #endif /* not DEBUG */ #define SKIP while (C != '\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 /* -ms */ #define MM 1 /* -mm */ #define ME 2 /* -me */ #define MA 3 /* -man */ #ifdef DEBUG char *mactab[] = { "-ms", "-mm", "-me", "-ma" }; #endif /* DEBUG */ #define ONE 1 #define TWO 2 #define NOCHAR -2 #define SPECIAL 0 #define APOS 1 #define PUNCT 2 #define DIGIT 3 #define LETTER 4 #define MAXFILES 20 int iflag; int wordflag; int msflag; /* processing a source written using a mac package */ int mac; /* which package */ int disp; int parag; int inmacro; int intable; int keepblock; /* keep blocks of text; normally false when msflag */ char chars[128]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */ char line[LINE_MAX]; char *lp; int c; int pc; int ldelim; int rdelim; char fname[PATH_MAX]; FILE *files[MAXFILES]; FILE **filesp; FILE *infile; int argc; char **argv; /* * Macro processing * * Macro table definitions */ typedef int pacmac; /* compressed macro name */ int argconcat = 0; /* concat arguments together (-me only) */ #define tomac(c1, c2) ((((c1) & 0xFF) << 8) | ((c2) & 0xFF)) #define frommac(src, c1, c2) (((c1)=((src)>>8)&0xFF),((c2) =(src)&0xFF)) struct mactab{ int condition; pacmac macname; int (*func)(); /* XXX - args */ }; struct mactab troffmactab[]; struct mactab ppmactab[]; struct mactab msmactab[]; struct mactab mmmactab[]; struct mactab memactab[]; struct mactab manmactab[]; /* * Macro table initialization */ #define M(cond, c1, c2, func) {cond, tomac(c1, c2), func} /* * Flags for matching conditions other than * the macro name */ #define NONE 0 #define FNEST 1 /* no nested files */ #define NOMAC 2 /* no macro */ #define MAC 3 /* macro */ #define PARAG 4 /* in a paragraph */ #define MSF 5 /* msflag is on */ #define NBLK 6 /* set if no blocks to be kept */ /* * Return codes from macro minions, determine where to jump, * how to repeat/reprocess text */ #define COMX 1 /* goto comx */ #define COM 2 /* goto com */ int skeqn(void); int eof(void); int _C1(void); int _C(void); int EQ(void); int domacro(void); int PS(void); int skip(void); int intbl(void); int outtbl(void); int so(void); int nx(void); int skiptocom(void); int PP(pacmac); int AU(void); int SH(pacmac); int UX(void); int MMHU(pacmac); int mesnblock(pacmac); int mssnblock(pacmac); int nf(void); int ce(void); int meip(pacmac); int mepp(pacmac); int mesh(pacmac); int mefont(pacmac); int manfont(pacmac); int manpp(pacmac); int macsort(const void *, const void *); int sizetab(struct mactab *); void getfname(void); void textline(char *, int); void work(void); void regline(void (*)(char *, int), int); void macro(void); void tbl(void); void stbl(void); void eqn(void); void backsl(void); void sce(void); void refer(int); void inpic(void); void msputmac(char *, int); void msputwords(int); void meputmac(char *, int); void meputwords(int); void noblock(char, char); void defcomline(pacmac); void comline(void); void buildtab(struct mactab **, int *); FILE *opn(char *); struct mactab *macfill(struct mactab *, struct mactab *); __dead void usage(void); int main(int ac, char **av) { int i, ch; int errflg = 0; int kflag = NO; if (pledge("stdio rpath", NULL) == -1) err(1, "pledge"); iflag = NO; wordflag = NO; msflag = NO; mac = ME; disp = NO; parag = NO; inmacro = NO; intable = NO; ldelim = NOCHAR; rdelim = NOCHAR; keepblock = YES; while ((ch = getopt(ac, av, "ikpwm:")) != -1) { switch (ch) { case 'i': iflag = YES; break; case 'k': kflag = YES; break; case 'm': msflag = YES; keepblock = NO; switch (optarg[0]) { case 'm': mac = MM; break; case 's': mac = MS; break; case 'e': mac = ME; break; case 'a': mac = MA; break; case 'l': disp = YES; break; default: errflg = 1; break; } if (optarg[1] != '\0') errflg = 1; break; case 'p': parag = YES; break; case 'w': wordflag = YES; kflag = YES; break; default: errflg = 1; } } argc = ac - optind; argv = av + optind; if (kflag) keepblock = YES; if (errflg) usage(); #ifdef DEBUG printf("msflag = %d, mac = %s, keepblock = %d, disp = %d\n", msflag, mactab[mac], keepblock, disp); #endif /* DEBUG */ 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; chars['.'] = PUNCT; chars[','] = PUNCT; chars[';'] = PUNCT; chars['?'] = PUNCT; chars[':'] = PUNCT; work(); exit(0); } int skeqn(void) { while ((c = getc(infile)) != rdelim) { if (c == EOF) c = eof(); else if (c == '"') { while ((c = getc(infile)) != '"') { if (c == EOF || (c == '\\' && (c = getc(infile)) == EOF)) c = eof(); } } } if (msflag) return((c = 'x')); return((c = ' ')); } FILE * opn(char *p) { FILE *fd; if ((fd = fopen(p, "r")) == NULL) err(1, "fopen %s", p); return(fd); } int eof(void) { 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); } void getfname(void) { char *p; struct chain { struct chain *nextp; char *datap; } *q; static struct chain *namechain= NULL; while (C == ' ') ; /* nothing */ for (p = fname ; p - fname < sizeof(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) == 0) { fname[0] = '\0'; return; } q = malloc(sizeof(struct chain)); if (q == NULL) err(1, NULL); q->nextp = namechain; q->datap = strdup(fname); if (q->datap == NULL) err(1, NULL); namechain = q; } /*ARGSUSED*/ void textline(char *str, int constant) { if (wordflag) { msputwords(0); return; } puts(str); } void work(void) { for (;;) { C; #ifdef FULLDEBUG printf("Starting work with `%c'\n", c); #endif /* FULLDEBUG */ if (c == '.' || c == '\'') comline(); else regline(textline, TWO); } } void regline(void (*pfunc)(char *, int), int constant) { line[0] = c; lp = line; while (lp - line < sizeof(line)) { 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') (*pfunc)(line, constant); } void macro(void) { if (msflag) { do { SKIP; } while (C!='.' || C!='.' || C=='.'); /* look for .. */ if (c != '\n') SKIP; return; } SKIP; inmacro = YES; } void tbl(void) { while (C != '.') ; /* nothing */ SKIP; intable = YES; } void stbl(void) { while (C != '.') ; /* nothing */ SKIP_TO_COM; if (c != 'T' || C != 'E') { SKIP; pc = c; while (C != '.' || pc != '\n' || C != 'T' || C != 'E') pc = c; } } void eqn(void) { int 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) { putchar('x'); putchar(' '); if (last) { putchar(last); putchar('\n'); } } return; } } else if (c == 'd') { /* look for delim */ if (C1 == 'e' && C1 == 'l') if (C1 == 'i' && C1 == 'm') { while (C1 == ' ') ; /* nothing */ 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 construction */ void backsl(void) { int bdelim; sw: switch (C) { case '"': SKIP; return; case 's': if (C == '\\') backsl(); else { while (C >= '0' && c <= '9') ; /* nothing */ ungetc(c, infile); c = '0'; } --lp; return; case 'f': case 'n': case '*': if (C != '(') return; case '(': if (msflag) { if (C == 'e') { if (C == 'm') { *lp = '-'; return; } } else if (c != '\n') C; return; } 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; } } void sce(void) { char *ap; int n, i; char a[10]; for (ap = a; C != '\n'; ap++) { *ap = c; if (ap == &a[9]) { SKIP; ap = a; break; } } if (ap != a) n = atoi(a); else n = 1; for (i = 0; i < n;) { if (C == '.') { if (C == 'c') { if (C == 'e') { while (C == ' ') ; /* nothing */ 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; i++; } } } void refer(int c1) { int c2; if (c1 != '\n') SKIP; for (c2 = -1;;) { if (C != '.') SKIP; else { if (C != ']') SKIP; else { while (C != '\n') c2 = c; if (c2 != -1 && chars[c2] == PUNCT) putchar(c2); return; } } } } void inpic(void) { int c1; char *p1, *ep; SKIP; p1 = line; ep = line + sizeof(line) - 1; c = '\n'; for (;;) { c1 = c; if (C == '.' && c1 == '\n') { if (C != 'P') { if (c == '\n') continue; else { SKIP; c = '\n'; continue; } } if (C != 'E') { if (c == '\n') continue; else { SKIP; c = '\n'; continue; } } SKIP; return; } else if (c == '\"') { while (C != '\"') { if (c == '\\') { if (C == '\"') continue; ungetc(c, infile); backsl(); } else if (p1 + 1 >= ep) { errx(1, ".PS length exceeds limit"); } else { *p1++ = c; } } *p1++ = ' '; } else if (c == '\n' && p1 != line) { *p1 = '\0'; if (wordflag) msputwords(NO); else { puts(line); putchar('\n'); } p1 = line; } } } #ifdef DEBUG int _C1(void) { return(C1get); } int _C(void) { return(Cget); } #endif /* DEBUG */ /* * Put out a macro line, using ms and mm conventions. */ void msputmac(char *s, int constant) { char *t; int found; int last; last = 0; found = 0; if (wordflag) { msputwords(YES); return; } while (*s) { while (*s == ' ' || *s == '\t') putchar(*s++); for (t = s ; *t != ' ' && *t != '\t' && *t != '\0' ; ++t) ; /* nothing */ if (*s == '\"') s++; if (t > s + constant && chars[(unsigned char)s[0]] == LETTER && chars[(unsigned char)s[1]] == LETTER) { while (s < t) if (*s == '\"') s++; else putchar(*s++); last = *(t-1); found++; } else if (found && chars[(unsigned char)s[0]] == PUNCT && s[1] == '\0') { putchar(*s++); } else { last = *(t - 1); s = t; } } putchar('\n'); if (msflag && chars[last] == PUNCT) { putchar(last); putchar('\n'); } } /* * put out words (for the -w option) with ms and mm conventions */ void msputwords(int macline) { char *p, *p1; int i, nlet; for (p1 = line;;) { /* * skip initial specials ampersands and apostrophes */ while (chars[(unsigned char)*p1] < DIGIT) if (*p1++ == '\0') return; nlet = 0; for (p = p1 ; (i = chars[(unsigned char)*p]) != SPECIAL ; ++p) if (i == LETTER) ++nlet; if (nlet > 1 && chars[(unsigned char)p1[0]] == LETTER) { /* * delete trailing ampersands and apostrophes */ while ((i = chars[(unsigned char)p[-1]]) == PUNCT || i == APOS ) --p; while (p1 < p) putchar(*p1++); putchar('\n'); } else { p1 = p; } } } /* * put out a macro using the me conventions */ #define SKIPBLANK(cp) while (*cp == ' ' || *cp == '\t') { cp++; } #define SKIPNONBLANK(cp) while (*cp !=' ' && *cp !='\cp' && *cp !='\0') { cp++; } void meputmac(char *cp, int constant) { char *np; int found; int argno; int last; int inquote; last = 0; found = 0; if (wordflag) { meputwords(YES); return; } for (argno = 0; *cp; argno++) { SKIPBLANK(cp); inquote = (*cp == '"'); if (inquote) cp++; for (np = cp; *np; np++) { switch (*np) { case '\n': case '\0': break; case '\t': case ' ': if (inquote) continue; else goto endarg; case '"': if (inquote && np[1] == '"') { memmove(np, np + 1, strlen(np)); np++; continue; } else { *np = ' '; /* bye bye " */ goto endarg; } default: continue; } } endarg: ; /* * cp points at the first char in the arg * np points one beyond the last char in the arg */ if ((argconcat == 0) || (argconcat != argno)) putchar(' '); #ifdef FULLDEBUG { char *p; printf("[%d,%d: ", argno, np - cp); for (p = cp; p < np; p++) { putchar(*p); } printf("]"); } #endif /* FULLDEBUG */ /* * Determine if the argument merits being printed * * constant is the cut off point below which something * is not a word. */ if (((np - cp) > constant) && (inquote || (chars[(unsigned char)cp[0]] == LETTER))) { for (cp = cp; cp < np; cp++) putchar(*cp); last = np[-1]; found++; } else if (found && (np - cp == 1) && chars[(unsigned char)*cp] == PUNCT) { putchar(*cp); } else { last = np[-1]; } cp = np; } if (msflag && chars[last] == PUNCT) putchar(last); putchar('\n'); } /* * put out words (for the -w option) with ms and mm conventions */ void meputwords(int macline) { msputwords(macline); } /* * * Skip over a nested set of macros * * Possible arguments to noblock are: * * fi end of unfilled text * PE pic ending * DE display ending * * for ms and mm only: * KE keep ending * * NE undocumented match to NS (for mm?) * LE mm only: matches RL or *L (for lists) * * for me: * ([lqbzcdf] */ void noblock(char a1, char a2) { int c1,c2; int eqnf; int lct; lct = 0; eqnf = 1; SKIP; for (;;) { while (C != '.') if (c == '\n') continue; else SKIP; if ((c1 = C) == '\n') continue; if ((c2 = C) == '\n') continue; if (c1 == a1 && c2 == a2) { SKIP; if (lct != 0) { lct--; continue; } if (eqnf) putchar('.'); putchar('\n'); return; } else if (a1 == 'L' && c2 == 'L') { lct++; SKIP; } /* * equations (EQ) nested within a display */ else if (c1 == 'E' && c2 == 'Q') { if ((mac == ME && a1 == ')') || (mac != ME && a1 == 'D')) { eqn(); eqnf=0; } } /* * turning on filling is done by the paragraphing * macros */ else if (a1 == 'f') { /* .fi */ if ((mac == ME && (c2 == 'h' || c2 == 'p')) || (mac != ME && (c1 == 'P' || c2 == 'P'))) { SKIP; return; } } else { SKIP; } } } int EQ(void) { eqn(); return(0); } int domacro(void) { macro(); return(0); } int PS(void) { for (C; c == ' ' || c == '\t'; C) ; /* nothing */ if (c == '<') { /* ".PS < file" -- don't expect a .PE */ SKIP; return(0); } if (!msflag) inpic(); else noblock('P', 'E'); return(0); } int skip(void) { SKIP; return(0); } int intbl(void) { if (msflag) stbl(); else tbl(); return(0); } int outtbl(void) { intable = NO; return(0); } int so(void) { if (!iflag) { getfname(); if (fname[0]) { if (++filesp - &files[0] > MAXFILES) err(1, "too many nested files (max %d)", MAXFILES); infile = *filesp = opn(fname); } } return(0); } int nx(void) { if (!iflag) { getfname(); if (fname[0] == '\0') exit(0); if (infile != stdin) fclose(infile); infile = *filesp = opn(fname); } return(0); } int skiptocom(void) { SKIP_TO_COM; return(COMX); } int PP(pacmac c12) { int c1, c2; frommac(c12, c1, c2); printf(".%c%c", c1, c2); while (C != '\n') putchar(c); putchar('\n'); return(0); } int AU(void) { if (mac == MM) return(0); SKIP_TO_COM; return(COMX); } int SH(pacmac c12) { int c1, c2; frommac(c12, c1, c2); if (parag) { printf(".%c%c", c1, c2); while (C != '\n') putchar(c); putchar(c); putchar('!'); for (;;) { while (C != '\n') putchar(c); putchar('\n'); if (C == '.') return(COM); putchar('!'); putchar(c); } /*NOTREACHED*/ } else { SKIP_TO_COM; return(COMX); } } int UX(void) { if (wordflag) printf("UNIX\n"); else printf("UNIX "); return(0); } int MMHU(pacmac c12) { int c1, c2; frommac(c12, c1, c2); if (parag) { printf(".%c%c", c1, c2); while (C != '\n') putchar(c); putchar('\n'); } else { SKIP; } return(0); } int mesnblock(pacmac c12) { int c1, c2; frommac(c12, c1, c2); noblock(')', c2); return(0); } int mssnblock(pacmac c12) { int c1, c2; frommac(c12, c1, c2); noblock(c1, 'E'); return(0); } int nf(void) { noblock('f', 'i'); return(0); } int ce(void) { sce(); return(0); } int meip(pacmac c12) { if (parag) mepp(c12); else if (wordflag) /* save the tag */ regline(meputmac, ONE); else SKIP; return(0); } /* * only called for -me .pp or .sh, when parag is on */ int mepp(pacmac c12) { PP(c12); /* eats the line */ return(0); } /* * Start of a section heading; output the section name if doing words */ int mesh(pacmac c12) { if (parag) mepp(c12); else if (wordflag) defcomline(c12); else SKIP; return(0); } /* * process a font setting */ int mefont(pacmac c12) { argconcat = 1; defcomline(c12); argconcat = 0; return(0); } int manfont(pacmac c12) { return(mefont(c12)); } int manpp(pacmac c12) { return(mepp(c12)); } void defcomline(pacmac c12) { int c1, c2; frommac(c12, c1, c2); if (msflag && mac == MM && c2 == 'L') { if (disp || c1 == 'R') { noblock('L', 'E'); } else { SKIP; putchar('.'); } } else if (c1 == '.' && c2 == '.') { if (msflag) { SKIP; return; } while (C == '.') /*VOID*/; } ++inmacro; /* * Process the arguments to the macro */ switch (mac) { default: case MM: case MS: if (c1 <= 'Z' && msflag) regline(msputmac, ONE); else regline(msputmac, TWO); break; case ME: regline(meputmac, ONE); break; } --inmacro; } void comline(void) { int c1; int c2; pacmac c12; int mid; int lb, ub; int hit; static int tabsize = 0; static struct mactab *mactab = (struct mactab *)0; struct mactab *mp; if (mactab == 0) buildtab(&mactab, &tabsize); com: while (C == ' ' || c == '\t') ; comx: if ((c1 = c) == '\n') return; c2 = C; if (c1 == '.' && c2 != '.') inmacro = NO; if (msflag && c1 == '[') { refer(c2); return; } if (parag && mac==MM && c1 == 'P' && c2 == '\n') { printf(".P\n"); return; } if (c2 == '\n') return; /* * Single letter macro */ if (mac == ME && (c2 == ' ' || c2 == '\t') ) c2 = ' '; c12 = tomac(c1, c2); /* * binary search through the table of macros */ lb = 0; ub = tabsize - 1; while (lb <= ub) { mid = (ub + lb) / 2; mp = &mactab[mid]; if (mp->macname < c12) lb = mid + 1; else if (mp->macname > c12) ub = mid - 1; else { hit = 1; #ifdef FULLDEBUG printf("preliminary hit macro %c%c ", c1, c2); #endif /* FULLDEBUG */ switch (mp->condition) { case NONE: hit = YES; break; case FNEST: hit = (filesp == files); break; case NOMAC: hit = !inmacro; break; case MAC: hit = inmacro; break; case PARAG: hit = parag; break; case NBLK: hit = !keepblock; break; default: hit = 0; } if (hit) { #ifdef FULLDEBUG printf("MATCH\n"); #endif /* FULLDEBUG */ switch ((*(mp->func))(c12)) { default: return; case COMX: goto comx; case COM: goto com; } } #ifdef FULLDEBUG printf("FAIL\n"); #endif /* FULLDEBUG */ break; } } defcomline(c12); } int macsort(const void *p1, const void *p2) { struct mactab *t1 = (struct mactab *)p1; struct mactab *t2 = (struct mactab *)p2; return(t1->macname - t2->macname); } int sizetab(struct mactab *mp) { int i; i = 0; if (mp) { for (; mp->macname; mp++, i++) /*VOID*/ ; } return(i); } struct mactab * macfill(struct mactab *dst, struct mactab *src) { if (src) { while (src->macname) *dst++ = *src++; } return(dst); } __dead void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-ikpw] [-m a | e | l | m | s] [file ...]\n", __progname); exit(1); } void buildtab(struct mactab **r_back, int *r_size) { int size; struct mactab *p, *p1, *p2; struct mactab *back; size = sizetab(troffmactab) + sizetab(ppmactab); p1 = p2 = NULL; if (msflag) { switch (mac) { case ME: p1 = memactab; break; case MM: p1 = msmactab; p2 = mmmactab; break; case MS: p1 = msmactab; break; case MA: p1 = manmactab; break; default: break; } } size += sizetab(p1); size += sizetab(p2); back = calloc(size+2, sizeof(struct mactab)); if (back == NULL) err(1, NULL); p = macfill(back, troffmactab); p = macfill(p, ppmactab); p = macfill(p, p1); p = macfill(p, p2); qsort(back, size, sizeof(struct mactab), macsort); *r_size = size; *r_back = back; } /* * troff commands */ struct mactab troffmactab[] = { M(NONE, '\\','"', skip), /* comment */ M(NOMAC, 'd','e', domacro), /* define */ M(NOMAC, 'i','g', domacro), /* ignore till .. */ M(NOMAC, 'a','m', domacro), /* append macro */ M(NBLK, 'n','f', nf), /* filled */ M(NBLK, 'c','e', ce), /* centered */ M(NONE, 's','o', so), /* source a file */ M(NONE, 'n','x', nx), /* go to next file */ M(NONE, 't','m', skip), /* print string on tty */ M(NONE, 'h','w', skip), /* exception hyphen words */ M(NONE, 0,0, 0) }; /* * Preprocessor output */ struct mactab ppmactab[] = { M(FNEST, 'E','Q', EQ), /* equation starting */ M(FNEST, 'T','S', intbl), /* table starting */ M(FNEST, 'T','C', intbl), /* alternative table? */ M(FNEST, 'T','&', intbl), /* table reformatting */ M(NONE, 'T','E', outtbl),/* table ending */ M(NONE, 'P','S', PS), /* picture starting */ M(NONE, 0,0, 0) }; /* * Particular to ms and mm */ struct mactab msmactab[] = { M(NONE, 'T','L', skiptocom), /* title follows */ M(NONE, 'F','S', skiptocom), /* start footnote */ M(NONE, 'O','K', skiptocom), /* Other kws */ M(NONE, 'N','R', skip), /* undocumented */ M(NONE, 'N','D', skip), /* use supplied date */ M(PARAG, 'P','P', PP), /* begin parag */ M(PARAG, 'I','P', PP), /* begin indent parag, tag x */ M(PARAG, 'L','P', PP), /* left blocked parag */ M(NONE, 'A','U', AU), /* author */ M(NONE, 'A','I', AU), /* authors institution */ M(NONE, 'S','H', SH), /* section heading */ M(NONE, 'S','N', SH), /* undocumented */ M(NONE, 'U','X', UX), /* unix */ M(NBLK, 'D','S', mssnblock), /* start display text */ M(NBLK, 'K','S', mssnblock), /* start keep */ M(NBLK, 'K','F', mssnblock), /* start float keep */ M(NONE, 0,0, 0) }; struct mactab mmmactab[] = { M(NONE, 'H',' ', MMHU), /* -mm ? */ M(NONE, 'H','U', MMHU), /* -mm ? */ M(PARAG, 'P',' ', PP), /* paragraph for -mm */ M(NBLK, 'N','S', mssnblock), /* undocumented */ M(NONE, 0,0, 0) }; struct mactab memactab[] = { M(PARAG, 'p','p', mepp), M(PARAG, 'l','p', mepp), M(PARAG, 'n','p', mepp), M(NONE, 'i','p', meip), M(NONE, 's','h', mesh), M(NONE, 'u','h', mesh), M(NBLK, '(','l', mesnblock), M(NBLK, '(','q', mesnblock), M(NBLK, '(','b', mesnblock), M(NBLK, '(','z', mesnblock), M(NBLK, '(','c', mesnblock), M(NBLK, '(','d', mesnblock), M(NBLK, '(','f', mesnblock), M(NBLK, '(','x', mesnblock), M(NONE, 'r',' ', mefont), M(NONE, 'i',' ', mefont), M(NONE, 'b',' ', mefont), M(NONE, 'u',' ', mefont), M(NONE, 'q',' ', mefont), M(NONE, 'r','b', mefont), M(NONE, 'b','i', mefont), M(NONE, 'b','x', mefont), M(NONE, 0,0, 0) }; struct mactab manmactab[] = { M(PARAG, 'B','I', manfont), M(PARAG, 'B','R', manfont), M(PARAG, 'I','B', manfont), M(PARAG, 'I','R', manfont), M(PARAG, 'R','B', manfont), M(PARAG, 'R','I', manfont), M(PARAG, 'P','P', manpp), M(PARAG, 'L','P', manpp), M(PARAG, 'H','P', manpp), M(NONE, 0,0, 0) };