]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing usr.bin/deroff 47/1147/1
authorThomas Cort <tcort@minix3.org>
Wed, 13 Nov 2013 00:03:55 +0000 (19:03 -0500)
committerThomas Cort <tcort@minix3.org>
Wed, 13 Nov 2013 00:04:27 +0000 (19:04 -0500)
No Minix-specific changes needed.

Change-Id: I87c3629238b7bfdbff3b64682fcdb245a7ce740c

distrib/sets/lists/minix/mi
releasetools/nbsd_ports
usr.bin/Makefile
usr.bin/deroff/Makefile [new file with mode: 0644]
usr.bin/deroff/TODO [new file with mode: 0644]
usr.bin/deroff/deroff.1 [new file with mode: 0644]
usr.bin/deroff/deroff.c [new file with mode: 0644]

index 9e2ccc3a5c47597ef2bb1fb11ff3548b0ad8a619..feb0390454fcbb061f3e35c6620362bd12e5e426 100644 (file)
 ./usr/bin/dd                           minix-sys
 ./usr/bin/decomp16                     minix-sys
 ./usr/bin/del_route                    minix-sys
+./usr/bin/deroff                       minix-sys
 ./usr/bin/DESCRIBE                     minix-sys
 ./usr/bin/devmand                      minix-sys
 ./usr/bin/devsize                      minix-sys
 ./usr/man/man1/cut.1                   minix-sys
 ./usr/man/man1/date.1                  minix-sys
 ./usr/man/man1/dd.1                    minix-sys
+./usr/man/man1/deroff.1                        minix-sys
 ./usr/man/man1/dev2name.1              minix-sys       obsolete
 ./usr/man/man1/df.1                    minix-sys
 ./usr/man/man1/dhrystone.1             minix-sys
index 0a82f3e4ccd3515130fe1571fc5efc96cdc112d6..c1cd823a892a0e26f56aab7889d3bfabe76524a8 100644 (file)
 2012/10/17 12:00:00,usr.bin/csplit
 2012/10/17 12:00:00,usr.bin/ctags
 2013/10/14 12:00:00,usr.bin/cut
+2012/10/17 12:00:00,usr.bin/deroff
 2012/10/17 12:00:00,usr.bin/dirname
 2011/09/01 13:37:33,usr.bin/du
 2012/10/17 12:00:00,usr.bin/expand
index 07f6dc2acb2f6f19574b6b22db593008e230b191..815d0c3499350db7281f2c3cd249221adc11bb59 100644 (file)
@@ -8,7 +8,7 @@ SUBDIR= asa \
        bzip2 bzip2recover cal calendar \
        chpass cksum col colrm \
        column comm csplit ctags cut \
-       dirname du \
+       deroff dirname du \
        env expand \
        finger fold fpr from \
        fsplit ftp genassym getopt \
diff --git a/usr.bin/deroff/Makefile b/usr.bin/deroff/Makefile
new file mode 100644 (file)
index 0000000..98a4fd0
--- /dev/null
@@ -0,0 +1,7 @@
+#      $NetBSD: Makefile,v 1.3 2009/04/14 22:15:19 lukem Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/6/93
+#      OpenBSD: Makefile,v 1.1 2002/02/28 06:58:21 millert Exp
+
+PROG=  deroff
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/deroff/TODO b/usr.bin/deroff/TODO
new file mode 100644 (file)
index 0000000..f416a25
--- /dev/null
@@ -0,0 +1,6 @@
+       $NetBSD: TODO,v 1.2 2005/09/01 18:27:24 rpaulo Exp $
+
+Things this needs:
+
+a) code cleanup
+b) support for mdoc macros
diff --git a/usr.bin/deroff/deroff.1 b/usr.bin/deroff/deroff.1
new file mode 100644 (file)
index 0000000..69ce5de
--- /dev/null
@@ -0,0 +1,184 @@
+.\"    $NetBSD: deroff.1,v 1.3 2005/07/05 15:28:16 wiz Exp $
+.\"
+.\" $OpenBSD: deroff.1,v 1.5 2003/06/10 09:12:10 jmc Exp $
+.\"
+.\" Copyright (c) 1990, 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.
+.\"
+.\"    @(#)deroff.1    8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt DEROFF 1
+.Os
+.Sh NAME
+.Nm deroff
+.Nd remove nroff/troff, eqn, pic and tbl constructs
+.Sh SYNOPSIS
+.Nm deroff
+.Op Fl ikpw
+.Oo
+.Fl m
+.Ar a | e | l | m | s
+.Oc
+.Op Ar
+.Sh DESCRIPTION
+.Nm deroff
+reads each file in sequence and removes all
+.Xr nroff 1
+and
+.Xr troff 1
+command lines, backslash constructions, macro definitions,
+.Xr eqn 1
+constructs (between
+.Dq .EQ
+and
+.Dq .EN
+lines or between delimiters),
+.Xr pic 1
+pictures,
+and table descriptions and writes the remainder to the standard output.
+.Nm
+follows chains of included files
+.Po
+.Dq .so
+and
+.Dq .nx
+commands
+.Pc ;
+if a file has already been included, a
+.Dq .so
+is ignored and a
+.Dq .nx
+terminates execution.
+If no input file is given,
+.Nm
+reads from the standard input.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl i
+Ignore
+.Dq .so
+and
+.Dq .nx
+commands.
+.It Fl k
+Keep blocks of text intact.
+This is the default behavior unless the
+.Fl m
+option is given.
+.It Fl m
+Enable support for common macro packages.
+The
+.Fl m
+option takes the following arguments:
+.Bl -tag -width Ds -offset indent -compact
+.It a
+recognize
+.Xr man 7
+macros.
+.It e
+recognize
+.Xr me 7
+macros.
+.It l
+remove list constructs.
+.It m
+recognize
+.Xr mm 7
+macros.
+.It s
+recognize
+.Xr ms 7
+macros.
+.El
+.It Fl p
+Preserve paragraph macros.
+This option only has an effect if the
+.Fl m
+option is also specified.
+.It Fl w
+Output a word list, one
+.Sq word
+(string of letters, digits, and apostrophes, beginning with a
+letter; apostrophes are removed) per line, and all other characters
+ignored.
+Normally, the output follows the original, with the deletions
+mentioned above.
+.El
+.Sh SEE ALSO
+.Xr eqn 1 ,
+.Xr nroff 1 ,
+.Xr pic 1 ,
+.Xr tbl 1 ,
+.Xr troff 1
+.Sh HISTORY
+.Nm
+appeared in
+.At v7 .
+.Sh BUGS
+.Nm
+is not a complete
+.Xr troff 1
+interpreter, so it can be confused by subtle constructs.
+Most errors result in too much rather than too little output.
+.Pp
+The
+.Fl ml
+option does not correctly handle nested lists.
diff --git a/usr.bin/deroff/deroff.c b/usr.bin/deroff/deroff.c
new file mode 100644 (file)
index 0000000..24e72a5
--- /dev/null
@@ -0,0 +1,1743 @@
+/*     $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)
+};