--- /dev/null
+/* $NetBSD: deroff.c,v 1.9 2011/08/31 13:38:19 joerg Exp $ */
+
+/* taken from: OpenBSD: deroff.c,v 1.6 2004/06/02 14:58:46 tom 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)deroff.c 8.1 (Berkeley) 6/6/93";
+#else
+static const char rcsid[] = "$NetBSD: deroff.c,v 1.9 2011/08/31 13:38:19 joerg Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <err.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * 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
+static 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
+
+static int iflag;
+static int wordflag;
+static int msflag; /* processing a source written using a mac package */
+static int mac; /* which package */
+static int disp;
+static int parag;
+static int inmacro;
+static int intable;
+static int keepblock; /* keep blocks of text; normally false when msflag */
+
+static char chars[128]; /* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
+
+static char line[LINE_MAX];
+static char *lp;
+
+static int c;
+static int pc;
+static int ldelim;
+static int rdelim;
+
+static char fname[PATH_MAX];
+static FILE *files[MAXFILES];
+static FILE **filesp;
+static FILE *infile;
+
+static int argc;
+static char **argv;
+
+/*
+ * Macro processing
+ *
+ * Macro table definitions
+ */
+typedef int pacmac; /* compressed macro name */
+static 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)(pacmac);
+};
+
+static const struct mactab troffmactab[];
+static const struct mactab ppmactab[];
+static const struct mactab msmactab[];
+static const struct mactab mmmactab[];
+static const struct mactab memactab[];
+static const 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 */
+
+static int skeqn(void);
+static int eof(void);
+#ifdef DEBUG
+static int _C1(void);
+static int _C(void);
+#endif
+static int EQ(pacmac);
+static int domacro(pacmac);
+static int PS(pacmac);
+static int skip(pacmac);
+static int intbl(pacmac);
+static int outtbl(pacmac);
+static int so(pacmac);
+static int nx(pacmac);
+static int skiptocom(pacmac);
+static int PP(pacmac);
+static int AU(pacmac);
+static int SH(pacmac);
+static int UX(pacmac);
+static int MMHU(pacmac);
+static int mesnblock(pacmac);
+static int mssnblock(pacmac);
+static int nf(pacmac);
+static int ce(pacmac);
+static int meip(pacmac);
+static int mepp(pacmac);
+static int mesh(pacmac);
+static int mefont(pacmac);
+static int manfont(pacmac);
+static int manpp(pacmac);
+static int macsort(const void *, const void *);
+static int sizetab(const struct mactab *);
+static void getfname(void);
+static void textline(char *, int);
+static void work(void) __dead;
+static void regline(void (*)(char *, int), int);
+static void macro(void);
+static void tbl(void);
+static void stbl(void);
+static void eqn(void);
+static void backsl(void);
+static void sce(void);
+static void refer(int);
+static void inpic(void);
+static void msputmac(char *, int);
+static void msputwords(int);
+static void meputmac(char *, int);
+static void meputwords(int);
+static void noblock(char, char);
+static void defcomline(pacmac);
+static void comline(void);
+static void buildtab(const struct mactab **, int *);
+static FILE *opn(char *);
+static struct mactab *macfill(struct mactab *, const struct mactab *);
+static void usage(void) __dead;
+
+int
+main(int ac, char **av)
+{
+ int i, ch;
+ int errflg = 0;
+ int kflag = NO;
+
+ 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++;
+ break;
+ }
+ if (errflg == 0 && optarg[1] != '\0')
+ errflg++;
+ break;
+ case 'p':
+ parag = YES;
+ break;
+ case 'w':
+ wordflag = YES;
+ kflag = YES;
+ break;
+ default:
+ errflg++;
+ }
+ }
+ 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();
+ return 0;
+}
+
+static 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 == ' ';
+}
+
+static FILE *
+opn(char *p)
+{
+ FILE *fd;
+
+ if ((fd = fopen(p, "r")) == NULL)
+ err(1, "fopen %s", p);
+
+ return fd;
+}
+
+static 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;
+}
+
+static 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 < (ptrdiff_t)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 = (struct chain *) 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*/
+static void
+textline(char *str, int constant)
+{
+
+ if (wordflag) {
+ msputwords(0);
+ return;
+ }
+ puts(str);
+}
+
+static void
+work(void)
+{
+
+ for (;;) {
+ C;
+#ifdef FULLDEBUG
+ printf("Starting work with `%c'\n", c);
+#endif /* FULLDEBUG */
+ if (c == '.' || c == '\'')
+ comline();
+ else
+ regline(textline, TWO);
+ }
+}
+
+static void
+regline(void (*pfunc)(char *, int), int constant)
+{
+
+ line[0] = c;
+ lp = line;
+ while (lp - line < (ptrdiff_t)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);
+}
+
+static void
+macro(void)
+{
+
+ if (msflag) {
+ do {
+ SKIP;
+ } while (C!='.' || C!='.' || C=='.'); /* look for .. */
+ if (c != '\n')
+ SKIP;
+ return;
+ }
+ SKIP;
+ inmacro = YES;
+}
+
+static void
+tbl(void)
+{
+
+ while (C != '.')
+ ; /* nothing */
+ SKIP;
+ intable = YES;
+}
+
+static 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;
+ }
+}
+
+static 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 */
+static 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;
+ }
+}
+
+static 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++;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+ }
+}
+
+static void
+inpic(void)
+{
+ int c1;
+ char *p1;
+
+ SKIP;
+ p1 = line;
+ 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
+ *p1++ = c;
+ }
+ *p1++ = ' ';
+ }
+ else if (c == '\n' && p1 != line) {
+ *p1 = '\0';
+ if (wordflag)
+ msputwords(NO);
+ else {
+ puts(line);
+ putchar('\n');
+ }
+ p1 = line;
+ }
+ }
+}
+
+#ifdef DEBUG
+static int
+_C1(void)
+{
+
+ return C1get;
+}
+
+static int
+_C(void)
+{
+
+ return Cget;
+}
+#endif /* DEBUG */
+
+/*
+ * Put out a macro line, using ms and mm conventions.
+ */
+static 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
+ */
+static 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++; }
+
+static 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 < 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
+ */
+static 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]
+ */
+static 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;
+ }
+ }
+}
+
+static int
+/*ARGSUSED*/
+EQ(pacmac unused)
+{
+
+ eqn();
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+domacro(pacmac unused)
+{
+
+ macro();
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+PS(pacmac unused)
+{
+
+ 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;
+}
+
+static int
+/*ARGSUSED*/
+skip(pacmac unused)
+{
+
+ SKIP;
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+intbl(pacmac unused)
+{
+
+ if (msflag)
+ stbl();
+ else
+ tbl();
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+outtbl(pacmac unused)
+{
+
+ intable = NO;
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+so(pacmac unused)
+{
+
+ 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;
+}
+
+static int
+/*ARGSUSED*/
+nx(pacmac unused)
+{
+
+ if (!iflag) {
+ getfname();
+ if (fname[0] == '\0')
+ exit(0);
+ if (infile != stdin)
+ fclose(infile);
+ infile = *filesp = opn(fname);
+ }
+ return 0;
+}
+
+static int
+/*ARGSUSED*/
+skiptocom(pacmac unused)
+{
+
+ SKIP_TO_COM;
+ return COMX;
+}
+
+static 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;
+}
+
+static int
+/*ARGSUSED*/
+AU(pacmac unused)
+{
+
+ if (mac == MM)
+ return 0;
+ SKIP_TO_COM;
+ return COMX;
+}
+
+static 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;
+ }
+}
+
+static int
+/*ARGSUSED*/
+UX(pacmac unused)
+{
+
+ if (wordflag)
+ printf("UNIX\n");
+ else
+ printf("UNIX ");
+ return 0;
+}
+
+static 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;
+}
+
+static int
+mesnblock(pacmac c12)
+{
+ int c1, c2;
+
+ frommac(c12, c1, c2);
+ noblock(')', c2);
+ return 0;
+}
+
+static int
+mssnblock(pacmac c12)
+{
+ int c1, c2;
+
+ frommac(c12, c1, c2);
+ noblock(c1, 'E');
+ return 0;
+}
+
+static int
+/*ARGUSED*/
+nf(pacmac unused)
+{
+
+ noblock('f', 'i');
+ return 0;
+}
+
+static int
+/*ARGUSED*/
+ce(pacmac unused)
+{
+
+ sce();
+ return 0;
+}
+
+static 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
+ */
+static int
+mepp(pacmac c12)
+{
+
+ PP(c12); /* eats the line */
+ return 0;
+}
+
+/*
+ * Start of a section heading; output the section name if doing words
+ */
+static int
+mesh(pacmac c12)
+{
+
+ if (parag)
+ mepp(c12);
+ else if (wordflag)
+ defcomline(c12);
+ else
+ SKIP;
+ return 0;
+}
+
+/*
+ * process a font setting
+ */
+static int
+mefont(pacmac c12)
+{
+
+ argconcat = 1;
+ defcomline(c12);
+ argconcat = 0;
+ return 0;
+}
+
+static int
+manfont(pacmac c12)
+{
+
+ return mefont(c12);
+}
+
+static int
+manpp(pacmac c12)
+{
+
+ return mepp(c12);
+}
+
+static 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;
+}
+
+static void
+comline(void)
+{
+ int c1;
+ int c2;
+ pacmac c12;
+ int mid;
+ int lb, ub;
+ int hit;
+ static int tabsize = 0;
+ static const struct mactab *mactab = NULL;
+ const 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);
+}
+
+static int
+macsort(const void *p1, const void *p2)
+{
+ const struct mactab *t1 = p1;
+ const struct mactab *t2 = p2;
+
+ return t1->macname - t2->macname;
+}
+
+static int
+sizetab(const struct mactab *mp)
+{
+ int i;
+
+ i = 0;
+ if (mp) {
+ for (; mp->macname; mp++, i++)
+ /*VOID*/ ;
+ }
+ return i;
+}
+
+static struct mactab *
+macfill(struct mactab *dst, const struct mactab *src)
+{
+
+ if (src) {
+ while (src->macname)
+ *dst++ = *src++;
+ }
+ return dst;
+}
+
+static void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-ikpw ] [ -m a | e | l | m | s] [file ...]\n", __progname);
+ exit(1);
+}
+
+static void
+buildtab(const struct mactab **r_back, int *r_size)
+{
+ size_t size;
+ const struct mactab *p1, *p2;
+ struct mactab *back, *p;
+
+ 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
+ */
+static const 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
+ */
+static const 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
+ */
+static const 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)
+};
+
+static const 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)
+};
+
+static const 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)
+};
+
+static const 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)
+};