summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DESCR2
-rw-r--r--Makefile66
-rw-r--r--action.c1
-rw-r--r--mdoc.h8
-rw-r--r--mdocml.1100
-rw-r--r--mdocterm.1113
-rw-r--r--mdocterm.c289
-rw-r--r--mdoctree.1113
-rw-r--r--mdoctree.c (renamed from mdocml.c)252
-rw-r--r--private.h6
-rw-r--r--strings.c6
-rw-r--r--term.c77
-rw-r--r--term.h7
-rw-r--r--termact.c75
-rw-r--r--tree.c3
-rw-r--r--validate.c2
16 files changed, 745 insertions, 375 deletions
diff --git a/DESCR b/DESCR
index fe90abf6..045f7eda 100644
--- a/DESCR
+++ b/DESCR
@@ -1,2 +1,2 @@
-The mdocml utility interfaces with the mdoc library to compile
+The mdocml utilities interface with the mdoc library to compile
mdoc-roff documents into a variety of output formats.
diff --git a/Makefile b/Makefile
index 47624ee7..d4d37610 100644
--- a/Makefile
+++ b/Makefile
@@ -1,33 +1,37 @@
-VERSION = 1.2.0
+VERSION = 1.3.0
CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -g
LIBLNS = macro.ln mdoc.ln hash.ln strings.ln xstd.ln argv.ln \
validate.ln action.ln
-BINLNS = mdocml.ln term.ln tree.ln termact.ln
+TREELNS = mdoctree.ln tree.ln
-LNS = $(LIBLNS) $(BINLNS)
+TERMLNS = mdoctree.ln term.ln termact.ln
-LLNS = llib-llibmdoc.ln llib-lmdocml.ln
+LNS = $(LIBLNS) $(TREELNS) $(TERMLNS)
+
+LLNS = llib-llibmdoc.ln llib-lmdoctree.ln llib-lmdocterm.ln
LIBS = libmdoc.a
LIBOBJS = macro.o mdoc.o hash.o strings.o xstd.o argv.o \
- validate.o action.o
+ validate.o action.o
+
+TERMOBJS= mdocterm.o term.o termact.o
-BINOBJS = mdocml.o term.o tree.o termact.o
+TREEOBJS= mdoctree.o tree.o
-OBJS = $(LIBOBJS) $(BINOBJS)
+OBJS = $(LIBOBJS) $(TERMOBJS) $(TREEOBJS)
-SRCS = macro.c mdoc.c mdocml.c hash.c strings.c xstd.c argv.c \
- validate.c action.c term.c tree.c termact.c
+SRCS = macro.c mdoc.c hash.c strings.c xstd.c argv.c validate.c \
+ action.c term.c tree.c termact.c mdoctree.c mdocterm.c
HEADS = mdoc.h private.h term.h
-MANS = mdocml.1 mdoc.3
+MANS = mdoctree.1 mdocterm.1 mdoc.3
-BINS = mdocml
+BINS = mdocterm mdoctree
CLEAN = $(BINS) $(LNS) $(LLNS) $(LIBS) $(OBJS)
@@ -109,8 +113,10 @@ install:
mkdir -p $(PREFIX)/include/mdoc/
mkdir -p $(PREFIX)/lib/
mkdir -p $(PREFIX)/man/man1/
- install -m 0755 mdocml $(PREFIX)/bin/
- install -m 0444 mdocml.1 $(PREFIX)/man/man1/
+ install -m 0755 mdocterm $(PREFIX)/bin/
+ install -m 0755 mdoctree $(PREFIX)/bin/
+ install -m 0444 mdocterm.1 $(PREFIX)/man/man1/
+ install -m 0444 mdocterm.1 $(PREFIX)/man/man1/
install -m 0444 mdoc.3 $(PREFIX)/man/man3/
install -m 0644 libmdoc.a $(PREFIX)/lib/
install -m 0444 mdoc.h $(PREFIX)/include/
@@ -122,8 +128,10 @@ install-dist: mdocml-$(VERSION).tar.gz mdocml-oport-$(VERSION).tar.gz
install -m 0644 mdocml-oport-$(VERSION).tar.gz $(PREFIX)/mdocml-oport.tar.gz
uninstall:
- rm -f $(PREFIX)/bin/mdocml
- rm -f $(PREFIX)/man/man1/mdocml.1
+ rm -f $(PREFIX)/bin/mdocterm
+ rm -f $(PREFIX)/bin/mdoctree
+ rm -f $(PREFIX)/man/man1/mdocterm.1
+ rm -f $(PREFIX)/man/man1/mdoctree.1
rm -f $(PREFIX)/man/man3/mdoc.3
rm -f $(PREFIX)/lib/libmdoc.a
rm -f $(PREFIX)/include/mdoc.h
@@ -156,9 +164,13 @@ mdoc.ln: mdoc.c private.h
mdoc.o: mdoc.c private.h
-mdocml.ln: mdocml.c mdoc.h
+mdocterm.ln: mdocterm.c term.h
+
+mdocterm.o: mdocterm.c term.h
-mdocml.o: mdocml.c mdoc.h
+mdoctree.ln: mdoctree.c mdoc.h
+
+mdoctree.o: mdoctree.c mdoc.h
xstd.ln: xstd.c private.h
@@ -186,10 +198,12 @@ mdocml-oport-$(VERSION).tar.gz: Makefile.port DESCR
sha1 mdocml-$(VERSION).tar.gz >> .dist/mdocml/distinfo
install -m 0644 DESCR .dist/mdocml/pkg/DESCR
echo @comment $$OpenBSD$$ > .dist/mdocml/pkg/PLIST
- echo bin/mdocml >> .dist/mdocml/pkg/PLIST
+ echo bin/mdocterm >> .dist/mdocml/pkg/PLIST
+ echo bin/mdoctree >> .dist/mdocml/pkg/PLIST
echo lib/libmdoc.a >> .dist/mdocml/pkg/PLIST
echo include/mdoc.h >> .dist/mdocml/pkg/PLIST
- echo @man man/man1/mdocml.1 >> .dist/mdocml/pkg/PLIST
+ echo @man man/man1/mdoctree.1 >> .dist/mdocml/pkg/PLIST
+ echo @man man/man1/mdocterm.1 >> .dist/mdocml/pkg/PLIST
echo @man man/man3/mdoc.3 >> .dist/mdocml/pkg/PLIST
( cd .dist/ && tar zcf ../$@ mdocml/ )
rm -rf .dist/
@@ -203,12 +217,18 @@ mdocml-$(VERSION).tar.gz: $(INSTALL)
llib-llibmdoc.ln: $(LIBLNS)
$(LINT) $(LINTFLAGS) -Clibmdoc $(LIBLNS)
-llib-lmdocml.ln: $(BINLNS) llib-llibmdoc.ln
- $(LINT) $(LINTFLAGS) -Cmdocml $(BINLNS) llib-llibmdoc.ln
+llib-lmdoctree.ln: $(TREELNS) llib-llibmdoc.ln
+ $(LINT) $(LINTFLAGS) -Cmdoctree $(TREELNS) llib-llibmdoc.ln
+
+llib-lmdocterm.ln: $(TERMLNS) llib-llibmdoc.ln
+ $(LINT) $(LINTFLAGS) -Cmdocterm $(TERMLNS) llib-llibmdoc.ln
libmdoc.a: $(LIBOBJS)
$(AR) rs $@ $(LIBOBJS)
-mdocml: $(BINOBJS) libmdoc.a
- $(CC) $(CFLAGS) -o $@ $(BINOBJS) libmdoc.a
+mdocterm: $(TERMOBJS) libmdoc.a
+ $(CC) $(CFLAGS) -o $@ $(TERMOBJS) libmdoc.a
+
+mdoctree: $(TREEOBJS) libmdoc.a
+ $(CC) $(CFLAGS) -o $@ $(TREEOBJS) libmdoc.a
diff --git a/action.c b/action.c
index 851910c4..1ffb2993 100644
--- a/action.c
+++ b/action.c
@@ -216,6 +216,7 @@ post_dt(struct mdoc *mdoc)
assert(NULL == mdoc->meta.title);
+ /* LINTED */
for (i = 0, n = mdoc->last->child; n; n = n->next, i++) {
assert(MDOC_TEXT == n->type);
p = n->data.text.string;
diff --git a/mdoc.h b/mdoc.h
index 7d6da91b..49e1bcda 100644
--- a/mdoc.h
+++ b/mdoc.h
@@ -421,6 +421,14 @@ const struct mdoc_meta *mdoc_meta(struct mdoc *);
/* Signal end of parse sequence (boolean retval). */
int mdoc_endparse(struct mdoc *);
+const char *mdoc_arch2a(enum mdoc_arch);
+
+const char *mdoc_vol2a(enum mdoc_vol);
+
+const char *mdoc_msec2a(enum mdoc_msec);
+
+int mdoc_isdelim(const char *);
+
__END_DECLS
#endif /*!MDOC_H*/
diff --git a/mdocml.1 b/mdocml.1
deleted file mode 100644
index 2bae03d2..00000000
--- a/mdocml.1
+++ /dev/null
@@ -1,100 +0,0 @@
-.\"
-.Dd $Mdocdate$
-.Dt mdocml 1
-.Os
-.\" SECTION
-.Sh NAME
-.Nm mdocml
-.Nd mdoc macro compiler
-.\" SECTION
-.Sh SYNOPSIS
-.Nm mdocml
-.Op Fl f Ns Ar filter
-.Op Fl v
-.Op Fl W Ns Ar err...
-.Op Ar infile
-.\" SECTION
-.Sh DESCRIPTION
-The
-.Nm
-utility interfaces the
-.Xr mdoc 3
-library to scan, parse, validate and output mdoc-macro documents.
-Arguments follow:
-.Bl -tag -width "\-Werr... "
-.\" ITEM
-.It Fl f Ns Ar filter
-Pipe the parsed syntax tree into an output filter. May be either
-.Ar tree
-for the parse tree or
-.Ar term
-for a terminal-encoded, formatted manual page.
-.\" ITEM
-.It Fl v
-Print verbose parsing output.
-.\" ITEM
-.It Fl W Ns Ar err...
-Print warning messages. May be set to
-.Fl W Ns Ar all
-for all warnings,
-.Ar compat
-for groff/troff-compatibility warnings, or
-.Ar syntax
-for syntax warnings. If
-.Fl W Ns Ar error
-is specified, warnings are considered errors and cause utility
-termination. Multiple
-.Fl W
-arguments may be comma-separated, such as
-.Fl W Ns Ar error,all .
-.\" ITEM
-.It Ar infile
-Read input from
-.Ar infile ,
-which may be
-.Dq \-
-for stdin.
-.El
-.\" PARAGRAPH
-.Pp
-Parsing and validation rules are drawn entirely from the
-.Xr mdoc 7
-and
-.Xr mdoc.samples 7
-manuals.
-.\" PARAGRAPH
-.Pp
-By default,
-.Nm
-reads from stdin and only validates its input.
-.\" PARAGRAPH
-.Pp
-.Ex -std mdocml
-.\" PARAGRAPH
-.Pp
-.Nm
-is
-.Ud
-.\" SECTION
-.Sh EXAMPLES
-To validate this manual page:
-.\" PARAGRAPH
-.Pp
-.D1 % mdocml \-Wall,error mdocml.1
-.\" SECTION
-.Sh SEE ALSO
-.Xr groff 1 ,
-.Xr mdoc.samples 7 ,
-.Xr mdoc 7 ,
-.Xr mdoc 3
-.\"
-.Sh AUTHORS
-The
-.Nm
-utility was written by
-.An Kristaps Dzonsons Aq kristaps@kth.se .
-.\" SECTION
-.Sh CAVEATS
-See
-.Xr mdoc 3
-for a list of bugs, caveats, and incomplete macros.
diff --git a/mdocterm.1 b/mdocterm.1
new file mode 100644
index 00000000..fd4abf63
--- /dev/null
+++ b/mdocterm.1
@@ -0,0 +1,113 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the
+.\" above copyright notice and this permission notice appear in all
+.\" copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+.\" AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+.\" TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt mdocmterm 1
+.Os
+.\" SECTION
+.Sh NAME
+.Nm mdocmterm
+.Nd mdoc macro compiler
+.\" SECTION
+.Sh SYNOPSIS
+.Nm mdocmterm
+.Op Fl v
+.Op Fl W Ns Ar err...
+.Op Ar infile
+.\" SECTION
+.Sh DESCRIPTION
+The
+.Nm
+utility formats a BSD
+.Dq mdoc
+manual page for display on the terminal. The arguments are as follows:
+.Bl -tag -width "\-Werr... "
+.\" ITEM
+.It Fl v
+Print verbose parsing output.
+.\" ITEM
+.It Fl W Ns Ar err...
+Print warning messages. May be set to
+.Fl W Ns Ar all
+for all warnings,
+.Ar compat
+for groff/troff-compatibility warnings, or
+.Ar syntax
+for syntax warnings. If
+.Fl W Ns Ar error
+is specified, warnings are considered errors and cause utility
+termination. Multiple
+.Fl W
+arguments may be comma-separated, such as
+.Fl W Ns Ar error,all .
+.\" ITEM
+.It Ar infile
+Read input from
+.Ar infile ,
+which may be
+.Dq \-
+for stdin.
+.El
+.\" PARAGRAPH
+The
+.Nm
+utility is a formatting front-end for
+.Xr mdoc 3 ,
+which parses the
+.Dq mdoc
+input, documented at
+.Xr mdoc 7
+and
+.Xr mdoc.samples 7 ,
+into an abstract syntax tree.
+.\" PARAGRAPH
+.Pp
+By default,
+.Nm
+reads from stdin and prints terminal-encoded output to stdout.
+.\" PARAGRAPH
+.Pp
+.Ex -std mdocmterm
+.\" PARAGRAPH
+.Pp
+.Nm
+is
+.Ud
+.\" SECTION
+.Sh EXAMPLES
+To display this manual page:
+.\" PARAGRAPH
+.Pp
+.D1 % mdocmterm \-Wall,error mdocmterm.1
+.\" SECTION
+.Sh SEE ALSO
+.Xr mdoctree 1 ,
+.Xr mdoc.samples 7 ,
+.Xr mdoc 7 ,
+.Xr mdoc 3
+.\"
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Kristaps Dzonsons Aq kristaps@kth.se .
+.\" SECTION
+.Sh CAVEATS
+See
+.Xr mdoc 3
+for a list of bugs, caveats, and incomplete macros.
diff --git a/mdocterm.c b/mdocterm.c
new file mode 100644
index 00000000..9dccd5c5
--- /dev/null
+++ b/mdocterm.c
@@ -0,0 +1,289 @@
+ /* $Id$ */
+/*
+ * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <err.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mdoc.h"
+#include "term.h"
+
+#define MD_LINE_SZ (256) /* Max input line size. */
+
+struct md_parse {
+ int warn; /* Warning flags. */
+#define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */
+#define MD_WARN_COMPAT (1 << 1) /* Show compat warnings. */
+#define MD_WARN_ALL (0x03) /* Show all warnings. */
+#define MD_WARN_ERR (1 << 2) /* Make warnings->errors. */
+ int dbg; /* Debug level. */
+ struct mdoc *mdoc; /* Active parser. */
+ char *buf; /* Input buffer. */
+ u_long bufsz; /* Input buffer size. */
+ char *in; /* Input file name. */
+ int fdin; /* Input file desc. */
+};
+
+extern char *__progname;
+
+static void usage(void);
+static int getsopts(struct md_parse *, char *);
+static int parse(struct md_parse *);
+static void msg_msg(void *, int, int, const char *);
+static int msg_err(void *, int, int, const char *);
+static int msg_warn(void *, int, int,
+ enum mdoc_warn, const char *);
+
+#ifdef __linux__
+extern int getsubopt(char **, char *const *, char **);
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ struct md_parse p;
+ struct mdoc_cb cb;
+ struct stat st;
+ int c;
+ extern char *optarg;
+ extern int optind;
+
+ (void)memset(&p, 0, sizeof(struct md_parse));
+
+ while (-1 != (c = getopt(argc, argv, "vW:")))
+ switch (c) {
+ case ('v'):
+ p.dbg++;
+ break;
+ case ('W'):
+ if ( ! getsopts(&p, optarg))
+ return(0);
+ break;
+ default:
+ usage();
+ return(0);
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ /* Initialise the input file. */
+
+ p.in = "-";
+ p.fdin = STDIN_FILENO;
+
+ if (argc > 0) {
+ p.in = *argv++;
+ p.fdin = open(p.in, O_RDONLY, 0);
+ if (-1 == p.fdin)
+ err(1, "%s", p.in);
+ }
+
+ /* Allocate a buffer to be BUFSIZ/block size. */
+
+ if (-1 == fstat(p.fdin, &st)) {
+ warn("%s", p.in);
+ p.bufsz = BUFSIZ;
+ } else
+ p.bufsz = MAX(st.st_blksize, BUFSIZ);
+
+ p.buf = malloc(p.bufsz);
+ if (NULL == p.buf)
+ err(1, "malloc");
+
+ /* Allocate the parser. */
+
+ cb.mdoc_err = msg_err;
+ cb.mdoc_warn = msg_warn;
+ cb.mdoc_msg = msg_msg;
+
+ p.mdoc = mdoc_alloc(&p, &cb);
+
+ /* Parse the input file. */
+
+ c = parse(&p);
+ free(p.buf);
+
+ if (STDIN_FILENO != p.fdin && -1 == close(p.fdin))
+ warn("%s", p.in);
+
+ if (0 == c) {
+ mdoc_free(p.mdoc);
+ return(EXIT_FAILURE);
+ }
+
+ /* If the parse succeeded, print it out. */
+
+ termprint(mdoc_node(p.mdoc), mdoc_meta(p.mdoc));
+ mdoc_free(p.mdoc);
+
+ return(EXIT_SUCCESS);
+}
+
+
+static int
+getsopts(struct md_parse *p, char *arg)
+{
+ char *v;
+ char *toks[] = { "all", "compat",
+ "syntax", "error", NULL };
+
+ while (*arg)
+ switch (getsubopt(&arg, toks, &v)) {
+ case (0):
+ p->warn |= MD_WARN_ALL;
+ break;
+ case (1):
+ p->warn |= MD_WARN_COMPAT;
+ break;
+ case (2):
+ p->warn |= MD_WARN_SYNTAX;
+ break;
+ case (3):
+ p->warn |= MD_WARN_ERR;
+ break;
+ default:
+ usage();
+ return(0);
+ }
+
+ return(1);
+}
+
+
+static int
+parse(struct md_parse *p)
+{
+ ssize_t sz, i;
+ size_t pos;
+ char line[MD_LINE_SZ];
+ int lnn;
+
+ /*
+ * This is a little more complicated than fgets. TODO: have
+ * some benchmarks that show it's faster (note that I want to
+ * check many, many manuals simultaneously, so speed is
+ * important). Fill a buffer (sized to the block size) with a
+ * single read, then parse \n-terminated lines into a line
+ * buffer, which is passed to the parser. Hard-code the line
+ * buffer to a particular size -- a reasonable assumption.
+ */
+
+ for (lnn = 1, pos = 0; ; ) {
+ if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
+ warn("%s", p->in);
+ return(0);
+ } else if (0 == sz)
+ break;
+
+ for (i = 0; i < sz; i++) {
+ if ('\n' != p->buf[i]) {
+ if (pos < sizeof(line)) {
+ line[(int)pos++] = p->buf[(int)i];
+ continue;
+ }
+ warnx("%s: line %d too long", p->in, lnn);
+ return(0);
+ }
+
+ line[(int)pos] = 0;
+ if ( ! mdoc_parseln(p->mdoc, lnn, line))
+ return(0);
+
+ lnn++;
+ pos = 0;
+ }
+ }
+
+ return(mdoc_endparse(p->mdoc));
+}
+
+
+static int
+msg_err(void *arg, int line, int col, const char *msg)
+{
+ struct md_parse *p;
+
+ p = (struct md_parse *)arg;
+
+ warnx("%s:%d: error: %s (column %d)",
+ p->in, line, msg, col);
+ return(0);
+}
+
+
+static void
+msg_msg(void *arg, int line, int col, const char *msg)
+{
+ struct md_parse *p;
+
+ p = (struct md_parse *)arg;
+
+ if (0 == p->dbg)
+ return;
+
+ warnx("%s:%d: debug: %s (column %d)",
+ p->in, line, msg, col);
+}
+
+
+static int
+msg_warn(void *arg, int line, int col,
+ enum mdoc_warn type, const char *msg)
+{
+ struct md_parse *p;
+
+ p = (struct md_parse *)arg;
+
+ switch (type) {
+ case (WARN_COMPAT):
+ if (p->warn & MD_WARN_COMPAT)
+ break;
+ return(1);
+ case (WARN_SYNTAX):
+ if (p->warn & MD_WARN_SYNTAX)
+ break;
+ return(1);
+ }
+
+ warnx("%s:%d: warning: %s (column %d)",
+ p->in, line, msg, col);
+
+ if ( ! (p->warn & MD_WARN_ERR))
+ return(1);
+
+ warnx("%s: considering warnings as errors", __progname);
+ return(0);
+}
+
+
+static void
+usage(void)
+{
+
+ warnx("usage: %s [-v] [-Wwarn...] [infile]", __progname);
+}
+
diff --git a/mdoctree.1 b/mdoctree.1
new file mode 100644
index 00000000..0174f888
--- /dev/null
+++ b/mdoctree.1
@@ -0,0 +1,113 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the
+.\" above copyright notice and this permission notice appear in all
+.\" copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+.\" AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+.\" TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt mdoctree 1
+.Os
+.\" SECTION
+.Sh NAME
+.Nm mdoctree
+.Nd mdoc macro compiler
+.\" SECTION
+.Sh SYNOPSIS
+.Nm mdoctree
+.Op Fl v
+.Op Fl W Ns Ar err...
+.Op Ar infile
+.\" SECTION
+.Sh DESCRIPTION
+The
+.Nm
+utility parses a BSD
+.Dq mdoc
+manual pages and prints its syntax tree. The arguments are as follows:
+.Bl -tag -width "\-Werr... "
+.\" ITEM
+.It Fl v
+Print verbose parsing output.
+.\" ITEM
+.It Fl W Ns Ar err...
+Print warning messages. May be set to
+.Fl W Ns Ar all
+for all warnings,
+.Ar compat
+for groff/troff-compatibility warnings, or
+.Ar syntax
+for syntax warnings. If
+.Fl W Ns Ar error
+is specified, warnings are considered errors and cause utility
+termination. Multiple
+.Fl W
+arguments may be comma-separated, such as
+.Fl W Ns Ar error,all .
+.\" ITEM
+.It Ar infile
+Read input from
+.Ar infile ,
+which may be
+.Dq \-
+for stdin.
+.El
+.\" PARAGRAPH
+The
+.Nm
+utility is a formatting front-end for
+.Xr mdoc 3 ,
+which parses the
+.Dq mdoc
+input, documented at
+.Xr mdoc 7
+and
+.Xr mdoc.samples 7 ,
+into an abstract syntax tree.
+.\" PARAGRAPH
+.Pp
+By default,
+.Nm
+reads from stdin and prints the syntax tree to stdout.
+.\" PARAGRAPH
+.Pp
+.Ex -std mdoctree
+.\" PARAGRAPH
+.Pp
+.Nm
+is
+.Ud
+.\" SECTION
+.Sh EXAMPLES
+To validate this manual page:
+.\" PARAGRAPH
+.Pp
+.D1 % mdoctree \-Wall,error mdoctree.1
+.\" SECTION
+.Sh SEE ALSO
+.Xr mdocterm 1 ,
+.Xr mdoc.samples 7 ,
+.Xr mdoc 7 ,
+.Xr mdoc 3
+.\"
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Kristaps Dzonsons Aq kristaps@kth.se .
+.\" SECTION
+.Sh CAVEATS
+See
+.Xr mdoc 3
+for a list of bugs, caveats, and incomplete macros.
diff --git a/mdocml.c b/mdoctree.c
index 7907a99d..9cc8fe9a 100644
--- a/mdocml.c
+++ b/mdoctree.c
@@ -32,9 +32,6 @@
#define MD_LINE_SZ (256) /* Max input line size. */
-typedef int (*md_print)(const struct mdoc_node *,
- const struct mdoc_meta *);
-
struct md_parse {
int warn; /* Warning flags. */
#define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */
@@ -47,28 +44,21 @@ struct md_parse {
u_long bufsz; /* Input buffer size. */
char *in; /* Input file name. */
int fdin; /* Input file desc. */
- md_print fp;
};
extern char *__progname;
static void usage(void);
-
-static int parse_opts(struct md_parse *, int, char *[]);
-static int parse_subopts(struct md_parse *, char *);
-
-static int parse_begin(struct md_parse *);
-static int parse_leave(struct md_parse *, int);
-static int io_begin(struct md_parse *);
-static int io_leave(struct md_parse *, int);
-static int buf_begin(struct md_parse *);
-static int buf_leave(struct md_parse *, int);
-
+static int getsopts(struct md_parse *, char *);
+static int parse(struct md_parse *);
static void msg_msg(void *, int, int, const char *);
static int msg_err(void *, int, int, const char *);
static int msg_warn(void *, int, int,
enum mdoc_warn, const char *);
+extern void treeprint(const struct mdoc_node *,
+ const struct mdoc_meta *);
+
#ifdef __linux__
extern int getsubopt(char **, char *const *, char **);
#endif
@@ -76,102 +66,22 @@ extern int getsubopt(char **, char *const *, char **);
int
main(int argc, char *argv[])
{
- struct md_parse parser;
-
- (void)memset(&parser, 0, sizeof(struct md_parse));
-
- if ( ! parse_opts(&parser, argc, argv))
- return(EXIT_FAILURE);
- if ( ! io_begin(&parser))
- return(EXIT_FAILURE);
-
- return(EXIT_SUCCESS);
-}
-
-
-static int
-io_leave(struct md_parse *p, int code)
-{
-
- if (-1 == p->fdin || STDIN_FILENO == p->fdin)
- return(code);
-
- if (-1 == close(p->fdin)) {
- warn("%s", p->in);
- code = 0;
- }
- return(code);
-}
-
-
-static int
-parse_subopts(struct md_parse *p, char *arg)
-{
- char *v;
- char *toks[] = { "all", "compat",
- "syntax", "error", NULL };
-
- /*
- * Future -Wxxx levels and so on should be here. For now we
- * only recognise syntax and compat warnings as categories,
- * beyond the usually "all" and "error" (make warn error out).
- */
-
- while (*arg)
- switch (getsubopt(&arg, toks, &v)) {
- case (0):
- p->warn |= MD_WARN_ALL;
- break;
- case (1):
- p->warn |= MD_WARN_COMPAT;
- break;
- case (2):
- p->warn |= MD_WARN_SYNTAX;
- break;
- case (3):
- p->warn |= MD_WARN_ERR;
- break;
- default:
- usage();
- return(0);
- }
-
- return(1);
-}
-
-
-static int
-parse_opts(struct md_parse *p, int argc, char *argv[])
-{
+ struct md_parse p;
+ struct mdoc_cb cb;
+ struct stat st;
int c;
-
extern char *optarg;
extern int optind;
- extern int termprint(const struct mdoc_node *,
- const struct mdoc_meta *);
- extern int treeprint(const struct mdoc_node *,
- const struct mdoc_meta *);
-
- p->in = "-";
+ (void)memset(&p, 0, sizeof(struct md_parse));
- while (-1 != (c = getopt(argc, argv, "f:vW:")))
+ while (-1 != (c = getopt(argc, argv, "vW:")))
switch (c) {
- case ('f'):
- if (0 == strcmp(optarg, "tree")) {
- p->fp = treeprint;
- break;
- } else if (0 == strcmp(optarg, "term")) {
- p->fp = termprint;
- break;
- }
- warnx("unknown filter: %s", optarg);
- return(0);
case ('v'):
- p->dbg++;
+ p.dbg++;
break;
case ('W'):
- if ( ! parse_subopts(p, optarg))
+ if ( ! getsopts(&p, optarg))
return(0);
break;
default:
@@ -180,103 +90,100 @@ parse_opts(struct md_parse *p, int argc, char *argv[])
}
argv += optind;
- if (0 == (argc -= optind))
- return(1);
-
- p->in = *argv++;
- return(1);
-}
+ argc -= optind;
+ /* Initialise the input file. */
-static int
-io_begin(struct md_parse *p)
-{
+ p.in = "-";
+ p.fdin = STDIN_FILENO;
- p->fdin = STDIN_FILENO;
- if (0 != strncmp(p->in, "-", 1))
- if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
- warn("%s", p->in);
- return(io_leave(p, 0));
- }
+ if (argc > 0) {
+ p.in = *argv++;
+ p.fdin = open(p.in, O_RDONLY, 0);
+ if (-1 == p.fdin)
+ err(1, "%s", p.in);
+ }
- return(io_leave(p, buf_begin(p)));
-}
+ /* Allocate a buffer to be BUFSIZ/block size. */
+ if (-1 == fstat(p.fdin, &st)) {
+ warn("%s", p.in);
+ p.bufsz = BUFSIZ;
+ } else
+ p.bufsz = MAX(st.st_blksize, BUFSIZ);
-static int
-buf_leave(struct md_parse *p, int code)
-{
+ p.buf = malloc(p.bufsz);
+ if (NULL == p.buf)
+ err(1, "malloc");
- if (p->buf)
- free(p->buf);
- return(code);
-}
+ /* Allocate the parser. */
+ cb.mdoc_err = msg_err;
+ cb.mdoc_warn = msg_warn;
+ cb.mdoc_msg = msg_msg;
-static int
-buf_begin(struct md_parse *p)
-{
- struct stat st;
+ p.mdoc = mdoc_alloc(&p, &cb);
- if (-1 == fstat(p->fdin, &st)) {
- warn("%s", p->in);
- return(0);
- }
+ /* Parse the input file. */
- /*
- * Try to intuit the fastest way of sucking down buffered data
- * by using either the block buffer size or the hard-coded one.
- * This is inspired by bin/cat.c.
- */
+ c = parse(&p);
+ free(p.buf);
- p->bufsz = MAX(st.st_blksize, BUFSIZ);
+ if (STDIN_FILENO != p.fdin && -1 == close(p.fdin))
+ warn("%s", p.in);
- if (NULL == (p->buf = malloc(p->bufsz))) {
- warn("malloc");
- return(buf_leave(p, 0));
+ if (0 == c) {
+ mdoc_free(p.mdoc);
+ return(EXIT_FAILURE);
}
- return(buf_leave(p, parse_begin(p)));
+ /* If the parse succeeded, print it out. */
+
+ treeprint(mdoc_node(p.mdoc), mdoc_meta(p.mdoc));
+ mdoc_free(p.mdoc);
+
+ return(EXIT_SUCCESS);
}
static int
-parse_leave(struct md_parse *p, int code)
+getsopts(struct md_parse *p, char *arg)
{
- md_print fp;
-
- if (NULL == p->mdoc)
- return(code);
-
- if ( ! mdoc_endparse(p->mdoc))
- code = 0;
+ char *v;
+ char *toks[] = { "all", "compat",
+ "syntax", "error", NULL };
- if (code && (fp = p->fp)) {
- if ( ! (*fp)(mdoc_node(p->mdoc), mdoc_meta(p->mdoc)))
- code = 0;
- }
+ while (*arg)
+ switch (getsubopt(&arg, toks, &v)) {
+ case (0):
+ p->warn |= MD_WARN_ALL;
+ break;
+ case (1):
+ p->warn |= MD_WARN_COMPAT;
+ break;
+ case (2):
+ p->warn |= MD_WARN_SYNTAX;
+ break;
+ case (3):
+ p->warn |= MD_WARN_ERR;
+ break;
+ default:
+ usage();
+ return(0);
+ }
- mdoc_free(p->mdoc);
- return(code);
+ return(1);
}
static int
-parse_begin(struct md_parse *p)
+parse(struct md_parse *p)
{
ssize_t sz, i;
size_t pos;
char line[MD_LINE_SZ];
- struct mdoc_cb cb;
int lnn;
- cb.mdoc_err = msg_err;
- cb.mdoc_warn = msg_warn;
- cb.mdoc_msg = msg_msg;
-
- if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
- return(parse_leave(p, 0));
-
/*
* This is a little more complicated than fgets. TODO: have
* some benchmarks that show it's faster (note that I want to
@@ -290,7 +197,7 @@ parse_begin(struct md_parse *p)
for (lnn = 1, pos = 0; ; ) {
if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
warn("%s", p->in);
- return(parse_leave(p, 0));
+ return(0);
} else if (0 == sz)
break;
@@ -301,19 +208,19 @@ parse_begin(struct md_parse *p)
continue;
}
warnx("%s: line %d too long", p->in, lnn);
- return(parse_leave(p, 0));
+ return(0);
}
line[(int)pos] = 0;
if ( ! mdoc_parseln(p->mdoc, lnn, line))
- return(parse_leave(p, 0));
+ return(0);
lnn++;
pos = 0;
}
}
- return(parse_leave(p, 1));
+ return(mdoc_endparse(p->mdoc));
}
@@ -379,7 +286,6 @@ static void
usage(void)
{
- warnx("usage: %s [-ffilter] [-v] [-Wwarn...] [infile]",
- __progname);
+ warnx("usage: %s [-v] [-Wwarn...] [infile]", __progname);
}
diff --git a/private.h b/private.h
index af32c542..fff0a303 100644
--- a/private.h
+++ b/private.h
@@ -123,7 +123,6 @@ void mdoc_sibling(struct mdoc *, int, struct mdoc_node **,
void *mdoc_tokhash_alloc(void);
int mdoc_tokhash_find(const void *, const char *);
void mdoc_tokhash_free(void *);
-int mdoc_isdelim(const char *);
int mdoc_iscdelim(char);
enum mdoc_sec mdoc_atosec(const char *);
enum mdoc_msec mdoc_atomsec(const char *);
@@ -132,11 +131,6 @@ enum mdoc_arch mdoc_atoarch(const char *);
enum mdoc_att mdoc_atoatt(const char *);
time_t mdoc_atotime(const char *);
-/* FIXME: these three are only for output channels. */
-char *mdoc_arch2a(enum mdoc_arch);
-char *mdoc_vol2a(enum mdoc_vol);
-char *mdoc_msec2a(enum mdoc_msec);
-
char *mdoc_type2a(enum mdoc_type);
char *mdoc_node2a(struct mdoc_node *);
diff --git a/strings.c b/strings.c
index 760e89f0..4a63aaf5 100644
--- a/strings.c
+++ b/strings.c
@@ -336,7 +336,7 @@ mdoc_type2a(enum mdoc_type type)
}
-char *
+const char *
mdoc_arch2a(enum mdoc_arch arch)
{
@@ -404,7 +404,7 @@ mdoc_arch2a(enum mdoc_arch arch)
}
-char *
+const char *
mdoc_vol2a(enum mdoc_vol vol)
{
@@ -438,7 +438,7 @@ mdoc_vol2a(enum mdoc_vol vol)
}
-char *
+const char *
mdoc_msec2a(enum mdoc_msec msec)
{
diff --git a/term.c b/term.c
index 7686df2e..6489c3a3 100644
--- a/term.c
+++ b/term.c
@@ -25,7 +25,6 @@
#include <unistd.h>
#include "term.h"
-#include "private.h" /* XXX */
enum termstyle {
STYLE_CLEAR,
@@ -42,8 +41,10 @@ static void termprint_footer(struct termp *,
const struct mdoc_meta *);
static void pword(struct termp *, const char *, size_t);
+static void pescape(struct termp *,
+ const char *, size_t *, size_t);
static void chara(struct termp *, char);
-static void escape(struct termp *, enum termstyle);
+static void style(struct termp *, enum termstyle);
void
@@ -185,7 +186,7 @@ chara(struct termp *p, char c)
static void
-escape(struct termp *p, enum termstyle esc)
+style(struct termp *p, enum termstyle esc)
{
if (p->col + 4 >= p->maxcols)
@@ -212,6 +213,48 @@ escape(struct termp *p, enum termstyle esc)
static void
+pescape(struct termp *p, const char *word, size_t *i, size_t len)
+{
+
+ (*i)++;
+ assert(*i < len);
+
+ if ('(' == word[*i]) {
+ /* Two-character escapes. */
+ (*i)++;
+ assert(*i + 1 < len);
+
+ if ('r' == word[*i] && 'B' == word[*i + 1])
+ chara(p, ']');
+ else if ('l' == word[*i] && 'B' == word[*i + 1])
+ chara(p, '[');
+
+ (*i)++;
+ return;
+
+ } else if ('[' != word[*i]) {
+ /* One-character escapes. */
+ switch (word[*i]) {
+ case ('\\'):
+ /* FALLTHROUGH */
+ case ('\''):
+ /* FALLTHROUGH */
+ case ('`'):
+ /* FALLTHROUGH */
+ case ('-'):
+ /* FALLTHROUGH */
+ case ('.'):
+ chara(p, word[*i]);
+ default:
+ break;
+ }
+ return;
+ }
+ /* n-character escapes. */
+}
+
+
+static void
pword(struct termp *p, const char *word, size_t len)
{
size_t i;
@@ -224,18 +267,21 @@ pword(struct termp *p, const char *word, size_t len)
p->flags &= ~TERMP_NOSPACE;
if (p->flags & TERMP_BOLD)
- escape(p, STYLE_BOLD);
+ style(p, STYLE_BOLD);
if (p->flags & TERMP_UNDERLINE)
- escape(p, STYLE_UNDERLINE);
-
- /* TODO: escape patterns. */
+ style(p, STYLE_UNDERLINE);
- for (i = 0; i < len; i++)
+ for (i = 0; i < len; i++) {
+ if ('\\' == word[i]) {
+ pescape(p, word, &i, len);
+ continue;
+ }
chara(p, word[i]);
+ }
if (p->flags & TERMP_BOLD ||
p->flags & TERMP_UNDERLINE)
- escape(p, STYLE_CLEAR);
+ style(p, STYLE_CLEAR);
}
@@ -293,11 +339,9 @@ termprint_r(struct termp *p, const struct mdoc_meta *meta,
/* Post-processing. */
- if (MDOC_TEXT != node->type) {
+ if (MDOC_TEXT != node->type)
if (termacts[node->tok].post)
- if ( ! (*termacts[node->tok].post)(p, meta, node))
- return;
- }
+ (*termacts[node->tok].post)(p, meta, node);
/* Siblings. */
@@ -351,7 +395,8 @@ termprint_footer(struct termp *p, const struct mdoc_meta *meta)
static void
termprint_header(struct termp *p, const struct mdoc_meta *meta)
{
- char *msec, *buf, *title, *pp;
+ char *buf, *title;
+ const char *pp, *msec;
size_t ssz, tsz, ttsz, i;;
if (NULL == (buf = malloc(p->rmargin)))
@@ -439,7 +484,7 @@ termprint_header(struct termp *p, const struct mdoc_meta *meta)
}
-int
+void
termprint(const struct mdoc_node *node,
const struct mdoc_meta *meta)
{
@@ -459,8 +504,6 @@ termprint(const struct mdoc_node *node,
termprint_footer(&p, meta);
free(p.buf);
-
- return(1);
}
diff --git a/term.h b/term.h
index 65491597..c8d6274a 100644
--- a/term.h
+++ b/term.h
@@ -42,15 +42,20 @@ struct termact {
int (*pre)(struct termp *,
const struct mdoc_meta *,
const struct mdoc_node *);
- int (*post)(struct termp *,
+ void (*post)(struct termp *,
const struct mdoc_meta *,
const struct mdoc_node *);
};
+void termprint(const struct mdoc_node *,
+ const struct mdoc_meta *);
+
void newln(struct termp *);
void vspace(struct termp *);
void word(struct termp *, const char *);
void flushln(struct termp *);
+void transcode(struct termp *,
+ const char *, size_t);
const struct termact *termacts;
diff --git a/termact.c b/termact.c
index 7fdc5fd1..12666bbe 100644
--- a/termact.c
+++ b/termact.c
@@ -56,11 +56,10 @@ static int arg_getattr(int, size_t,
const struct mdoc_meta *meta, \
const struct mdoc_node *node
-#define DECL_PREPOST(name, suffix) \
-static int name##_##suffix(DECL_ARGS)
-
-#define DECL_PRE(name) DECL_PREPOST(name, pre)
-#define DECL_POST(name) DECL_PREPOST(name, post)
+#define DECL_PRE(name) \
+static int name##_pre(DECL_ARGS)
+#define DECL_POST(name) \
+static void name##_post(DECL_ARGS)
DECL_PRE(termp_aq);
DECL_PRE(termp_ar);
@@ -216,21 +215,20 @@ termp_dq_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_dq_post(DECL_ARGS)
{
if (MDOC_BODY != node->type)
- return(1);
+ return;
p->flags |= TERMP_NOSPACE;
word(p, "''");
- return(1);
}
/* ARGSUSED */
-static int
+static void
termp_it_post(DECL_ARGS)
{
const struct mdoc_node *n, *it;
@@ -251,7 +249,7 @@ termp_it_post(DECL_ARGS)
case (MDOC_HEAD):
break;
default:
- return(1);
+ return;
}
it = node->parent;
@@ -283,8 +281,6 @@ termp_it_post(DECL_ARGS)
p->flags &= ~TERMP_NOLPAD;
}
}
-
- return(1);
}
@@ -362,22 +358,20 @@ termp_it_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_nm_post(DECL_ARGS)
{
p->flags &= ~ttypes[TTYPE_PROG];
- return(1);
}
/* ARGSUSED */
-static int
+static void
termp_fl_post(DECL_ARGS)
{
p->flags &= ~ttypes[TTYPE_CMD_FLAG];
- return(1);
}
@@ -426,12 +420,11 @@ termp_pp_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_ar_post(DECL_ARGS)
{
p->flags &= ~ttypes[TTYPE_CMD_ARG];
- return(1);
}
@@ -466,40 +459,29 @@ termp_nd_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_bl_post(DECL_ARGS)
{
- switch (node->type) {
- case (MDOC_BLOCK):
+ if (MDOC_BLOCK == node->type)
newln(p);
- break;
- default:
- break;
- }
- return(1);
}
/* ARGSUSED */
-static int
+static void
termp_op_post(DECL_ARGS)
{
- switch (node->type) {
- case (MDOC_BODY):
- p->flags |= TERMP_NOSPACE;
- word(p, "\\]");
- break;
- default:
- break;
- }
- return(1);
+ if (MDOC_BODY != node->type)
+ return;
+ p->flags |= TERMP_NOSPACE;
+ word(p, "\\(rB");
}
/* ARGSUSED */
-static int
+static void
termp_sh_post(DECL_ARGS)
{
@@ -515,7 +497,6 @@ termp_sh_post(DECL_ARGS)
default:
break;
}
- return(1);
}
@@ -536,11 +517,11 @@ termp_xr_pre(DECL_ARGS)
assert(MDOC_TEXT == n->type);
p->flags |= TERMP_NOSPACE;
- word(p, "\\(");
+ word(p, "(");
p->flags |= TERMP_NOSPACE;
word(p, n->data.text.string);
p->flags |= TERMP_NOSPACE;
- word(p, "\\)");
+ word(p, ")");
return(0);
}
@@ -573,7 +554,7 @@ termp_op_pre(DECL_ARGS)
switch (node->type) {
case (MDOC_BODY):
- word(p, "\\[");
+ word(p, "\\(lB");
p->flags |= TERMP_NOSPACE;
break;
default:
@@ -619,15 +600,14 @@ termp_d1_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_d1_post(DECL_ARGS)
{
- if (MDOC_BODY != node->type)
- return(1);
+ if (MDOC_BODY != node->type)
+ return;
newln(p);
p->offset -= 4;
- return(1);
}
@@ -645,15 +625,14 @@ termp_aq_pre(DECL_ARGS)
/* ARGSUSED */
-static int
+static void
termp_aq_post(DECL_ARGS)
{
if (MDOC_BODY != node->type)
- return(1);
+ return;
p->flags |= TERMP_NOSPACE;
word(p, "\\>");
- return(1);
}
diff --git a/tree.c b/tree.c
index 774a01f6..aa3e4c44 100644
--- a/tree.c
+++ b/tree.c
@@ -123,11 +123,10 @@ treeprint_r(const struct mdoc_node *n, int indent)
/* ARGSUSED */
-int
+void
treeprint(const struct mdoc_node *node,
const struct mdoc_meta *meta)
{
treeprint_r(node, 0);
- return(1);
}
diff --git a/validate.c b/validate.c
index 62e09e1e..c397e958 100644
--- a/validate.c
+++ b/validate.c
@@ -35,7 +35,7 @@ typedef int (*v_post)(struct mdoc *);
/* FIXME: some sections should only occur in specific msecs. */
/* FIXME: ignoring Pp. */
/* FIXME: math symbols. */
-/* FIXME: valid character-escape checks. */
+/* FIXME: valid character-escape checks!!!! */
/* FIXME: make sure required sections are included (NAME, ...). */
struct valids {