diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 9e2ccc3a5..feb039045 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -283,6 +283,7 @@ ./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 @@ -1883,6 +1884,7 @@ ./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 diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index 0a82f3e4c..c1cd823a8 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -162,6 +162,7 @@ 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 diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 07f6dc2ac..815d0c349 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -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 index 000000000..98a4fd0cb --- /dev/null +++ b/usr.bin/deroff/Makefile @@ -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 diff --git a/usr.bin/deroff/TODO b/usr.bin/deroff/TODO new file mode 100644 index 000000000..f416a2514 --- /dev/null +++ b/usr.bin/deroff/TODO @@ -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 index 000000000..69ce5de8c --- /dev/null +++ b/usr.bin/deroff/deroff.1 @@ -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 index 000000000..24e72a572 --- /dev/null +++ b/usr.bin/deroff/deroff.c @@ -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 +#include +#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 +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) +};