Import mdocml-1.10.9
authorSascha Wildner <saw@online.de>
Mon, 7 Mar 2011 04:57:19 +0000 (05:57 +0100)
committerSascha Wildner <saw@online.de>
Mon, 7 Mar 2011 04:57:19 +0000 (05:57 +0100)
78 files changed:
contrib/mdocml/ChangeLog.xsl [new file with mode: 0644]
contrib/mdocml/Makefile [new file with mode: 0644]
contrib/mdocml/arch.c [new file with mode: 0644]
contrib/mdocml/arch.in [new file with mode: 0644]
contrib/mdocml/att.c [new file with mode: 0644]
contrib/mdocml/att.in [new file with mode: 0644]
contrib/mdocml/chars.c [new file with mode: 0644]
contrib/mdocml/chars.h [new file with mode: 0644]
contrib/mdocml/chars.in [new file with mode: 0644]
contrib/mdocml/compat.c [new file with mode: 0644]
contrib/mdocml/config.h.post [new file with mode: 0644]
contrib/mdocml/config.h.pre [new file with mode: 0644]
contrib/mdocml/example.style.css [new file with mode: 0644]
contrib/mdocml/external.png [new file with mode: 0644]
contrib/mdocml/html.c [new file with mode: 0644]
contrib/mdocml/html.h [new file with mode: 0644]
contrib/mdocml/index.css [new file with mode: 0644]
contrib/mdocml/index.sgml [new file with mode: 0644]
contrib/mdocml/lib.c [new file with mode: 0644]
contrib/mdocml/lib.in [new file with mode: 0644]
contrib/mdocml/libman.h [new file with mode: 0644]
contrib/mdocml/libmandoc.h [new file with mode: 0644]
contrib/mdocml/libmdoc.h [new file with mode: 0644]
contrib/mdocml/libroff.h [new file with mode: 0644]
contrib/mdocml/main.c [new file with mode: 0644]
contrib/mdocml/main.h [new file with mode: 0644]
contrib/mdocml/man.3 [new file with mode: 0644]
contrib/mdocml/man.7 [new file with mode: 0644]
contrib/mdocml/man.c [new file with mode: 0644]
contrib/mdocml/man.h [new file with mode: 0644]
contrib/mdocml/man_argv.c [new file with mode: 0644]
contrib/mdocml/man_hash.c [new file with mode: 0644]
contrib/mdocml/man_html.c [new file with mode: 0644]
contrib/mdocml/man_macro.c [new file with mode: 0644]
contrib/mdocml/man_term.c [new file with mode: 0644]
contrib/mdocml/man_validate.c [new file with mode: 0644]
contrib/mdocml/mandoc.1 [new file with mode: 0644]
contrib/mdocml/mandoc.c [new file with mode: 0644]
contrib/mdocml/mandoc.h [new file with mode: 0644]
contrib/mdocml/mandoc_char.7 [new file with mode: 0644]
contrib/mdocml/mdoc.3 [new file with mode: 0644]
contrib/mdocml/mdoc.7 [new file with mode: 0644]
contrib/mdocml/mdoc.c [new file with mode: 0644]
contrib/mdocml/mdoc.h [new file with mode: 0644]
contrib/mdocml/mdoc_argv.c [new file with mode: 0644]
contrib/mdocml/mdoc_hash.c [new file with mode: 0644]
contrib/mdocml/mdoc_html.c [new file with mode: 0644]
contrib/mdocml/mdoc_macro.c [new file with mode: 0644]
contrib/mdocml/mdoc_strings.c [new file with mode: 0644]
contrib/mdocml/mdoc_term.c [new file with mode: 0644]
contrib/mdocml/mdoc_validate.c [new file with mode: 0644]
contrib/mdocml/msec.c [new file with mode: 0644]
contrib/mdocml/msec.in [new file with mode: 0644]
contrib/mdocml/out.c [new file with mode: 0644]
contrib/mdocml/out.h [new file with mode: 0644]
contrib/mdocml/roff.3 [new file with mode: 0644]
contrib/mdocml/roff.7 [new file with mode: 0644]
contrib/mdocml/roff.c [new file with mode: 0644]
contrib/mdocml/roff.h [new file with mode: 0644]
contrib/mdocml/st.c [new file with mode: 0644]
contrib/mdocml/st.in [new file with mode: 0644]
contrib/mdocml/style.css [new file with mode: 0644]
contrib/mdocml/tbl.7 [new file with mode: 0644]
contrib/mdocml/tbl.c [new file with mode: 0644]
contrib/mdocml/tbl_data.c [new file with mode: 0644]
contrib/mdocml/tbl_html.c [new file with mode: 0644]
contrib/mdocml/tbl_layout.c [new file with mode: 0644]
contrib/mdocml/tbl_opts.c [new file with mode: 0644]
contrib/mdocml/tbl_term.c [new file with mode: 0644]
contrib/mdocml/term.c [new file with mode: 0644]
contrib/mdocml/term.h [new file with mode: 0644]
contrib/mdocml/term_ascii.c [new file with mode: 0644]
contrib/mdocml/term_ps.c [new file with mode: 0644]
contrib/mdocml/test-strlcat.c [new file with mode: 0644]
contrib/mdocml/test-strlcpy.c [new file with mode: 0644]
contrib/mdocml/tree.c [new file with mode: 0644]
contrib/mdocml/vol.c [new file with mode: 0644]
contrib/mdocml/vol.in [new file with mode: 0644]

diff --git a/contrib/mdocml/ChangeLog.xsl b/contrib/mdocml/ChangeLog.xsl
new file mode 100644 (file)
index 0000000..dccc79d
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version='1.0' encoding="utf-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
+<xsl:output encoding="utf-8" method="html" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
+<xsl:template match="/changelog">
+<html>
+       <head>
+               <title>mdocml - CVS-ChangeLog</title>
+               <link rel="stylesheet" href="index.css" type="text/css" media="all" />
+       </head>
+       <body>
+                               <xsl:for-each select="entry">
+                                       <div class="clhead">
+                                               <xsl:text>Files modified by </xsl:text>
+                                               <xsl:value-of select="concat(author, ': ', date, ' (', time, ')')" />
+                                       </div>
+                                       <div class="clbody">
+                                               <strong>
+                                                       <xsl:text>Note: </xsl:text>
+                                               </strong>
+                                               <xsl:value-of select="msg"/>
+                                               <ul class="clbody">
+                                                       <xsl:for-each select="file">
+                                                               <li>
+                                                                       <xsl:value-of select="name"/>
+                                                                       <span class="rev">
+                                                                               <xsl:text> &#8212; Rev: </xsl:text>
+                                                                               <xsl:value-of select="revision"/>
+                                                                               <xsl:text>, Status: </xsl:text>
+                                                                               <xsl:value-of select="cvsstate"/>
+                                                                               <xsl:if test="tag">
+                                                                                       <xsl:text>, Tag: </xsl:text>
+                                                                                       <xsl:value-of select="tag" />
+                                                                               </xsl:if>
+                                                                       </span>
+                                                               </li>
+                                                       </xsl:for-each>
+                                               </ul>
+                                       </div>
+                               </xsl:for-each>
+       </body>
+</html>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/contrib/mdocml/Makefile b/contrib/mdocml/Makefile
new file mode 100644 (file)
index 0000000..2361592
--- /dev/null
@@ -0,0 +1,345 @@
+.SUFFIXES:     .html .xml .sgml .1 .3 .7 .md5 .tar.gz 
+.SUFFIXES:     .1.txt .3.txt .7.txt
+.SUFFIXES:     .1.xhtml .3.xhtml .7.xhtml
+.SUFFIXES:     .1.sgml .3.sgml .7.sgml 
+.SUFFIXES:     .h .h.html 
+.SUFFIXES:     .1.ps .3.ps .7.ps
+.SUFFIXES:     .1.pdf .3.pdf .7.pdf
+
+PREFIX         = /usr/local
+BINDIR         = $(PREFIX)/bin
+INCLUDEDIR     = $(PREFIX)/include
+LIBDIR         = $(PREFIX)/lib
+MANDIR         = $(PREFIX)/man
+EXAMPLEDIR     = $(PREFIX)/share/examples/mandoc
+INSTALL                = install
+INSTALL_PROGRAM        = $(INSTALL) -m 0755
+INSTALL_DATA   = $(INSTALL) -m 0444
+INSTALL_LIB    = $(INSTALL) -m 0644
+INSTALL_MAN    = $(INSTALL_DATA)
+
+VERSION           = 1.10.9
+VDATE     = 07 January 2010
+
+VFLAGS    = -DVERSION="\"$(VERSION)\""
+WFLAGS     = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
+CFLAGS    += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H
+
+# Specify this if you want to hard-code the operating system to appear
+# in the lower-left hand corner of -mdoc manuals.
+# CFLAGS += -DOSNAME="\"OpenBSD 4.5\""
+
+LINTFLAGS += $(VFLAGS)
+
+ROFFLNS    = roff.ln tbl.ln tbl_opts.ln tbl_layout.ln tbl_data.ln
+
+ROFFSRCS   = roff.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c
+
+ROFFOBJS   = roff.o tbl.o tbl_opts.o tbl_layout.o tbl_data.o
+
+MANDOCLNS  = mandoc.ln
+
+MANDOCSRCS = mandoc.c
+
+MANDOCOBJS = mandoc.o
+
+MDOCLNS           = mdoc_macro.ln mdoc.ln mdoc_hash.ln mdoc_strings.ln \
+            mdoc_argv.ln mdoc_validate.ln \
+            lib.ln att.ln arch.ln vol.ln msec.ln st.ln
+
+MDOCOBJS   = mdoc_macro.o mdoc.o mdoc_hash.o mdoc_strings.o \
+            mdoc_argv.o mdoc_validate.o lib.o att.o \
+            arch.o vol.o msec.o st.o
+
+MDOCSRCS   = mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c \
+            mdoc_argv.c mdoc_validate.c lib.c att.c \
+            arch.c vol.c msec.c st.c
+
+MANLNS    = man_macro.ln man.ln man_hash.ln man_validate.ln \
+            man_argv.ln
+
+MANOBJS           = man_macro.o man.o man_hash.o man_validate.o \
+            man_argv.o
+MANSRCS           = man_macro.c man.c man_hash.c man_validate.c \
+            man_argv.c
+
+MAINLNS           = main.ln mdoc_term.ln chars.ln term.ln tree.ln \
+            compat.ln man_term.ln html.ln mdoc_html.ln \
+            man_html.ln out.ln term_ps.ln term_ascii.ln \
+            tbl_term.ln tbl_html.ln
+
+MAINOBJS   = main.o mdoc_term.o chars.o term.o tree.o compat.o \
+            man_term.o html.o mdoc_html.o man_html.o out.o \
+            term_ps.o term_ascii.o tbl_term.o tbl_html.o
+
+MAINSRCS   = main.c mdoc_term.c chars.c term.c tree.c compat.c \
+            man_term.c html.c mdoc_html.c man_html.c out.c \
+            term_ps.c term_ascii.c tbl_term.c tbl_html.c
+
+LLNS      = llib-llibmdoc.ln llib-llibman.ln llib-lmandoc.ln \
+            llib-llibmandoc.ln llib-llibroff.ln
+
+LNS       = $(MAINLNS) $(MDOCLNS) $(MANLNS) \
+            $(MANDOCLNS) $(ROFFLNS)
+
+LIBS      = libmdoc.a libman.a libmandoc.a libroff.a
+
+OBJS      = $(MDOCOBJS) $(MAINOBJS) $(MANOBJS) \
+            $(MANDOCOBJS) $(ROFFOBJS)
+
+SRCS      = $(MDOCSRCS) $(MAINSRCS) $(MANSRCS) \
+            $(MANDOCSRCS) $(ROFFSRCS)
+
+DATAS     = arch.in att.in lib.in msec.in st.in \
+            vol.in chars.in
+
+HEADS     = mdoc.h libmdoc.h man.h libman.h term.h \
+            libmandoc.h html.h chars.h out.h main.h roff.h \
+            mandoc.h libroff.h
+
+GSGMLS    = mandoc.1.sgml mdoc.3.sgml mdoc.7.sgml \
+            mandoc_char.7.sgml man.7.sgml man.3.sgml roff.7.sgml \
+            roff.3.sgml tbl.7.sgml
+
+SGMLS     = index.sgml
+
+XHTMLS    = mandoc.1.xhtml mdoc.3.xhtml \
+            man.3.xhtml mdoc.7.xhtml man.7.xhtml mandoc_char.7.xhtml \
+            roff.7.xhtml roff.3.xhtml tbl.7.xhtml
+
+HTMLS     = ChangeLog.html index.html man.h.html mdoc.h.html \
+            mandoc.h.html roff.h.html mandoc.1.html mdoc.3.html \
+            man.3.html mdoc.7.html man.7.html mandoc_char.7.html \
+            roff.7.html roff.3.html tbl.7.html
+
+PSS       = mandoc.1.ps mdoc.3.ps man.3.ps mdoc.7.ps man.7.ps \
+            mandoc_char.7.ps roff.7.ps roff.3.ps tbl.7.ps
+
+PDFS      = mandoc.1.pdf mdoc.3.pdf man.3.pdf mdoc.7.pdf man.7.pdf \
+            mandoc_char.7.pdf roff.7.pdf roff.3.pdf tbl.7.pdf
+
+XSLS      = ChangeLog.xsl
+
+TEXTS     = mandoc.1.txt mdoc.3.txt man.3.txt mdoc.7.txt man.7.txt \
+            mandoc_char.7.txt ChangeLog.txt \
+            roff.7.txt roff.3.txt tbl.7.txt
+
+EXAMPLES   = example.style.css
+
+XMLS      = ChangeLog.xml
+
+STATICS           = index.css style.css external.png
+
+MD5S      = mdocml-$(VERSION).md5 
+
+TARGZS    = mdocml-$(VERSION).tar.gz
+
+MANS      = mandoc.1 mdoc.3 mdoc.7 mandoc_char.7 man.7 \
+            man.3 roff.7 roff.3 tbl.7
+
+BINS      = mandoc
+
+TESTS     = test-strlcat.c test-strlcpy.c
+
+CONFIGS           = config.h.pre config.h.post
+
+DOCLEAN           = $(BINS) $(LNS) $(LLNS) $(LIBS) $(OBJS) $(HTMLS) \
+            $(TARGZS) tags $(MD5S) $(XMLS) $(TEXTS) $(GSGMLS) \
+            config.h config.log $(PSS) $(PDFS) $(XHTMLS)
+
+DOINSTALL  = $(SRCS) $(HEADS) Makefile $(MANS) $(SGMLS) $(STATICS) \
+            $(DATAS) $(XSLS) $(EXAMPLES) $(TESTS) $(CONFIGS)
+
+all:   $(BINS)
+
+lint:  $(LLNS)
+
+clean:
+       rm -f $(DOCLEAN)
+
+dist:  mdocml-$(VERSION).tar.gz
+
+www:   all $(GSGMLS) $(HTMLS) $(XHTMLS) $(TEXTS) $(MD5S) $(TARGZS) $(PSS) $(PDFS)
+
+ps:    $(PSS)
+
+pdf:   $(PDFS)
+
+installwww: www
+       $(INSTALL_DATA) $(HTMLS) $(XHTMLS) $(PSS) $(PDFS) $(TEXTS) $(STATICS) $(DESTDIR)$(PREFIX)/
+       $(INSTALL_DATA) mdocml-$(VERSION).tar.gz $(DESTDIR)$(PREFIX)/snapshots/
+       $(INSTALL_DATA) mdocml-$(VERSION).md5 $(DESTDIR)$(PREFIX)/snapshots/
+       $(INSTALL_DATA) mdocml-$(VERSION).tar.gz $(DESTDIR)$(PREFIX)/snapshots/mdocml.tar.gz
+       $(INSTALL_DATA) mdocml-$(VERSION).md5 $(DESTDIR)$(PREFIX)/snapshots/mdocml.md5
+
+install:
+       mkdir -p $(DESTDIR)$(BINDIR)
+       mkdir -p $(DESTDIR)$(EXAMPLEDIR)
+       mkdir -p $(DESTDIR)$(MANDIR)/man1
+       mkdir -p $(DESTDIR)$(MANDIR)/man7
+       $(INSTALL_PROGRAM) mandoc $(DESTDIR)$(BINDIR)
+       $(INSTALL_MAN) mandoc.1 $(DESTDIR)$(MANDIR)/man1
+       $(INSTALL_MAN) man.7 mdoc.7 roff.7 tbl.7 mandoc_char.7 $(DESTDIR)$(MANDIR)/man7
+       $(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR)
+
+uninstall:
+       rm -f $(DESTDIR)$(BINDIR)/mandoc
+       rm -f $(DESTDIR)$(MANDIR)/man1/mandoc.1
+       rm -f $(DESTDIR)$(MANDIR)/man7/mdoc.7
+       rm -f $(DESTDIR)$(MANDIR)/man7/roff.7
+       rm -f $(DESTDIR)$(MANDIR)/man7/tbl.7
+       rm -f $(DESTDIR)$(MANDIR)/man7/man.7
+       rm -f $(DESTDIR)$(MANDIR)/man7/mandoc_char.7
+       rm -f $(DESTDIR)$(EXAMPLEDIR)/example.style.css
+
+$(OBJS): config.h
+
+$(LNS): config.h
+
+man_macro.ln man_macro.o: man_macro.c libman.h
+
+lib.ln lib.o: lib.c lib.in libmdoc.h
+
+att.ln att.o: att.c att.in libmdoc.h
+
+arch.ln arch.o: arch.c arch.in libmdoc.h
+
+vol.ln vol.o: vol.c vol.in libmdoc.h
+
+chars.ln chars.o: chars.c chars.in chars.h
+
+msec.ln msec.o: msec.c msec.in libmdoc.h
+
+st.ln st.o: st.c st.in libmdoc.h
+
+mdoc_macro.ln mdoc_macro.o: mdoc_macro.c libmdoc.h
+
+mdoc_term.ln mdoc_term.o: mdoc_term.c term.h mdoc.h
+
+mdoc_strings.ln mdoc_strings.o: mdoc_strings.c libmdoc.h
+
+man_hash.ln man_hash.o: man_hash.c libman.h
+
+mdoc_hash.ln mdoc_hash.o: mdoc_hash.c libmdoc.h
+
+mdoc.ln mdoc.o: mdoc.c libmdoc.h
+
+man.ln man.o: man.c libman.h
+
+main.ln main.o: main.c mdoc.h man.h roff.h
+
+compat.ln compat.o: compat.c 
+
+term.ln term.o: term.c term.h man.h mdoc.h chars.h
+
+term_ps.ln term_ps.o: term_ps.c term.h main.h
+
+term_ascii.ln term_ascii.o: term_ascii.c term.h main.h
+
+html.ln html.o: html.c html.h chars.h
+
+mdoc_html.ln mdoc_html.o: mdoc_html.c html.h mdoc.h
+
+man_html.ln man_html.o: man_html.c html.h man.h out.h
+
+out.ln out.o: out.c out.h
+
+mandoc.ln mandoc.o: mandoc.c libmandoc.h
+
+tree.ln tree.o: tree.c man.h mdoc.h
+
+mdoc_argv.ln mdoc_argv.o: mdoc_argv.c libmdoc.h
+
+man_argv.ln man_argv.o: man_argv.c libman.h
+
+man_validate.ln man_validate.o: man_validate.c libman.h
+
+mdoc_validate.ln mdoc_validate.o: mdoc_validate.c libmdoc.h
+
+libmdoc.h: mdoc.h
+
+ChangeLog.xml:
+       cvs2cl --xml --xml-encoding iso-8859-15 -t --noxmlns -f $@
+
+ChangeLog.txt:
+       cvs2cl -t -f $@
+
+ChangeLog.html: ChangeLog.xml ChangeLog.xsl
+       xsltproc -o $@ ChangeLog.xsl ChangeLog.xml
+
+mdocml-$(VERSION).tar.gz: $(DOINSTALL)
+       mkdir -p .dist/mdocml/mdocml-$(VERSION)/
+       cp -f $(DOINSTALL) .dist/mdocml/mdocml-$(VERSION)/
+       ( cd .dist/mdocml/ && tar zcf ../../$@ mdocml-$(VERSION)/ )
+       rm -rf .dist/
+
+llib-llibmdoc.ln: $(MDOCLNS)
+       $(LINT) -Clibmdoc $(MDOCLNS)
+
+llib-llibman.ln: $(MANLNS)
+       $(LINT) -Clibman $(MANLNS)
+
+llib-llibmandoc.ln: $(MANDOCLNS)
+       $(LINT) -Clibmandoc $(MANDOCLNS)
+
+llib-llibroff.ln: $(ROFFLNS)
+       $(LINT) -Clibroff $(ROFFLNS)
+
+llib-lmandoc.ln: $(MAINLNS) llib-llibmdoc.ln llib-llibman.ln llib-llibmandoc.ln llib-llibroff.ln
+       $(LINT) -Cmandoc $(MAINLNS) llib-llibmdoc.ln llib-llibman.ln llib-llibmandoc.ln llib-llibroff.ln
+
+libmdoc.a: $(MDOCOBJS)
+       $(AR) rs $@ $(MDOCOBJS)
+
+libman.a: $(MANOBJS)
+       $(AR) rs $@ $(MANOBJS)
+
+libmandoc.a: $(MANDOCOBJS)
+       $(AR) rs $@ $(MANDOCOBJS)
+
+libroff.a: $(ROFFOBJS)
+       $(AR) rs $@ $(ROFFOBJS)
+
+mandoc: $(MAINOBJS) libroff.a libmdoc.a libman.a libmandoc.a
+       $(CC) $(CFLAGS) -o $@ $(MAINOBJS) libroff.a libmdoc.a libman.a libmandoc.a
+
+.sgml.html:
+       validate --warn $<
+       sed -e "s!@VERSION@!$(VERSION)!" -e "s!@VDATE@!$(VDATE)!" $< > $@
+
+.1.1.txt .3.3.txt .7.7.txt:
+       ./mandoc -Tascii -Wall,stop $< | col -b > $@
+
+.1.1.sgml .3.3.sgml .7.7.sgml:
+       ./mandoc -Thtml -Wall,stop -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< > $@
+
+.1.1.ps .3.3.ps .7.7.ps:
+       ./mandoc -Tps -Wall,stop $< > $@
+
+.1.1.xhtml .3.3.xhtml .7.7.xhtml:
+       ./mandoc -Txhtml -Wall,stop -Ostyle=style.css,man=%N.%S.xhtml,includes=%I.html $< > $@
+
+.1.1.pdf .3.3.pdf .7.7.pdf:
+       ./mandoc -Tpdf -Wall,stop $< > $@
+
+.tar.gz.md5:
+       md5 $< > $@
+
+.h.h.html:
+       highlight -I $< >$@
+
+config.h: config.h.pre config.h.post
+       rm -f config.log
+       ( cat config.h.pre; \
+       echo; \
+       if $(CC) $(CFLAGS) -Werror -c test-strlcat.c >> config.log 2>&1; then \
+               echo '#define HAVE_STRLCAT'; \
+               rm test-strlcat.o; \
+       fi; \
+       if $(CC) $(CFLAGS) -Werror -c test-strlcpy.c >> config.log 2>&1; then \
+               echo '#define HAVE_STRLCPY'; \
+               rm test-strlcpy.o; \
+       fi; \
+       echo; \
+       cat config.h.post \
+       ) > $@
diff --git a/contrib/mdocml/arch.c b/contrib/mdocml/arch.c
new file mode 100644 (file)
index 0000000..c3d7634
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $Id: arch.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "mandoc.h"
+#include "libmdoc.h"
+
+#define LINE(x, y) \
+       if (0 == strcmp(p, x)) return(y);
+
+const char *
+mdoc_a2arch(const char *p)
+{
+
+#include "arch.in" 
+
+       return(NULL);
+}
diff --git a/contrib/mdocml/arch.in b/contrib/mdocml/arch.in
new file mode 100644 (file)
index 0000000..41543a9
--- /dev/null
@@ -0,0 +1,56 @@
+/*     $Id: arch.in,v 1.10 2010/09/27 06:56:44 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+
+/*
+ * This file defines the architecture token of the .Dt prologue macro.
+ * All architectures that your system supports (or the manuals of your
+ * system) should be included here.  The right-hand-side is the
+ * formatted output.
+ *
+ * Be sure to escape strings.
+ *
+ * REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7!
+ */
+
+LINE("alpha",          "Alpha")
+LINE("amd64",          "AMD64")
+LINE("amiga",          "Amiga")
+LINE("arc",            "ARC")
+LINE("arm",            "ARM")
+LINE("armish",         "ARMISH")
+LINE("aviion",         "AViiON")
+LINE("hp300",          "HP300")
+LINE("hppa",           "HPPA")
+LINE("hppa64",         "HPPA64")
+LINE("i386",           "i386")
+LINE("landisk",                "LANDISK")
+LINE("loongson",       "Loongson")
+LINE("luna88k",                "Luna88k")
+LINE("mac68k",         "Mac68k")
+LINE("macppc",         "MacPPC")
+LINE("mips64",         "MIPS64")
+LINE("mvme68k",                "MVME68k")
+LINE("mvme88k",                "MVME88k")
+LINE("mvmeppc",                "MVMEPPC")
+LINE("pmax",           "PMAX")
+LINE("sgi",            "SGI")
+LINE("socppc",         "SOCPPC")
+LINE("sparc",          "SPARC")
+LINE("sparc64",                "SPARC64")
+LINE("sun3",           "Sun3")
+LINE("vax",            "VAX")
+LINE("zaurus",         "Zaurus")
diff --git a/contrib/mdocml/att.c b/contrib/mdocml/att.c
new file mode 100644 (file)
index 0000000..cfd6b44
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $Id: att.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "mandoc.h"
+#include "libmdoc.h"
+
+#define LINE(x, y) \
+       if (0 == strcmp(p, x)) return(y);
+
+const char *
+mdoc_a2att(const char *p)
+{
+
+#include "att.in" 
+
+       return(NULL);
+}
diff --git a/contrib/mdocml/att.in b/contrib/mdocml/att.in
new file mode 100644 (file)
index 0000000..48fcd30
--- /dev/null
@@ -0,0 +1,37 @@
+/*     $Id: att.in,v 1.6 2010/06/19 20:46:27 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+
+/*
+ * This file defines the AT&T versions of the .At macro.  This probably
+ * isn't going to change.  The right-hand side is the formatted string.
+ *
+ * Be sure to escape strings.
+ */
+
+LINE("v1",             "Version 1 AT&T UNIX")
+LINE("v2",             "Version 2 AT&T UNIX")
+LINE("v3",             "Version 3 AT&T UNIX")
+LINE("v4",             "Version 4 AT&T UNIX")
+LINE("v5",             "Version 5 AT&T UNIX")
+LINE("v6",             "Version 6 AT&T UNIX")
+LINE("v7",             "Version 7 AT&T UNIX")
+LINE("32v",            "Version 32V AT&T UNIX")
+LINE("V",              "AT&T System V UNIX")
+LINE("V.1",            "AT&T System V.1 UNIX")
+LINE("V.2",            "AT&T System V.2 UNIX")
+LINE("V.3",            "AT&T System V.3 UNIX")
+LINE("V.4",            "AT&T System V.4 UNIX")
diff --git a/contrib/mdocml/chars.c b/contrib/mdocml/chars.c
new file mode 100644 (file)
index 0000000..5edf947
--- /dev/null
@@ -0,0 +1,243 @@
+/*     $Id: chars.c,v 1.31 2011/01/02 10:10:57 kristaps Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "chars.h"
+
+#define        PRINT_HI         126
+#define        PRINT_LO         32
+
+struct ln {
+       struct ln        *next;
+       const char       *code;
+       const char       *ascii;
+       int               unicode;
+       int               type;
+#define        CHARS_CHAR       (1 << 0)
+#define        CHARS_STRING     (1 << 1)
+#define CHARS_BOTH      (CHARS_CHAR | CHARS_STRING)
+};
+
+#define        LINES_MAX         351
+
+#define CHAR(in, ch, code) \
+       { NULL, (in), (ch), (code), CHARS_CHAR },
+#define STRING(in, ch, code) \
+       { NULL, (in), (ch), (code), CHARS_STRING },
+#define BOTH(in, ch, code) \
+       { NULL, (in), (ch), (code), CHARS_BOTH },
+
+#define        CHAR_TBL_START    static struct ln lines[LINES_MAX] = {
+#define        CHAR_TBL_END      };
+
+#include "chars.in"
+
+struct ctab {
+       enum chars        type;
+       struct ln       **htab;
+};
+
+static inline int        match(const struct ln *,
+                               const char *, size_t, int);
+static const struct ln  *find(struct ctab *, const char *, size_t, int);
+
+
+void
+chars_free(void *arg)
+{
+       struct ctab     *tab;
+
+       tab = (struct ctab *)arg;
+
+       free(tab->htab);
+       free(tab);
+}
+
+
+void *
+chars_init(enum chars type)
+{
+       struct ctab      *tab;
+       struct ln       **htab;
+       struct ln        *pp;
+       int               i, hash;
+
+       /*
+        * Constructs a very basic chaining hashtable.  The hash routine
+        * is simply the integral value of the first character.
+        * Subsequent entries are chained in the order they're processed
+        * (they're in-line re-ordered during lookup).
+        */
+
+       tab = malloc(sizeof(struct ctab));
+       if (NULL == tab) {
+               perror(NULL);
+               exit((int)MANDOCLEVEL_SYSERR);
+       }
+
+       htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
+       if (NULL == htab) {
+               perror(NULL);
+               exit((int)MANDOCLEVEL_SYSERR);
+       }
+
+       for (i = 0; i < LINES_MAX; i++) {
+               hash = (int)lines[i].code[0] - PRINT_LO;
+
+               if (NULL == (pp = htab[hash])) {
+                       htab[hash] = &lines[i];
+                       continue;
+               }
+
+               for ( ; pp->next; pp = pp->next)
+                       /* Scan ahead. */ ;
+               pp->next = &lines[i];
+       }
+
+       tab->htab = htab;
+       tab->type = type;
+       return(tab);
+}
+
+
+/* 
+ * Special character to Unicode codepoint.
+ */
+int
+chars_spec2cp(void *arg, const char *p, size_t sz)
+{
+       const struct ln *ln;
+
+       ln = find((struct ctab *)arg, p, sz, CHARS_CHAR);
+       if (NULL == ln)
+               return(-1);
+       return(ln->unicode);
+}
+
+
+/* 
+ * Reserved word to Unicode codepoint.
+ */
+int
+chars_res2cp(void *arg, const char *p, size_t sz)
+{
+       const struct ln *ln;
+
+       ln = find((struct ctab *)arg, p, sz, CHARS_STRING);
+       if (NULL == ln)
+               return(-1);
+       return(ln->unicode);
+}
+
+
+/* 
+ * Special character to string array.
+ */
+const char *
+chars_spec2str(void *arg, const char *p, size_t sz, size_t *rsz)
+{
+       const struct ln *ln;
+
+       ln = find((struct ctab *)arg, p, sz, CHARS_CHAR);
+       if (NULL == ln)
+               return(NULL);
+
+       *rsz = strlen(ln->ascii);
+       return(ln->ascii);
+}
+
+
+/* 
+ * Reserved word to string array.
+ */
+const char *
+chars_res2str(void *arg, const char *p, size_t sz, size_t *rsz)
+{
+       const struct ln *ln;
+
+       ln = find((struct ctab *)arg, p, sz, CHARS_STRING);
+       if (NULL == ln)
+               return(NULL);
+
+       *rsz = strlen(ln->ascii);
+       return(ln->ascii);
+}
+
+
+static const struct ln *
+find(struct ctab *tab, const char *p, size_t sz, int type)
+{
+       struct ln        *pp, *prev;
+       struct ln       **htab;
+       int               hash;
+
+       assert(p);
+       if (0 == sz)
+               return(NULL);
+
+       if (p[0] < PRINT_LO || p[0] > PRINT_HI)
+               return(NULL);
+
+       /*
+        * Lookup the symbol in the symbol hash.  See ascii2htab for the
+        * hashtable specs.  This dynamically re-orders the hash chain
+        * to optimise for repeat hits.
+        */
+
+       hash = (int)p[0] - PRINT_LO;
+       htab = tab->htab;
+
+       if (NULL == (pp = htab[hash]))
+               return(NULL);
+
+       for (prev = NULL; pp; pp = pp->next) {
+               if ( ! match(pp, p, sz, type)) {
+                       prev = pp;
+                       continue;
+               }
+
+               if (prev) {
+                       prev->next = pp->next;
+                       pp->next = htab[hash];
+                       htab[hash] = pp;
+               }
+
+               return(pp);
+       }
+
+       return(NULL);
+}
+
+
+static inline int
+match(const struct ln *ln, const char *p, size_t sz, int type)
+{
+
+       if ( ! (ln->type & type))
+               return(0);
+       if (strncmp(ln->code, p, sz))
+               return(0);
+       return('\0' == ln->code[(int)sz]);
+}
diff --git a/contrib/mdocml/chars.h b/contrib/mdocml/chars.h
new file mode 100644 (file)
index 0000000..894008b
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $Id: chars.h,v 1.6 2010/07/31 23:52:58 schwarze Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef CHARS_H
+#define CHARS_H
+
+__BEGIN_DECLS
+
+enum   chars {
+       CHARS_ASCII,
+       CHARS_HTML
+};
+
+void            *chars_init(enum chars);
+const char      *chars_spec2str(void *, const char *, size_t, size_t *);
+int              chars_spec2cp(void *, const char *, size_t);
+const char      *chars_res2str(void *, const char *, size_t, size_t *);
+int              chars_res2cp(void *, const char *, size_t);
+void             chars_free(void *);
+
+__END_DECLS
+
+#endif /*!CHARS_H*/
diff --git a/contrib/mdocml/chars.in b/contrib/mdocml/chars.in
new file mode 100644 (file)
index 0000000..63d9fb4
--- /dev/null
@@ -0,0 +1,425 @@
+/*     $Id: chars.in,v 1.35 2010/09/15 13:10:30 kristaps Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+
+/*
+ * The ASCII translation tables.  STRING corresponds to predefined
+ * strings (cf. mdoc_samples.7 and tmac/mdoc/doc-nroff).  CHAR
+ * corresponds to special characters (cf. groff_char.7).  BOTH contains
+ * sequences that are equivalent in both STRING and CHAR.
+ *
+ * Either way, the left-hand side corresponds to the input sequence (\x,
+ * \(xx, \*(xx and so on) whose length is listed second element.  The
+ * right-hand side is what's produced by the front-end, with the fourth
+ * element being its length.
+ *
+ * XXX - C-escape strings!
+ * XXX - update LINES_MAX if adding more!
+ */
+
+/* Non-breaking, non-collapsing space uses unit separator. */
+static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' };
+
+CHAR_TBL_START
+
+/* Spacing. */
+CHAR("c",                      "",             0)
+CHAR("0",                      " ",            8194)
+CHAR(" ",                      ascii_nbrsp,    160)
+CHAR("~",                      ascii_nbrsp,    160)
+CHAR("%",                      "",             0)
+CHAR("&",                      "",             0)
+CHAR("^",                      "",             0)
+CHAR("|",                      "",             0)
+CHAR("}",                      "",             0)
+
+/* Accents. */
+CHAR("a\"",                    "\"",           779)
+CHAR("a-",                     "-",            175)
+CHAR("a.",                     ".",            729)
+CHAR("a^",                     "^",            770)
+BOTH("\'",                     "\'",           769)
+BOTH("aa",                     "\'",           769)
+BOTH("ga",                     "`",            768)
+BOTH("`",                      "`",            768)
+CHAR("ab",                     "`",            774)
+CHAR("ac",                     ",",            807)
+CHAR("ad",                     "\"",           776)
+CHAR("ah",                     "v",            711)
+CHAR("ao",                     "o",            730)
+CHAR("a~",                     "~",            771)
+CHAR("ho",                     ",",            808)
+CHAR("ha",                     "^",            94)
+CHAR("ti",                     "~",            126)
+
+/* Quotes. */
+CHAR("Bq",                     ",,",           8222)
+CHAR("bq",                     ",",            8218)
+BOTH("lq",                     "``",           8220)
+BOTH("rq",                     "\'\'",         8221)
+CHAR("oq",                     "`",            8216)
+CHAR("cq",                     "\'",           8217)
+CHAR("aq",                     "\'",           39)
+CHAR("dq",                     "\"",           34)
+CHAR("Fo",                     "<<",           171)
+CHAR("Fc",                     ">>",           187)
+CHAR("fo",                     "<",            8249)
+CHAR("fc",                     ">",            8250)
+
+/* Brackets. */
+CHAR("lB",                     "[",            91)
+CHAR("rB",                     "]",            93)
+CHAR("lC",                     "{",            123)
+CHAR("rC",                     "}",            125)
+CHAR("la",                     "<",            60)
+CHAR("ra",                     ">",            62)
+CHAR("bv",                     "|",            9130)
+CHAR("braceex",                        "|",            9130)
+CHAR("bracketlefttp",          "|",            9121)
+CHAR("bracketleftbp",          "|",            9123)
+CHAR("bracketleftex",          "|",            9122)
+CHAR("bracketrighttp",         "|",            9124)
+CHAR("bracketrightbp",         "|",            9126)
+CHAR("bracketrightex",         "|",            9125)
+CHAR("lt",                     ",-",           9127)
+CHAR("bracelefttp",            ",-",           9127)
+CHAR("lk",                     "{",            9128)
+CHAR("braceleftmid",           "{",            9128)
+CHAR("lb",                     ",-",           9129)
+CHAR("braceleftbp",            "`-",           9129)
+CHAR("braceleftex",            "|",            9130)
+CHAR("rt",                     "-.",           9131)
+CHAR("bracerighttp",           "-.",           9131)
+CHAR("rk",                     "}",            9132)
+CHAR("bracerightmid",          "}",            9132)
+CHAR("rb",                     "-\'",          9133)
+CHAR("bracerightbp",           "-\'",          9133)
+CHAR("bracerightex",           "|",            9130)
+CHAR("parenlefttp",            "/",            9115)
+CHAR("parenleftbp",            "\\",           9117)
+CHAR("parenleftex",            "|",            9116)
+CHAR("parenrighttp",           "\\",           9118)
+CHAR("parenrightbp",           "/",            9120)
+CHAR("parenrightex",           "|",            9119)
+
+/* Greek characters. */
+CHAR("*A",                     "A",            913)
+CHAR("*B",                     "B",            914)
+CHAR("*G",                     "|",            915)
+CHAR("*D",                     "/\\",          916)
+CHAR("*E",                     "E",            917)
+CHAR("*Z",                     "Z",            918)
+CHAR("*Y",                     "H",            919)
+CHAR("*H",                     "O",            920)
+CHAR("*I",                     "I",            921)
+CHAR("*K",                     "K",            922)
+CHAR("*L",                     "/\\",          923)
+CHAR("*M",                     "M",            924)
+CHAR("*N",                     "N",            925)
+CHAR("*C",                     "H",            926)
+CHAR("*O",                     "O",            927)
+CHAR("*P",                     "TT",           928)
+CHAR("*R",                     "P",            929)
+CHAR("*S",                     ">",            931)
+CHAR("*T",                     "T",            932)
+CHAR("*U",                     "Y",            933)
+CHAR("*F",                     "O_",           934)
+CHAR("*X",                     "X",            935)
+CHAR("*Q",                     "Y",            936)
+CHAR("*W",                     "O",            937)
+CHAR("*a",                     "a",            945)
+CHAR("*b",                     "B",            946)
+CHAR("*g",                     "y",            947)
+CHAR("*d",                     "d",            948)
+CHAR("*e",                     "e",            949)
+CHAR("*z",                     "C",            950)
+CHAR("*y",                     "n",            951)
+CHAR("*h",                     "0",            952)
+CHAR("*i",                     "i",            953)
+CHAR("*k",                     "k",            954)
+CHAR("*l",                     "\\",           955)
+CHAR("*m",                     "u",            956)
+CHAR("*n",                     "v",            957)
+CHAR("*c",                     "E",            958)
+CHAR("*o",                     "o",            959)
+CHAR("*p",                     "n",            960)
+CHAR("*r",                     "p",            961)
+CHAR("*s",                     "o",            963)
+CHAR("*t",                     "t",            964)
+CHAR("*u",                     "u",            965)
+CHAR("*f",                     "o",            981)
+CHAR("*x",                     "x",            967)
+CHAR("*q",                     "u",            968)
+CHAR("*w",                     "w",            969)
+CHAR("+h",                     "0",            977)
+CHAR("+f",                     "o",            966)
+CHAR("+p",                     "w",            982)
+CHAR("+e",                     "e",            1013)
+CHAR("ts",                     "s",            962)
+
+/* Accented letters. */
+CHAR(",C",                     "C",            199)
+CHAR(",c",                     "c",            231)
+CHAR("/L",                     "L",            321)
+CHAR("/O",                     "O",            216)
+CHAR("/l",                     "l",            322)
+CHAR("/o",                     "o",            248)
+CHAR("oA",                     "A",            197)
+CHAR("oa",                     "a",            229)
+CHAR(":A",                     "A",            196)
+CHAR(":E",                     "E",            203)
+CHAR(":I",                     "I",            207)
+CHAR(":O",                     "O",            214)
+CHAR(":U",                     "U",            220)
+CHAR(":a",                     "a",            228)
+CHAR(":e",                     "e",            235)
+CHAR(":i",                     "i",            239)
+CHAR(":o",                     "o",            245)
+CHAR(":u",                     "u",            252)
+CHAR(":y",                     "y",            255)
+CHAR("\'A",                    "A",            193)
+CHAR("\'E",                    "E",            201)
+CHAR("\'I",                    "I",            205)
+CHAR("\'O",                    "O",            211)
+CHAR("\'U",                    "U",            218)
+CHAR("\'a",                    "a",            225)
+CHAR("\'e",                    "e",            233)
+CHAR("\'i",                    "i",            237)
+CHAR("\'o",                    "o",            243)
+CHAR("\'u",                    "u",            250)
+CHAR("^A",                     "A",            194)
+CHAR("^E",                     "E",            202)
+CHAR("^I",                     "I",            206)
+CHAR("^O",                     "O",            212)
+CHAR("^U",                     "U",            219)
+CHAR("^a",                     "a",            226)
+CHAR("^e",                     "e",            234)
+CHAR("^i",                     "i",            238)
+CHAR("^o",                     "o",            244)
+CHAR("^u",                     "u",            251)
+CHAR("`A",                     "A",            192)
+CHAR("`E",                     "E",            200)
+CHAR("`I",                     "I",            204)
+CHAR("`O",                     "O",            210)
+CHAR("`U",                     "U",            217)
+CHAR("`a",                     "a",            224)
+CHAR("`e",                     "e",            232)
+CHAR("`i",                     "i",            236)
+CHAR("`o",                     "o",            242)
+CHAR("`u",                     "u",            249)
+CHAR("~A",                     "A",            195)
+CHAR("~N",                     "N",            209)
+CHAR("~O",                     "O",            213)
+CHAR("~a",                     "a",            227)
+CHAR("~n",                     "n",            241)
+CHAR("~o",                     "o",            245)
+
+/* Arrows and lines. */
+CHAR("<-",                     "<-",           8592)
+CHAR("->",                     "->",           8594)
+CHAR("<>",                     "<>",           8596)
+CHAR("da",                     "v",            8595)
+BOTH("ua",                     "^",            8593)
+BOTH("va",                     "^v",           8597)
+CHAR("lA",                     "<=",           8656)
+CHAR("rA",                     "=>",           8658)
+CHAR("hA",                     "<=>",          8660)
+CHAR("dA",                     "v",            8659)
+CHAR("uA",                     "^",            8657)
+CHAR("vA",                     "^=v",          8661)
+
+/* Logic. */
+CHAR("AN",                     "^",            8743)
+CHAR("OR",                     "v",            8744)
+CHAR("no",                     "~",            172)
+CHAR("tno",                    "~",            172)
+CHAR("te",                     "3",            8707)
+CHAR("fa",                     "V",            8704)
+CHAR("st",                     "-)",           8715)
+CHAR("tf",                     ".:.",          8756)
+CHAR("3d",                     ".:.",          8756)
+CHAR("or",                     "|",            124)
+
+/* Mathematicals. */
+CHAR("pl",                     "+",            43)
+CHAR("mi",                     "-",            8722)
+CHAR("-",                      "-",            45)
+CHAR("-+",                     "-+",           8723)
+CHAR("+-",                     "+-",           177)
+CHAR("t+-",                    "+-",           177)
+CHAR("pc",                     ".",            183)
+CHAR("md",                     ".",            8901)
+CHAR("mu",                     "x",            215)
+CHAR("tmu",                    "x",            215)
+CHAR("c*",                     "x",            8855)
+CHAR("c+",                     "+",            8853)
+CHAR("di",                     "-:-",          247)
+CHAR("tdi",                    "-:-",          247)
+CHAR("f/",                     "/",            8260)
+CHAR("**",                     "*",            8727)
+BOTH("<=",                     "<=",           8804)
+BOTH(">=",                     ">=",           8805)
+CHAR("<<",                     "<<",           8810)
+CHAR(">>",                     ">>",           8811)
+CHAR("eq",                     "=",            61)
+CHAR("!=",                     "!=",           8800)
+CHAR("==",                     "==",           8801)
+CHAR("ne",                     "!==",          8802)
+CHAR("=~",                     "=~",           8773)
+CHAR("-~",                     "-~",           8771)
+CHAR("ap",                     "~",            8764)
+CHAR("~~",                     "~~",           8776)
+CHAR("~=",                     "~=",           8780)
+CHAR("pt",                     "oc",           8733)
+CHAR("es",                     "{}",           8709)
+CHAR("mo",                     "E",            8712)
+CHAR("nm",                     "!E",           8713)
+CHAR("sb",                     "(=",           8834)
+CHAR("nb",                     "(!=",          8836)
+CHAR("sp",                     "=)",           8835)
+CHAR("nc",                     "!=)",          8837)
+CHAR("ib",                     "(=",           8838)
+CHAR("ip",                     "=)",           8839)
+CHAR("ca",                     "(^)",          8745)
+CHAR("cu",                     "U",            8746)
+CHAR("/_",                     "/_",           8736)
+CHAR("pp",                     "_|_",          8869)
+CHAR("is",                     "I",            8747)
+CHAR("integral",               "I",            8747)
+CHAR("sum",                    "E",            8721)
+CHAR("product",                        "TT",           8719)
+CHAR("coproduct",              "U",            8720)
+CHAR("gr",                     "V",            8711)
+CHAR("sr",                     "\\/",          8730)
+CHAR("sqrt",                   "\\/",          8730)
+CHAR("lc",                     "|~",           8968)
+CHAR("rc",                     "~|",           8969)
+CHAR("lf",                     "|_",           8970)
+CHAR("rf",                     "_|",           8971)
+CHAR("if",                     "oo",           8734)
+CHAR("Ah",                     "N",            8501)
+CHAR("Im",                     "I",            8465)
+CHAR("Re",                     "R",            8476)
+CHAR("pd",                     "a",            8706)
+CHAR("-h",                     "/h",           8463)
+
+/* Ligatures. */
+CHAR("ff",                     "ff",           64256)
+CHAR("fi",                     "fi",           64257)
+CHAR("fl",                     "fl",           64258)
+CHAR("Fi",                     "ffi",          64259)
+CHAR("Fl",                     "ffl",          64260)
+CHAR("AE",                     "AE",           198)
+CHAR("ae",                     "ae",           230)
+CHAR("OE",                     "OE",           338)
+CHAR("oe",                     "oe",           339)
+CHAR("ss",                     "ss",           223)
+CHAR("IJ",                     "IJ",           306)
+CHAR("ij",                     "ij",           307)
+
+/* Special letters. */
+CHAR("-D",                     "D",            208)
+CHAR("Sd",                     "o",            240)
+CHAR("TP",                     "b",            222)
+CHAR("Tp",                     "b",            254)
+CHAR(".i",                     "i",            305)
+CHAR(".j",                     "j",            567)
+
+/* Currency. */
+CHAR("Do",                     "$",            36)
+CHAR("ct",                     "c",            162)
+CHAR("Eu",                     "EUR",          8364)
+CHAR("eu",                     "EUR",          8364)
+CHAR("Ye",                     "Y",            165)
+CHAR("Po",                     "L",            163)
+CHAR("Cs",                     "x",            164)
+CHAR("Fn",                     "f",            402)
+
+/* Old style. */
+STRING("Am",                   "&",            38)
+STRING("Ba",                   "|",            124)
+STRING("Ge",                   ">=",           8805)
+STRING("Gt",                   ">",            62)
+STRING("If",                   "infinity",     0)
+STRING("Le",                   "<=",           8804)
+STRING("Lq",                   "``",           8220)
+STRING("Lt",                   "<",            60)
+STRING("Na",                   "NaN",          0)
+STRING("Ne",                   "!=",           8800)
+STRING("Pi",                   "pi",           960)
+STRING("Pm",                   "+-",           177)
+STRING("Rq",                   "\'\'",         8221)
+STRING("left-bracket",         "[",            91)
+STRING("left-parenthesis",     "(",            40)
+STRING("left-singlequote",     "`",            8216)
+STRING("lp",                   "(",            40)
+STRING("q",                    "\"",           34)
+STRING("quote-left",           "`",            8216)
+STRING("quote-right",          "\'",           8217)
+STRING("R",                    "(R)",          174)
+STRING("right-bracket",                "]",            93)
+STRING("right-parenthesis",    ")",            41)
+STRING("right-singlequote",    "\'",           8217)
+STRING("rp",                   ")",            41)
+STRING("Tm",                   "(Tm)",         8482)
+
+/* Lines. */
+CHAR("ba",                     "|",            124)
+CHAR("br",                     "|",            9474)
+CHAR("ul",                     "_",            95)
+CHAR("rl",                     "-",            8254)
+CHAR("bb",                     "|",            166)
+CHAR("sl",                     "/",            47)
+CHAR("rs",                     "\\",           92)
+
+/* Text markers. */
+CHAR("ci",                     "o",            9675)
+CHAR("bu",                     "o",            8226)
+CHAR("dd",                     "=",            8225)
+CHAR("dg",                     "-",            8224)
+CHAR("lz",                     "<>",           9674)
+CHAR("sq",                     "[]",           9633)
+CHAR("ps",                     "9|",           182)
+CHAR("sc",                     "S",            167)
+CHAR("lh",                     "<=",           9756)
+CHAR("rh",                     "=>",           9758)
+CHAR("at",                     "@",            64)
+CHAR("sh",                     "#",            35)
+CHAR("CR",                     "_|",           8629)
+CHAR("OK",                     "\\/",          10003)
+
+/* Legal symbols. */
+CHAR("co",                     "(C)",          169)
+CHAR("rg",                     "(R)",          174)
+CHAR("tm",                     "tm",           8482)
+
+/* Punctuation. */
+CHAR(".",                      ".",            46)
+CHAR("r!",                     "i",            161)
+CHAR("r?",                     "c",            191)
+CHAR("em",                     "--",           8212)
+CHAR("en",                     "-",            8211)
+CHAR("hy",                     "-",            8208)
+CHAR("e",                      "\\",           92)
+
+/* Units. */
+CHAR("de",                     "o",            176)
+CHAR("%0",                     "%o",           8240)
+CHAR("fm",                     "\'",           8242)
+CHAR("sd",                     "\"",           8243)
+CHAR("mc",                     "mu",           181)
+
+CHAR_TBL_END
diff --git a/contrib/mdocml/compat.c b/contrib/mdocml/compat.c
new file mode 100644 (file)
index 0000000..f00cc5c
--- /dev/null
@@ -0,0 +1,95 @@
+/*     $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $      */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+int dummy; /* To prevent an empty object file */
+
+#ifndef HAVE_STRLCAT
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+       size_t dlen;
+
+       /* Find the end of dst and adjust bytes left but don't go past end */
+       while (n-- != 0 && *d != '\0')
+               d++;
+       dlen = d - dst;
+       n = siz - dlen;
+
+       if (n == 0)
+               return(dlen + strlen(s));
+       while (*s != '\0') {
+               if (n != 1) {
+                       *d++ = *s;
+                       n--;
+               }
+               s++;
+       }
+       *d = '\0';
+
+       return(dlen + (s - src));       /* count does not include NUL */
+}
+#endif
+
+#ifndef HAVE_STRLCPY
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+       char *d = dst;
+       const char *s = src;
+       size_t n = siz;
+
+       /* Copy as many bytes as will fit */
+       if (n != 0) {
+               while (--n != 0) {
+                       if ((*d++ = *s++) == '\0')
+                               break;
+               }
+       }
+
+       /* Not enough room in dst, add NUL and traverse rest of src */
+       if (n == 0) {
+               if (siz != 0)
+                       *d = '\0';              /* NUL-terminate dst */
+               while (*s++)
+                       ;
+       }
+
+       return(s - src - 1);    /* count does not include NUL */
+}
+#endif 
diff --git a/contrib/mdocml/config.h.post b/contrib/mdocml/config.h.post
new file mode 100644 (file)
index 0000000..81c01b9
--- /dev/null
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+
+#if !defined(__BEGIN_DECLS)
+#  ifdef __cplusplus
+#  define      __BEGIN_DECLS           extern "C" {
+#  else
+#  define      __BEGIN_DECLS
+#  endif
+#endif
+#if !defined(__END_DECLS)
+#  ifdef __cplusplus
+#  define      __END_DECLS             }
+#  else
+#  define      __END_DECLS
+#  endif
+#endif
+
+#ifndef HAVE_STRLCAT
+extern size_t    strlcat(char *, const char *, size_t);
+#endif
+#ifndef HAVE_STRLCPY
+extern size_t    strlcpy(char *, const char *, size_t);
+#endif
+
+#endif /* MANDOC_CONFIG_H */
diff --git a/contrib/mdocml/config.h.pre b/contrib/mdocml/config.h.pre
new file mode 100644 (file)
index 0000000..a309ed9
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef        MANDOC_CONFIG_H
+#define        MANDOC_CONFIG_H
+
+#if defined(__linux__) || defined(__MINT__)
+# define _GNU_SOURCE /* strptime(), getsubopt() */
+#endif
diff --git a/contrib/mdocml/example.style.css b/contrib/mdocml/example.style.css
new file mode 100644 (file)
index 0000000..7f640ee
--- /dev/null
@@ -0,0 +1,146 @@
+/* $Id: example.style.css,v 1.41 2011/01/05 13:00:11 kristaps Exp $ */
+
+/*
+ * This is an example style-sheet provided for mandoc(1) and the -Thtml
+ * or -Txhtml output mode.
+ *
+ * It mimics the appearance of the traditional cvsweb output.
+ *
+ * See mdoc(7) and man(7) for macro explanations.
+ */
+
+html           { min-width: 580px; width: 580px; }
+body           { font-family: monospace; }
+
+/* Preamble structure. */
+
+table.foot     { width: 100%; } /* Document footer. */
+td.foot-date   { width: 50%; } /* Document footer: date. */
+td.foot-os     { width: 50%; text-align: right; } /* Document footer: OS/source. */
+table.head     { width: 100%; } /* Document header. */
+td.head-ltitle { width: 10%; } /* Document header: left-title. */
+td.head-vol    { width: 80%; text-align: center; } /* Document header: volume. */
+td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */
+
+/* Sections. */
+
+h1             { margin-bottom: 0px; font-size: medium; margin-left: -4ex; } /* Section header (Sh, SH). */
+h2             { margin-bottom: 0px; font-size: medium; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
+div.section    { margin-bottom: 2ex; margin-left: 4ex; } /* Sections (Sh, SH). */
+div.subsection { } /* Sub-sections (Ss, SS). */
+table.synopsis { } /* SYNOPSIS section table. */
+
+/* Vertical spacing. */
+
+p              { } /* Paragraph: Pp, Lp. */
+blockquote     { margin-top: 0px; margin-bottom: 0px; }
+table          { margin-top: 0px; margin-bottom: 0px; }
+td             { vertical-align: top; }
+
+/* General font modes. */
+
+i              { } /* Italic: BI, IB, I, (implicit). */
+.emph          { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */
+b              { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */
+.symb          { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */
+small          { } /* Small: SB, SM. */
+
+/* Block modes. */
+
+.display       { } /* Top of all Bd, D1, Dl. */
+.list          { } /* Top of all Bl. */
+
+/* Context-specific modes. */
+
+i.addr         { font-weight: normal; } /* Address (Ad). */
+i.arg          { font-weight: normal; } /* Command argument (Ar). */
+span.author    { } /* Author name (An). */
+b.cmd          { font-style: normal; } /* Command (Cm). */ 
+b.config       { font-style: normal; } /* Config statement (Cd). */
+span.define    { } /* Defines (Dv). */
+span.desc      { } /* Nd.  After em-dash. */
+b.diag         { font-style: normal; } /* Diagnostic (Bl -diag). */
+span.env       { } /* Environment variables (Ev). */
+span.errno     { } /* Error string (Er). */
+i.farg         { font-weight: normal; } /* Function argument (Fa, Fn). */
+i.file         { font-weight: normal; } /* File (Pa). */
+b.flag         { font-style: normal; } /* Flag (Fl, Cm). */
+b.fname                { font-style: normal; } /* Function name (Fa, Fn, Rv). */
+i.ftype                { font-weight: normal; } /* Function types (Ft, Fn). */
+b.includes     { font-style: normal; } /* Header includes (In). */
+span.lib       { } /* Library (Lb). */
+i.link-sec     { font-weight: normal; } /* Section links (Sx). */
+code.lit       { font-style: normal; font-weight: normal; } /* Literal: Dl, Li, Bf -literal, Bl -literal, Bl -unfilled. */
+b.macro                { font-style: normal; } /* Macro-ish thing (Fd). */
+b.name         { font-style: normal; } /* Name of utility (Nm). */
+span.opt       { } /* Options (Op, Oo/Oc). */
+span.ref       { } /* Citations (Rs). */
+span.ref-auth  { } /* Reference author (%A). */
+i.ref-book     { font-weight: normal; } /* Reference book (%B). */
+span.ref-city  { } /* Reference city (%C). */
+span.ref-date  { } /* Reference date (%D). */
+i.ref-issue    { font-weight: normal; } /* Reference issuer/publisher (%I). */
+i.ref-jrnl     { font-weight: normal; } /* Reference journal (%J). */
+span.ref-num   { } /* Reference number (%N). */
+span.ref-opt   { } /* Reference optionals (%O). */
+span.ref-page  { } /* Reference page (%P). */
+span.ref-corp  { } /* Reference corporate/foreign author (%Q). */
+span.ref-rep   { } /* Reference report (%R). */
+span.ref-title { text-decoration: underline; } /* Reference title (%T). */
+span.ref-vol   { } /* Reference volume (%V). */
+span.type      { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
+span.unix      { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
+b.utility      { font-style: normal; } /* Name of utility (Ex). */
+b.var          { font-style: normal; } /* Variables (Rv). */
+
+a.link-ext     { } /* Off-site link (Lk). */
+a.link-includes        { } /* Include-file link (In). */
+a.link-mail    { } /* Mailto links (Mt). */
+a.link-man     { } /* Manual links (Xr). */
+a.link-ref     { } /* Reference section links (%Q). */
+a.link-sec     { } /* Section links (Sx). */
+
+/* Formatting for lists.  See mdoc(7). */
+
+dl.list-diag   { }
+dt.list-diag   { }
+dd.list-diag   { }
+
+dl.list-hang   { }
+dt.list-hang   { }
+dd.list-hang   { }
+
+dl.list-inset  { }
+dt.list-inset  { }
+dd.list-inset  { }
+
+dl.list-ohang  { }
+dt.list-ohang  { }
+dd.list-ohang  { margin-left: 0em; }
+
+dl.list-tag    { }
+dt.list-tag    { }
+dd.list-tag    { }
+
+table.list-col { }
+tr.list-col    { }
+td.list-col    { }
+
+ul.list-bul    { list-style-type: disc; padding-left: 1em; }
+li.list-bul    { }
+
+ul.list-dash   { list-style-type: none; padding-left: 0em; }
+li.list-dash:before { content: "\2014  "; }
+
+ul.list-hyph   { list-style-type: none; padding-left: 0em; }
+li.list-hyph:before { content: "\2013  "; }
+
+ul.list-item   { list-style-type: none; padding-left: 0em; }
+li.list-item   { }
+
+ol.list-enum   { padding-left: 2em; }
+li.list-enum   { }
+
+/* Table modes.  See tbl(7). */
+
+table.tbl      { }
diff --git a/contrib/mdocml/external.png b/contrib/mdocml/external.png
new file mode 100644 (file)
index 0000000..419c06f
Binary files /dev/null and b/contrib/mdocml/external.png differ
diff --git a/contrib/mdocml/html.c b/contrib/mdocml/html.c
new file mode 100644 (file)
index 0000000..70403ff
--- /dev/null
@@ -0,0 +1,798 @@
+/*     $Id: html.c,v 1.124 2010/12/27 21:41:05 schwarze Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc.h"
+#include "out.h"
+#include "chars.h"
+#include "html.h"
+#include "main.h"
+
+struct htmldata {
+       const char       *name;
+       int               flags;
+#define        HTML_CLRLINE     (1 << 0)
+#define        HTML_NOSTACK     (1 << 1)
+#define        HTML_AUTOCLOSE   (1 << 2) /* Tag has auto-closure. */
+};
+
+static const struct htmldata htmltags[TAG_MAX] = {
+       {"html",        HTML_CLRLINE}, /* TAG_HTML */
+       {"head",        HTML_CLRLINE}, /* TAG_HEAD */
+       {"body",        HTML_CLRLINE}, /* TAG_BODY */
+       {"meta",        HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
+       {"title",       HTML_CLRLINE}, /* TAG_TITLE */
+       {"div",         HTML_CLRLINE}, /* TAG_DIV */
+       {"h1",          0}, /* TAG_H1 */
+       {"h2",          0}, /* TAG_H2 */
+       {"span",        0}, /* TAG_SPAN */
+       {"link",        HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
+       {"br",          HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
+       {"a",           0}, /* TAG_A */
+       {"table",       HTML_CLRLINE}, /* TAG_TABLE */
+       {"tbody",       HTML_CLRLINE}, /* TAG_TBODY */
+       {"col",         HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
+       {"tr",          HTML_CLRLINE}, /* TAG_TR */
+       {"td",          HTML_CLRLINE}, /* TAG_TD */
+       {"li",          HTML_CLRLINE}, /* TAG_LI */
+       {"ul",          HTML_CLRLINE}, /* TAG_UL */
+       {"ol",          HTML_CLRLINE}, /* TAG_OL */
+       {"dl",          HTML_CLRLINE}, /* TAG_DL */
+       {"dt",          HTML_CLRLINE}, /* TAG_DT */
+       {"dd",          HTML_CLRLINE}, /* TAG_DD */
+       {"blockquote",  HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
+       {"p",           HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */
+       {"pre",         HTML_CLRLINE }, /* TAG_PRE */
+       {"b",           0 }, /* TAG_B */
+       {"i",           0 }, /* TAG_I */
+       {"code",        0 }, /* TAG_CODE */
+       {"small",       0 }, /* TAG_SMALL */
+};
+
+static const char      *const htmlattrs[ATTR_MAX] = {
+       "http-equiv", /* ATTR_HTTPEQUIV */
+       "content", /* ATTR_CONTENT */
+       "name", /* ATTR_NAME */
+       "rel", /* ATTR_REL */
+       "href", /* ATTR_HREF */
+       "type", /* ATTR_TYPE */
+       "media", /* ATTR_MEDIA */
+       "class", /* ATTR_CLASS */
+       "style", /* ATTR_STYLE */
+       "width", /* ATTR_WIDTH */
+       "id", /* ATTR_ID */
+       "summary", /* ATTR_SUMMARY */
+       "align", /* ATTR_ALIGN */
+};
+
+static void              print_spec(struct html *, enum roffdeco,
+                               const char *, size_t);
+static void              print_res(struct html *, const char *, size_t);
+static void              print_ctag(struct html *, enum htmltag);
+static void              print_doctype(struct html *);
+static void              print_xmltype(struct html *);
+static int               print_encode(struct html *, const char *, int);
+static void              print_metaf(struct html *, enum roffdeco);
+static void              print_attr(struct html *, 
+                               const char *, const char *);
+static void             *ml_alloc(char *, enum htmltype);
+
+
+static void *
+ml_alloc(char *outopts, enum htmltype type)
+{
+       struct html     *h;
+       const char      *toks[4];
+       char            *v;
+
+       toks[0] = "style";
+       toks[1] = "man";
+       toks[2] = "includes";
+       toks[3] = NULL;
+
+       h = calloc(1, sizeof(struct html));
+       if (NULL == h) {
+               perror(NULL);
+               exit((int)MANDOCLEVEL_SYSERR);
+       }
+
+       h->type = type;
+       h->tags.head = NULL;
+       h->symtab = chars_init(CHARS_HTML);
+
+       while (outopts && *outopts)
+               switch (getsubopt(&outopts, UNCONST(toks), &v)) {
+               case (0):
+                       h->style = v;
+                       break;
+               case (1):
+                       h->base_man = v;
+                       break;
+               case (2):
+                       h->base_includes = v;
+                       break;
+               default:
+                       break;
+               }
+
+       return(h);
+}
+
+void *
+html_alloc(char *outopts)
+{
+
+       return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
+}
+
+
+void *
+xhtml_alloc(char *outopts)
+{
+
+       return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
+}
+
+
+void
+html_free(void *p)
+{
+       struct tag      *tag;
+       struct html     *h;
+
+       h = (struct html *)p;
+
+       while ((tag = h->tags.head) != NULL) {
+               h->tags.head = tag->next;       
+               free(tag);
+       }
+       
+       if (h->symtab)
+               chars_free(h->symtab);
+
+       free(h);
+}
+
+
+void
+print_gen_head(struct html *h)
+{
+       struct htmlpair  tag[4];
+
+       tag[0].key = ATTR_HTTPEQUIV;
+       tag[0].val = "Content-Type";
+       tag[1].key = ATTR_CONTENT;
+       tag[1].val = "text/html; charset=utf-8";
+       print_otag(h, TAG_META, 2, tag);
+
+       tag[0].key = ATTR_NAME;
+       tag[0].val = "resource-type";
+       tag[1].key = ATTR_CONTENT;
+       tag[1].val = "document";
+       print_otag(h, TAG_META, 2, tag);
+
+       if (h->style) {
+               tag[0].key = ATTR_REL;
+               tag[0].val = "stylesheet";
+               tag[1].key = ATTR_HREF;
+               tag[1].val = h->style;
+               tag[2].key = ATTR_TYPE;
+               tag[2].val = "text/css";
+               tag[3].key = ATTR_MEDIA;
+               tag[3].val = "all";
+               print_otag(h, TAG_LINK, 4, tag);
+       }
+}
+
+
+static void
+print_spec(struct html *h, enum roffdeco d, const char *p, size_t len)
+{
+       int              cp;
+       const char      *rhs;
+       size_t           sz;
+
+       if ((cp = chars_spec2cp(h->symtab, p, len)) > 0) {
+               printf("&#%d;", cp);
+               return;
+       } else if (-1 == cp && DECO_SSPECIAL == d) {
+               fwrite(p, 1, len, stdout);
+               return;
+       } else if (-1 == cp)
+               return;
+
+       if (NULL != (rhs = chars_spec2str(h->symtab, p, len, &sz)))
+               fwrite(rhs, 1, sz, stdout);
+}
+
+
+static void
+print_res(struct html *h, const char *p, size_t len)
+{
+       int              cp;
+       const char      *rhs;
+       size_t           sz;
+
+       if ((cp = chars_res2cp(h->symtab, p, len)) > 0) {
+               printf("&#%d;", cp);
+               return;
+       } else if (-1 == cp)
+               return;
+
+       if (NULL != (rhs = chars_res2str(h->symtab, p, len, &sz)))
+               fwrite(rhs, 1, sz, stdout);
+}
+
+
+static void
+print_metaf(struct html *h, enum roffdeco deco)
+{
+       enum htmlfont    font;
+
+       switch (deco) {
+       case (DECO_PREVIOUS):
+               font = h->metal;
+               break;
+       case (DECO_ITALIC):
+               font = HTMLFONT_ITALIC;
+               break;
+       case (DECO_BOLD):
+               font = HTMLFONT_BOLD;
+               break;
+       case (DECO_ROMAN):
+               font = HTMLFONT_NONE;
+               break;
+       default:
+               abort();
+               /* NOTREACHED */
+       }
+
+       if (h->metaf) {
+               print_tagq(h, h->metaf);
+               h->metaf = NULL;
+       }
+
+       h->metal = h->metac;
+       h->metac = font;
+
+       if (HTMLFONT_NONE != font)
+               h->metaf = HTMLFONT_BOLD == font ?
+                       print_otag(h, TAG_B, 0, NULL) :
+                       print_otag(h, TAG_I, 0, NULL);
+}
+
+
+static int
+print_encode(struct html *h, const char *p, int norecurse)
+{
+       size_t           sz;
+       int              len, nospace;
+       const char      *seq;
+       enum roffdeco    deco;
+       static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
+
+       nospace = 0;
+
+       for (; *p; p++) {
+               sz = strcspn(p, rejs);
+
+               fwrite(p, 1, sz, stdout);
+               p += /* LINTED */
+                       sz;
+
+               if ('<' == *p) {
+                       printf("&lt;");
+                       continue;
+               } else if ('>' == *p) {
+                       printf("&gt;");
+                       continue;
+               } else if ('&' == *p) {
+                       printf("&amp;");
+                       continue;
+               } else if (ASCII_HYPH == *p) {
+                       /*
+                        * Note: "soft hyphens" aren't graphically
+                        * displayed when not breaking the text; we want
+                        * them to be displayed.
+                        */
+                       /*printf("&#173;");*/
+                       putchar('-');
+                       continue;
+               } else if ('\0' == *p)
+                       break;
+
+               seq = ++p;
+               len = a2roffdeco(&deco, &seq, &sz);
+
+               switch (deco) {
+               case (DECO_RESERVED):
+                       print_res(h, seq, sz);
+                       break;
+               case (DECO_SSPECIAL):
+                       /* FALLTHROUGH */
+               case (DECO_SPECIAL):
+                       print_spec(h, deco, seq, sz);
+                       break;
+               case (DECO_PREVIOUS):
+                       /* FALLTHROUGH */
+               case (DECO_BOLD):
+                       /* FALLTHROUGH */
+               case (DECO_ITALIC):
+                       /* FALLTHROUGH */
+               case (DECO_ROMAN):
+                       if (norecurse)
+                               break;
+                       print_metaf(h, deco);
+                       break;
+               default:
+                       break;
+               }
+
+               p += len - 1;
+
+               if (DECO_NOSPACE == deco && '\0' == *(p + 1))
+                       nospace = 1;
+       }
+
+       return(nospace);
+}
+
+
+static void
+print_attr(struct html *h, const char *key, const char *val)
+{
+       printf(" %s=\"", key);
+       (void)print_encode(h, val, 1);
+       putchar('\"');
+}
+
+
+struct tag *
+print_otag(struct html *h, enum htmltag tag, 
+               int sz, const struct htmlpair *p)
+{
+       int              i;
+       struct tag      *t;
+
+       /* Push this tags onto the stack of open scopes. */
+
+       if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
+               t = malloc(sizeof(struct tag));
+               if (NULL == t) {
+                       perror(NULL);
+                       exit((int)MANDOCLEVEL_SYSERR);
+               }
+               t->tag = tag;
+               t->next = h->tags.head;
+               h->tags.head = t;
+       } else
+               t = NULL;
+
+       if ( ! (HTML_NOSPACE & h->flags))
+               if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
+                       /* Manage keeps! */
+                       if ( ! (HTML_KEEP & h->flags)) {
+                               if (HTML_PREKEEP & h->flags)
+                                       h->flags |= HTML_KEEP;
+                               putchar(' ');
+                       } else
+                               printf("&#160;");
+               }
+
+       if ( ! (h->flags & HTML_NONOSPACE))
+               h->flags &= ~HTML_NOSPACE;
+       else
+               h->flags |= HTML_NOSPACE;
+
+       /* Print out the tag name and attributes. */
+
+       printf("<%s", htmltags[tag].name);
+       for (i = 0; i < sz; i++)
+               print_attr(h, htmlattrs[p[i].key], p[i].val);
+
+       /* Add non-overridable attributes. */
+
+       if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
+               print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
+               print_attr(h, "xml:lang", "en");
+               print_attr(h, "lang", "en");
+       }
+
+       /* Accomodate for XML "well-formed" singleton escaping. */
+
+       if (HTML_AUTOCLOSE & htmltags[tag].flags)
+               switch (h->type) {
+               case (HTML_XHTML_1_0_STRICT):
+                       putchar('/');
+                       break;
+               default:
+                       break;
+               }
+
+       putchar('>');
+
+       h->flags |= HTML_NOSPACE;
+
+       if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
+               putchar('\n');
+
+       return(t);
+}
+
+
+static void
+print_ctag(struct html *h, enum htmltag tag)
+{
+       
+       printf("</%s>", htmltags[tag].name);
+       if (HTML_CLRLINE & htmltags[tag].flags) {
+               h->flags |= HTML_NOSPACE;
+               putchar('\n');
+       } 
+}
+
+
+void
+print_gen_decls(struct html *h)
+{
+
+       print_xmltype(h);
+       print_doctype(h);
+}
+
+
+static void
+print_xmltype(struct html *h)
+{
+
+       if (HTML_XHTML_1_0_STRICT == h->type)
+               puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+}
+
+
+static void
+print_doctype(struct html *h)
+{
+       const char      *doctype;
+       const char      *dtd;
+       const char      *name;
+
+       switch (h->type) {
+       case (HTML_HTML_4_01_STRICT):
+               name = "HTML";
+               doctype = "-//W3C//DTD HTML 4.01//EN";
+               dtd = "http://www.w3.org/TR/html4/strict.dtd";
+               break;
+       default:
+               name = "html";
+               doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
+               dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
+               break;
+       }
+
+       printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", 
+                       name, doctype, dtd);
+}
+
+
+void
+print_text(struct html *h, const char *word)
+{
+
+       if (word[0] && '\0' == word[1])
+               switch (word[0]) {
+               case('.'):
+                       /* FALLTHROUGH */
+               case(','):
+                       /* FALLTHROUGH */
+               case(';'):
+                       /* FALLTHROUGH */
+               case(':'):
+                       /* FALLTHROUGH */
+               case('?'):
+                       /* FALLTHROUGH */
+               case('!'):
+                       /* FALLTHROUGH */
+               case(')'):
+                       /* FALLTHROUGH */
+               case(']'):
+                       if ( ! (HTML_IGNDELIM & h->flags))
+                               h->flags |= HTML_NOSPACE;
+                       break;
+               default:
+                       break;
+               }
+
+       if ( ! (HTML_NOSPACE & h->flags)) {
+               /* Manage keeps! */
+               if ( ! (HTML_KEEP & h->flags)) {
+                       if (HTML_PREKEEP & h->flags)
+                               h->flags |= HTML_KEEP;
+                       putchar(' ');
+               } else
+                       printf("&#160;");
+       }
+
+       assert(NULL == h->metaf);
+       if (HTMLFONT_NONE != h->metac)
+               h->metaf = HTMLFONT_BOLD == h->metac ?
+                       print_otag(h, TAG_B, 0, NULL) :
+                       print_otag(h, TAG_I, 0, NULL);
+
+       assert(word);
+       if ( ! print_encode(h, word, 0))
+               if ( ! (h->flags & HTML_NONOSPACE))
+                       h->flags &= ~HTML_NOSPACE;
+
+       if (h->metaf) {
+               print_tagq(h, h->metaf);
+               h->metaf = NULL;
+       }
+
+       h->flags &= ~HTML_IGNDELIM;
+
+       /* 
+        * Note that we don't process the pipe: the parser sees it as
+        * punctuation, but we don't in terms of typography.
+        */
+       if (word[0] && '\0' == word[1])
+               switch (word[0]) {
+               case('('):
+                       /* FALLTHROUGH */
+               case('['):
+                       h->flags |= HTML_NOSPACE;
+                       break;
+               default:
+                       break;
+               }
+}
+
+
+void
+print_tagq(struct html *h, const struct tag *until)
+{
+       struct tag      *tag;
+
+       while ((tag = h->tags.head) != NULL) {
+               if (tag == h->metaf)
+                       h->metaf = NULL;
+               print_ctag(h, tag->tag);
+               h->tags.head = tag->next;
+               free(tag);
+               if (until && tag == until)
+                       return;
+       }
+}
+
+
+void
+print_stagq(struct html *h, const struct tag *suntil)
+{
+       struct tag      *tag;
+
+       while ((tag = h->tags.head) != NULL) {
+               if (suntil && tag == suntil)
+                       return;
+               if (tag == h->metaf)
+                       h->metaf = NULL;
+               print_ctag(h, tag->tag);
+               h->tags.head = tag->next;
+               free(tag);
+       }
+}
+
+
+void
+bufinit(struct html *h)
+{
+
+       h->buf[0] = '\0';
+       h->buflen = 0;
+}
+
+
+void
+bufcat_style(struct html *h, const char *key, const char *val)
+{
+
+       bufcat(h, key);
+       bufncat(h, ":", 1);
+       bufcat(h, val);
+       bufncat(h, ";", 1);
+}
+
+
+void
+bufcat(struct html *h, const char *p)
+{
+
+       bufncat(h, p, strlen(p));
+}
+
+
+void
+buffmt(struct html *h, const char *fmt, ...)
+{
+       va_list          ap;
+
+       va_start(ap, fmt);
+       (void)vsnprintf(h->buf + (int)h->buflen, 
+                       BUFSIZ - h->buflen - 1, fmt, ap);
+       va_end(ap);
+       h->buflen = strlen(h->buf);
+}
+
+
+void
+bufncat(struct html *h, const char *p, size_t sz)
+{
+
+       if (h->buflen + sz > BUFSIZ - 1)
+               sz = BUFSIZ - 1 - h->buflen;
+
+       (void)strncat(h->buf, p, sz);
+       h->buflen += sz;
+}
+
+
+void
+buffmt_includes(struct html *h, const char *name)
+{
+       const char      *p, *pp;
+
+       pp = h->base_includes;
+       
+       while (NULL != (p = strchr(pp, '%'))) {
+               bufncat(h, pp, (size_t)(p - pp));
+               switch (*(p + 1)) {
+               case('I'):
+                       bufcat(h, name);
+                       break;
+               default:
+                       bufncat(h, p, 2);
+                       break;
+               }
+               pp = p + 2;
+       }
+       if (pp)
+               bufcat(h, pp);
+}
+
+
+void
+buffmt_man(struct html *h, 
+               const char *name, const char *sec)
+{
+       const char      *p, *pp;
+
+       pp = h->base_man;
+       
+       /* LINTED */
+       while (NULL != (p = strchr(pp, '%'))) {
+               bufncat(h, pp, (size_t)(p - pp));
+               switch (*(p + 1)) {
+               case('S'):
+                       bufcat(h, sec ? sec : "1");
+                       break;
+               case('N'):
+                       buffmt(h, name);
+                       break;
+               default:
+                       bufncat(h, p, 2);
+                       break;
+               }
+               pp = p + 2;
+       }
+       if (pp)
+               bufcat(h, pp);
+}
+
+
+void
+bufcat_su(struct html *h, const char *p, const struct roffsu *su)
+{
+       double           v;
+       const char      *u;
+
+       v = su->scale;
+
+       switch (su->unit) {
+       case (SCALE_CM):
+               u = "cm";
+               break;
+       case (SCALE_IN):
+               u = "in";
+               break;
+       case (SCALE_PC):
+               u = "pc";
+               break;
+       case (SCALE_PT):
+               u = "pt";
+               break;
+       case (SCALE_EM):
+               u = "em";
+               break;
+       case (SCALE_MM):
+               if (0 == (v /= 100))
+                       v = 1;
+               u = "em";
+               break;
+       case (SCALE_EN):
+               u = "ex";
+               break;
+       case (SCALE_BU):
+               u = "ex";
+               break;
+       case (SCALE_VS):
+               u = "em";
+               break;
+       default:
+               u = "ex";
+               break;
+       }
+
+       /* 
+        * XXX: the CSS spec isn't clear as to which types accept
+        * integer or real numbers, so we just make them all decimals.
+        */
+       buffmt(h, "%s: %.2f%s;", p, v, u);
+}
+
+
+void
+html_idcat(char *dst, const char *src, int sz)
+{
+       int              ssz;
+
+       assert(sz > 2);
+
+       /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
+
+       /* We can't start with a number (bah). */
+
+       if ('#' == *dst) {
+               dst++;
+               sz--;
+       }
+       if ('\0' == *dst) {
+               *dst++ = 'x';
+               *dst = '\0';
+               sz--;
+       }
+
+       for ( ; *dst != '\0' && sz; dst++, sz--)
+               /* Jump to end. */ ;
+
+       for ( ; *src != '\0' && sz > 1; src++) {
+               ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
+               sz -= ssz;
+               dst += ssz;
+       }
+}
diff --git a/contrib/mdocml/html.h b/contrib/mdocml/html.h
new file mode 100644 (file)
index 0000000..8d9db89
--- /dev/null
@@ -0,0 +1,157 @@
+/*     $Id: html.h,v 1.38 2011/01/06 11:55:39 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef HTML_H
+#define HTML_H
+
+__BEGIN_DECLS
+
+enum   htmltag {
+       TAG_HTML,
+       TAG_HEAD,
+       TAG_BODY,
+       TAG_META,
+       TAG_TITLE,
+       TAG_DIV,
+       TAG_H1,
+       TAG_H2,
+       TAG_SPAN,
+       TAG_LINK,
+       TAG_BR,
+       TAG_A,
+       TAG_TABLE,
+       TAG_TBODY,
+       TAG_COL,
+       TAG_TR,
+       TAG_TD,
+       TAG_LI,
+       TAG_UL,
+       TAG_OL,
+       TAG_DL,
+       TAG_DT,
+       TAG_DD,
+       TAG_BLOCKQUOTE,
+       TAG_P,
+       TAG_PRE,
+       TAG_B,
+       TAG_I,
+       TAG_CODE,
+       TAG_SMALL,
+       TAG_MAX
+};
+
+enum   htmlattr {
+       ATTR_HTTPEQUIV,
+       ATTR_CONTENT,
+       ATTR_NAME,
+       ATTR_REL,
+       ATTR_HREF,
+       ATTR_TYPE,
+       ATTR_MEDIA,
+       ATTR_CLASS,
+       ATTR_STYLE,
+       ATTR_WIDTH,
+       ATTR_ID,
+       ATTR_SUMMARY,
+       ATTR_ALIGN,
+       ATTR_MAX
+};
+
+enum   htmlfont {
+       HTMLFONT_NONE = 0,
+       HTMLFONT_BOLD,
+       HTMLFONT_ITALIC,
+       HTMLFONT_MAX
+};
+
+struct tag {
+       struct tag       *next;
+       enum htmltag      tag;
+};
+
+struct tagq {
+       struct tag       *head;
+};
+
+struct htmlpair {
+       enum htmlattr     key;
+       const char       *val;
+};
+
+#define        PAIR_INIT(p, t, v) \
+       do { \
+               (p)->key = (t); \
+               (p)->val = (v); \
+       } while (/* CONSTCOND */ 0)
+
+#define        PAIR_ID_INIT(p, v)      PAIR_INIT(p, ATTR_ID, v)
+#define        PAIR_CLASS_INIT(p, v)   PAIR_INIT(p, ATTR_CLASS, v)
+#define        PAIR_HREF_INIT(p, v)    PAIR_INIT(p, ATTR_HREF, v)
+#define        PAIR_STYLE_INIT(p, h)   PAIR_INIT(p, ATTR_STYLE, (h)->buf)
+#define        PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v)
+
+enum   htmltype {
+       HTML_HTML_4_01_STRICT,
+       HTML_XHTML_1_0_STRICT
+};
+
+struct html {
+       int               flags;
+#define        HTML_NOSPACE     (1 << 0)
+#define        HTML_IGNDELIM    (1 << 1)
+#define        HTML_KEEP        (1 << 2)
+#define        HTML_PREKEEP     (1 << 3)
+#define        HTML_NONOSPACE   (1 << 4)
+       struct tagq       tags; /* stack of open tags */
+       struct rofftbl    tbl; /* current table */
+       void             *symtab; /* character-escapes */
+       char             *base_man; /* base for manpage href */
+       char             *base_includes; /* base for include href */
+       char             *style; /* style-sheet URI */
+       char              buf[BUFSIZ]; /* see bufcat and friends */
+       size_t            buflen;
+       struct tag       *metaf; /* current open font scope */
+       enum htmlfont     metal; /* last used font */
+       enum htmlfont     metac; /* current font mode */
+       enum htmltype     type;
+};
+
+void             print_gen_decls(struct html *);
+void             print_gen_head(struct html *);
+struct tag      *print_otag(struct html *, enum htmltag, 
+                               int, const struct htmlpair *);
+void             print_tagq(struct html *, const struct tag *);
+void             print_stagq(struct html *, const struct tag *);
+void             print_text(struct html *, const char *);
+void             print_tbl(struct html *, const struct tbl_span *);
+
+void             bufcat_su(struct html *, const char *, 
+                       const struct roffsu *);
+void             buffmt_man(struct html *, 
+                       const char *, const char *);
+void             buffmt_includes(struct html *, const char *);
+void             buffmt(struct html *, const char *, ...);
+void             bufcat(struct html *, const char *);
+void             bufcat_style(struct html *, 
+                       const char *, const char *);
+void             bufncat(struct html *, const char *, size_t);
+void             bufinit(struct html *);
+
+void             html_idcat(char *, const char *, int);
+
+__END_DECLS
+
+#endif /*!HTML_H*/
diff --git a/contrib/mdocml/index.css b/contrib/mdocml/index.css
new file mode 100644 (file)
index 0000000..d8d0b2d
--- /dev/null
@@ -0,0 +1,48 @@
+body           { color: #333333;
+                 font-size: 0.93em;
+                 font-family: Times, sans-serif; }
+
+table.frame    { max-width: 800px; 
+                 padding-right: 2em;
+                 padding-left: 1em; }
+
+table          { padding-left: 40px; }
+
+p              { padding-left: 40px;
+                 text-align: justify; }
+
+h1             { font-weight: bold;
+                 font-size: small;
+                 font-family: Verdana, Tahoma, Arial, sans-serif; }
+
+h2             { font-weight: bold;
+                 font-size: small;
+                 padding-left: 20px;
+                 margin-bottom: 0px; 
+                 font-family: Verdana, Tahoma, Arial, sans-serif; }
+
+span.nm                { font-weight: bold; }
+
+span.file      { font-style: italic; }
+
+span.attn      { color: #000000; font-weight: bold; }
+
+span.flag      { font-weight: bold; }
+
+div.head       { border-bottom: 1px solid #dddddd; 
+                 padding-bottom: 5px;
+                 text-align: right; }
+
+div.foot       { border-top: 1px solid #dddddd; 
+                 padding-top: 5px;
+                 font-size: smaller;
+                 text-align: right; }
+
+a.external     { background: transparent url(external.png) center right no-repeat; 
+                 padding-right: 12px; }
+
+span.date      { color: #000000; }
+
+div.news       { margin-bottom: 2em; }
+
+div.news ul    { margin-left: 4em; }
diff --git a/contrib/mdocml/index.sgml b/contrib/mdocml/index.sgml
new file mode 100644 (file)
index 0000000..7abc997
--- /dev/null
@@ -0,0 +1,398 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<HTML>
+       <HEAD>
+               <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+               <META NAME="resource-type" CONTENT="document">
+               <LINK REL="stylesheet" HREF="index.css" TYPE="text/css" MEDIA="all">
+               <TITLE>mdocml | mdoc macro compiler</TITLE>
+       </HEAD>
+       <BODY>
+       <TABLE CLASS="frame" SUMMARY="[frame]">
+               <COL WIDTH="100%">
+               <TBODY>
+                       <TR>
+                               <TD>
+                                       <DIV CLASS="head">
+                                               <B>mdocml</B> &#8211; mdoc macro compiler
+                                       </DIV>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD VALIGN="top">
+                                       <H1>
+                                       <A NAME="description">DESCRIPTION</A>
+                                       </H1>
+
+                                       <P>
+                                       <SPAN CLASS="nm">mdocml</SPAN> is a suite of tools compiling <Q>-<A HREF="mdoc.7.html">mdoc</A></Q>, the
+                                       roff macro package of choice for BSD manual pages, and <Q>-<A HREF="man.7.html">man</A></Q>, the
+                                       predominant historical package for UNIX manuals.  The mission of <SPAN CLASS="nm">mdocml</SPAN> is to
+                                       deprecate <A HREF="http://www.gnu.org/software/groff/" CLASS="external">groff</A>, the GNU troff
+                                       implementation, for displaying -mdoc pages whilst providing token support for -man.
+                                       </P>
+
+                                       <P>
+                                       Why?  groff amounts to over 5 MB of source code, most of which is C++ and all of which is GPL.  It runs
+                                       slowly, produces uncertain output, and varies in operation from system to system.  mdocml strives to fix
+                                       this (respectively small, C, <A CLASS="external"
+                                       HREF="http://www.isc.org/software/license">ISC</A>-licensed, fast and regular).
+                                       </P>
+
+                                       <P>
+                                       <SPAN CLASS="nm">mdocml</SPAN> consists of the <A HREF="mdoc.3.html">libmdoc</A>, <A
+                                       HREF="man.3.html">libman</A>, and <A HREF="roff.3.html">libroff</A> validating compilers; and <A
+                                       HREF="mandoc.1.html">mandoc</A>, which interfaces with the compiler libraries to format output for UNIX
+                                       terminals, XHTML, HTML, PostScript, and PDF.  It is a <A CLASS="external"
+                                       HREF="http://bsd.lv/">BSD.lv</A> project.  
+                                       </P>
+
+                                       <P>
+                                       <I>Disambiguation</I>: <SPAN CLASS="nm">mdocml</SPAN> is often referred to by its installed binary,
+                                       <Q>mandoc</Q>.
+                                       </P>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD>
+                                       <H1>
+                                       <A NAME="sources">SOURCES</A>
+                                       </H1>
+
+                                       <P>
+                                       <SPAN CLASS="nm">mdocml</SPAN> is architecture- and system-neutral, written in plain-old C.  The most
+                                       current version is <SPAN CLASS="attn">@VERSION@</SPAN>, dated <SPAN class="attn">@VDATE@</SPAN>.  A full
+                                       <A HREF="ChangeLog.html">ChangeLog</A> (<A HREF="ChangeLog.txt">txt</A>) is written with each release.
+                                       </P>
+
+                                       <H2>
+                                       Current
+                                       </H2>
+
+                                       <TABLE WIDTH="100%" SUMMARY="Current Sources">
+                                       <COL WIDTH="175">
+                                       <COL>
+                                       <TBODY>
+                                               <TR>
+                                                       <TD>Source archive</TD>
+                                                       <TD>
+                                                       <A HREF="/snapshots/mdocml.tar.gz">/snapshots/mdocml.tar.gz</A> 
+                                                       (<A HREF="/snapshots/mdocml.md5">md5</A>)
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD>Online source</TD>
+                                                       <TD>
+                                                       <A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A>
+                                                       </TD>
+                                               </TR>
+                                       </TBODY>
+                                       </TABLE>
+
+                                       <H2>
+                                       Downstream
+                                       </H2>
+
+                                       <TABLE WIDTH="100%" SUMMARY="Downstream Sources">
+                                       <COL WIDTH="175">
+                                       <COL>
+                                       <TBODY>
+                                               <TR>
+                                                       <TD>DragonFly BSD</TD>
+                                                       <TD>
+                                                       <A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/usr.bin/mandoc"
+                                                               CLASS="external">usr.bin/mandoc</A>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD>FreeBSD</TD>
+                                                       <TD>
+                                                       <A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/ports/textproc/mdocml/" 
+                                                               CLASS="external">ports/textproc/mdocml</A>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD>NetBSD</TD>
+                                                       <TD>
+                                                       <A HREF="http://cvsweb.netbsd.org/bsdweb.cgi/src/external/bsd/mdocml/"
+                                                               CLASS="external">src/external/bsd/mdocml</A>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD>OpenBSD</TD>
+                                                       <TD>
+                                                       <A HREF="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/mandoc/"
+                                                               CLASS="external">src/usr.bin/mandoc</A> 
+                                                       </TD>
+                                               </TR>
+                                       </TBODY>
+                                       </TABLE>
+
+                                       <H2>
+                                       Historical
+                                       </H2>
+
+                                       <TABLE WIDTH="100%" SUMMARY="Archived Sources">
+                                       <COL WIDTH="175">
+                                       <COL>
+                                       <TBODY>
+                                               <TR>
+                                                       <TD>Source archive</TD>
+                                                       <TD>
+                                                       <A HREF="/snapshots/">/snapshots/</A> 
+                                                       </TD>
+                                               </TR>
+                                       </TBODY>
+                                       </TABLE>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD>
+                                       <H1>
+                                       <A NAME="documentation">DOCUMENTATION</A>
+                                       </H1>
+
+                                       <P>
+                                               These manuals are generated automatically and refer to the current snapshot.
+                                       </P>
+
+                                       <TABLE WIDTH="100%" SUMMARY="Documentation">
+                                       <COL WIDTH="175">
+                                       <COL>
+                                       <TBODY>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="mandoc.1.html">mandoc(1)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               format and display UNIX manuals
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="mandoc.1.txt">text</A> | 
+                                                                       <A HREF="mandoc.1.xhtml">xhtml</A> |
+                                                                       <A HREF="mandoc.1.pdf">pdf</A> |
+                                                                       <A HREF="mandoc.1.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="man.3.html">man(3)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               man macro compiler library
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="man.3.txt">text</A> | 
+                                                                       <A HREF="man.3.xhtml">xhtml</A> |
+                                                                       <A HREF="man.3.pdf">pdf</A> |
+                                                                       <A HREF="man.3.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="mdoc.3.html">mdoc(3)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               mdoc macro compiler library
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="mdoc.3.txt">text</A> | 
+                                                                       <A HREF="mdoc.3.xhtml">xhtml</A> |
+                                                                       <A HREF="mdoc.3.pdf">pdf</A> |
+                                                                       <A HREF="mdoc.3.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="roff.3.html">roff(3)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               roff macro compiler library
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="roff.3.txt">text</A> | 
+                                                                       <A HREF="roff.3.xhtml">xhtml</A> |
+                                                                       <A HREF="roff.3.pdf">pdf</A> |
+                                                                       <A HREF="roff.3.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="man.7.html">man(7)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               man language reference
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="man.7.txt">text</A> | 
+                                                                       <A HREF="man.7.xhtml">xhtml</A> |
+                                                                       <A HREF="man.7.pdf">pdf</A> |
+                                                                       <A HREF="man.7.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="mandoc_char.7.html">mandoc_char(7)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               mandoc special characters
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="mandoc_char.7.txt">text</A> | 
+                                                                       <A HREF="mandoc_char.7.xhtml">xhtml</A> |
+                                                                       <A HREF="mandoc_char.7.pdf">pdf</A> |
+                                                                       <A HREF="mandoc_char.7.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="mdoc.7.html">mdoc(7)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               mdoc language reference
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="mdoc.7.txt">text</A> | 
+                                                                       <A HREF="mdoc.7.xhtml">xhtml</A> |
+                                                                       <A HREF="mdoc.7.pdf">pdf</A> |
+                                                                       <A HREF="mdoc.7.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="roff.7.html">roff(7)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               roff-mandoc language reference
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="roff.7.txt">text</A> | 
+                                                                       <A HREF="roff.7.xhtml">xhtml</A> |
+                                                                       <A HREF="roff.7.pdf">pdf</A> |
+                                                                       <A HREF="roff.7.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                               <TR>
+                                                       <TD VALIGN="top"><A HREF="tbl.7.html">tbl(7)</A></TD>
+                                                       <TD VALIGN="top">
+                                                               tbl-mandoc language reference
+                                                               <SPAN STYLE="font-size: smaller;">
+                                                                       (<A HREF="tbl.7.txt">text</A> | 
+                                                                       <A HREF="tbl.7.xhtml">xhtml</A> |
+                                                                       <A HREF="tbl.7.pdf">pdf</A> |
+                                                                       <A HREF="tbl.7.ps">postscript</A>)
+                                                               </SPAN>
+                                                       </TD>
+                                               </TR>
+                                       </TBODY>
+                                       </TABLE>
+
+                                       <P>
+                                       See <Q><A CLASS="external" HREF="http://manpages.bsd.lv">Writing UNIX Manual Pages</A></Q> for a general
+                                       introduction to manpages and mdoc.
+                                       </P>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD>
+                                       <H1>
+                                       <A NAME="contact">CONTACT</A>
+                                       </H1>
+
+                                       <P>
+                                               Please use the mailing lists for bug-reports, patches, questions, etc. (these require
+                                               subscription).  Beyond that, contact Kristaps at <A
+                                               HREF="http://mailhide.recaptcha.net/d?k=01M6h_w7twDp58ZgH57eWC_w==&amp;c=Q2DBUt401ePlSeupJFrq_Q=="
+                                               TITLE="Reveal this e-mail address">kris...</A>@bsd.lv.
+                                       </P>
+
+                                       <TABLE WIDTH="100%" SUMMARY="Mailing Lists">
+                                               <COL WIDTH="175">
+                                               <COL>
+                                               <TBODY>
+                                                       <TR>
+                                                               <TD>
+                                                                       disc<A CLASS="external" TITLE="Reveal this e-mail address"
+                                                                       HREF="http://www.google.com/recaptcha/mailhide/d?k=01KQ80PFH5n3BBNpF5Gs4sRg==&amp;c=EV1QytpQqTHSItc2IXvZyocgYLPnG5K0JKw_gwMC9yc=">...</A>@mdocml.bsd.lv
+                                                               </TD>
+                                                               <TD>
+                                                                       bug-reports, general questions, and announcements 
+                                                                       <SPAN STYLE="font-size: smaller;">(<A HREF="/archives/discuss/summary.html">archive</A>)</SPAN>
+                                                               </TD>
+                                                       </TR>
+                                                       <TR>
+                                                               <TD>
+                                                                       tec<A CLASS="external" TITLE="Reveal this e-mail address"
+                                                                       HREF="http://www.google.com/recaptcha/mailhide/d?k=01qDX_iV0RlUOarEvb6mR28g==&amp;c=gRXsTjza0NNCFPaYu-Taj2tF0pmYZSc90EZkFkhkxgo=">...</A>@mdocml.bsd.lv
+                                                               </TD>
+                                                               <TD>
+                                                                       patches and system discussions 
+                                                                       <SPAN STYLE="font-size: smaller;">(<A HREF="/archives/tech/summary.html">archive</A>)</SPAN>
+                                                               </TD>
+                                                       </TR>
+                                                       <TR>
+                                                               <TD>
+                                                                       sou<A CLASS="external" TITLE="Reveal this e-mail address"
+                                                                       HREF="http://www.google.com/recaptcha/mailhide/d?k=01prQrAZhhl2EbIwVcRfABsQ==&amp;c=KtTW4Yic9xk-8g40KzJoca4fR3MYXv28g8NC6OQV-T8=">...</A>@mdocml.bsd.lv
+                                                               </TD>
+                                                               <TD>
+                                                                       source commit messages 
+                                                                       <SPAN STYLE="font-size: smaller;">(<A HREF="/archives/source/summary.html">archive</A>)</SPAN>
+                                                               </TD>
+                                                       </TR>
+                                               </TBODY>
+                                       </TABLE>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD>
+                                       <H1>
+                                       <A NAME="news">NEWS</A>
+                                       </H1>
+                                       <DIV CLASS="news">
+                                               <P>
+                                                       <SPAN CLASS="date">07-01-2011</SPAN>:
+                                                       version 1.10.9
+                                               </P>
+                                               <P>
+                                                       Many back-end fixes have been implemented: argument handling (quoting), <A
+                                                       HREF="man.7.html">man</A> improvements, error/warning classes, and many more.
+                                               </P>
+                                               <P>
+                                                       Initial <A HREF="tbl.7.html">tbl</A> functionality (see the <Q>TS</Q>, <Q>TE</Q>, and
+                                                       <Q>T&amp;</Q> macros in the <A HREF="roff.7.html#x5c265453">roff</A> manual) has been
+                                                       merged from <A CLASS="external" HREF="http://tbl.bsd.lv">tbl.bsd.lv</A>.  Output is
+                                                       still minimal, especially for <SPAN CLASS="flag">-Thtml</SPAN> and <SPAN
+                                                       CLASS="flag">-Txhtml</SPAN>, but manages to at least display data.  This means that <A
+                                                       HREF="mandoc.1.html">mandoc</A> now has built-in support for two troff preprocessors via
+                                                       <A HREF="roff.3.html">libroff</A>: soelim and tbl.
+                                               </P>
+                                       </DIV>
+                                       <DIV CLASS="news">
+                                               <P>
+                                                       <SPAN CLASS="date">24-12-2010</SPAN>:
+                                                       version 1.10.8
+                                               </P>
+                                               <P>
+                                                       Significant improvements merged from <A CLASS="external"
+                                                       HREF="http://www.openbsd.org">OpenBSD</A> downstream, including
+                                               </P>
+                                               <UL>
+                                                       <LI>many new <A HREF="roff.7.html">roff</A> components,</LI>
+                                                       <LI>in-line implementation of troff's soelim,</LI>
+                                                       <LI>broken-block handling,</LI>
+                                                       <LI>overhauled error classifications, and</LI>
+                                                       <LI>cleaned up handling of error conditions.</LI>
+                                               </UL>
+                                               <P>
+                                                       Also overhauled the <SPAN CLASS="flag">-Thtml</SPAN> and <SPAN
+                                                       CLASS="flag">-Txhtml</SPAN> output modes.  They now display readable output in arbitrary
+                                                       browsers, including text-based ones like <A CLASS="external"
+                                                       HREF="http://lynx.isc.org">lynx</A>.  See HTML and XHTML manuals in the <A
+                                                       HREF="#documentation">DOCUMENTATION</A> section for examples.  <SPAN
+                                                       CLASS="attn">Attention: available style-sheet classes have been considerably
+                                                       changed!</SPAN> See the <SPAN CLASS="file">example.style.css</SPAN> file for details.
+                                                       Lastly, <A HREF="mdoc.3.html">libmdoc</A> and <A HREF="man.3.html">libman</A> have been
+                                                       cleaned up and reduced in size and complexity.
+                                               </P>
+                                       </DIV>
+                                       <P>
+                                               See <A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/index.sgml?cvsroot=mdocml">cvsweb</A> for
+                                               historical notes.
+                                       </P>
+                               </TD>
+                       </TR>
+                       <TR>
+                               <TD>
+                                       <DIV CLASS="foot">
+                                               Copyright &#169; 2008&#8211;2010 Kristaps Dzonsons, $Date: 2011/01/07 13:10:03 $
+                                       </DIV>
+                               </TD>
+                       </TR>
+               </TBODY>
+       </TABLE>
+       </BODY>
+</HTML>
diff --git a/contrib/mdocml/lib.c b/contrib/mdocml/lib.c
new file mode 100644 (file)
index 0000000..bbf2aec
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $Id: lib.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "mandoc.h"
+#include "libmdoc.h"
+
+#define LINE(x, y) \
+       if (0 == strcmp(p, x)) return(y);
+
+const char *
+mdoc_a2lib(const char *p)
+{
+
+#include "lib.in"
+
+       return(NULL);
+}
diff --git a/contrib/mdocml/lib.in b/contrib/mdocml/lib.in
new file mode 100644 (file)
index 0000000..18ee711
--- /dev/null
@@ -0,0 +1,93 @@
+/*     $Id: lib.in,v 1.9 2010/06/19 20:46:27 kristaps Exp $ */
+/*
+ * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+
+/*
+ * These are all possible .Lb strings.  When a new library is added, add
+ * its short-string to the left-hand side and formatted string to the
+ * right-hand side.
+ *
+ * Be sure to escape strings.
+ */
+
+LINE("libarchive",     "Reading and Writing Streaming Archives Library (libarchive, \\-larchive)")
+LINE("libarm",         "ARM Architecture Library (libarm, \\-larm)")
+LINE("libarm32",       "ARM32 Architecture Library (libarm32, \\-larm32)")
+LINE("libbluetooth",   "Bluetooth Library (libbluetooth, \\-lbluetooth)")
+LINE("libbsm",         "Basic Security Module User Library (libbsm, \\-lbsm)")
+LINE("libc",           "Standard C Library (libc, \\-lc)")
+LINE("libc_r",         "Reentrant C\\~Library (libc_r, \\-lc_r)")
+LINE("libcalendar",    "Calendar Arithmetic Library (libcalendar, \\-lcalendar)")
+LINE("libcam",         "Common Access Method User Library (libcam, \\-lcam)")
+LINE("libcdk",         "Curses Development Kit Library (libcdk, \\-lcdk)")
+LINE("libcipher",      "FreeSec Crypt Library (libcipher, \\-lcipher)")
+LINE("libcompat",      "Compatibility Library (libcompat, \\-lcompat)")
+LINE("libcrypt",       "Crypt Library (libcrypt, \\-lcrypt)")
+LINE("libcurses",      "Curses Library (libcurses, \\-lcurses)")
+LINE("libdevinfo",     "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)")
+LINE("libdevstat",     "Device Statistics Library (libdevstat, \\-ldevstat)")
+LINE("libdisk",                "Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)")
+LINE("libedit",                "Command Line Editor Library (libedit, \\-ledit)")
+LINE("libelf",         "ELF Parsing Library (libelf, \\-lelf)")
+LINE("libevent",       "Event Notification Library (libevent, \\-levent)")
+LINE("libfetch",       "File Transfer Library for URLs (libfetch, \\-lfetch)")
+LINE("libform",                "Curses Form Library (libform, \\-lform)")
+LINE("libgeom",                "Userland API Library for kernel GEOM subsystem (libgeom, \\-lgeom)")
+LINE("libgpib",                "General-Purpose Instrument Bus (GPIB) library (libgpib, \\-lgpib)")
+LINE("libi386",                "i386 Architecture Library (libi386, \\-li386)")
+LINE("libintl",                "Internationalized Message Handling Library (libintl, \\-lintl)")
+LINE("libipsec",       "IPsec Policy Control Library (libipsec, \\-lipsec)")
+LINE("libipx",         "IPX Address Conversion Support Library (libipx, \\-lipx)")
+LINE("libiscsi",       "iSCSI protocol library (libiscsi, \\-liscsi)")
+LINE("libjail",                "Jail Library (libjail, \\-ljail)")
+LINE("libkiconv",      "Kernel side iconv library (libkiconv, \\-lkiconv)")
+LINE("libkse",         "N:M Threading Library (libkse, \\-lkse)")
+LINE("libkvm",         "Kernel Data Access Library (libkvm, \\-lkvm)")
+LINE("libm",           "Math Library (libm, \\-lm)")
+LINE("libm68k",                "m68k Architecture Library (libm68k, \\-lm68k)")
+LINE("libmagic",       "Magic Number Recognition Library (libmagic, \\-lmagic)")
+LINE("libmd",          "Message Digest (MD4, MD5, etc.) Support Library (libmd, \\-lmd)")
+LINE("libmemstat",     "Kernel Memory Allocator Statistics Library (libmemstat, \\-lmemstat)")
+LINE("libmenu",                "Curses Menu Library (libmenu, \\-lmenu)")
+LINE("libnetgraph",    "Netgraph User Library (libnetgraph, \\-lnetgraph)")
+LINE("libnetpgp",      "Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)")
+LINE("libossaudio",    "OSS Audio Emulation Library (libossaudio, \\-lossaudio)")
+LINE("libpam",         "Pluggable Authentication Module Library (libpam, \\-lpam)")
+LINE("libpcap",                "Capture Library (libpcap, \\-lpcap)")
+LINE("libpci",         "PCI Bus Access Library (libpci, \\-lpci)")
+LINE("libpmc",         "Performance Counters Library (libpmc, \\-lpmc)")
+LINE("libposix",       "POSIX Compatibility Library (libposix, \\-lposix)")
+LINE("libprop",                "Property Container Object Library (libprop, \\-lprop)")
+LINE("libpthread",     "POSIX Threads Library (libpthread, \\-lpthread)")
+LINE("libpuffs",       "puffs Convenience Library (libpuffs, \\-lpuffs)")
+LINE("librefuse",      "File System in Userspace Convenience Library (librefuse, \\-lrefuse)")
+LINE("libresolv",      "DNS Resolver Library (libresolv, \\-lresolv)")
+LINE("librpcsec_gss",  "RPC GSS-API Authentication Library (librpcsec_gss, \\-lrpcsec_gss)")
+LINE("librpcsvc",      "RPC Service Library (librpcsvc, \\-lrpcsvc)")
+LINE("librt",          "POSIX Real\\-time Library (librt, -lrt)")
+LINE("libsdp",         "Bluetooth Service Discovery Protocol User Library (libsdp, \\-lsdp)")
+LINE("libssp",         "Buffer Overflow Protection Library (libssp, \\-lssp)")
+LINE("libtermcap",     "Termcap Access Library (libtermcap, \\-ltermcap)")
+LINE("libterminfo",    "Terminal Information Library (libterminfo, \\-lterminfo)")
+LINE("libthr",         "1:1 Threading Library (libthr, \\-lthr)")
+LINE("libufs",         "UFS File System Access Library (libufs, \\-lufs)")
+LINE("libugidfw",      "File System Firewall Interface Library (libugidfw, \\-lugidfw)")
+LINE("libulog",                "User Login Record Library (libulog, \\-lulog)")
+LINE("libusbhid",      "USB Human Interface Devices Library (libusbhid, \\-lusbhid)")
+LINE("libutil",                "System Utilities Library (libutil, \\-lutil)")
+LINE("libvgl",         "Video Graphics Library (libvgl, \\-lvgl)")
+LINE("libx86_64",      "x86_64 Architecture Library (libx86_64, \\-lx86_64)")
+LINE("libz",           "Compression Library (libz, \\-lz)")
diff --git a/contrib/mdocml/libman.h b/contrib/mdocml/libman.h
new file mode 100644 (file)
index 0000000..e1a9aec
--- /dev/null
@@ -0,0 +1,92 @@
+/*     $Id: libman.h,v 1.44 2010/11/30 15:36:28 kristaps Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef LIBMAN_H
+#define LIBMAN_H
+
+#include "man.h"
+
+enum   man_next {
+       MAN_NEXT_SIBLING = 0,
+       MAN_NEXT_CHILD
+};
+
+struct man {
+       void            *data; /* private application data */
+       mandocmsg        msg; /* output message handler */
+       int              flags; /* parse flags */
+#define        MAN_HALT        (1 << 0) /* badness happened: die */
+#define        MAN_ELINE       (1 << 1) /* Next-line element scope. */
+#define        MAN_BLINE       (1 << 2) /* Next-line block scope. */
+#define        MAN_ILINE       (1 << 3) /* Ignored in next-line scope. */
+#define        MAN_LITERAL     (1 << 4) /* Literal input. */
+#define        MAN_BPLINE      (1 << 5)
+       enum man_next    next; /* where to put the next node */
+       struct man_node *last; /* the last parsed node */
+       struct man_node *first; /* the first parsed node */
+       struct man_meta  meta; /* document meta-data */
+       struct regset   *regs; /* registers */
+};
+
+#define        MACRO_PROT_ARGS   struct man *m, \
+                         enum mant tok, \
+                         int line, \
+                         int ppos, \
+                         int *pos, \
+                         char *buf
+
+struct man_macro {
+       int             (*fp)(MACRO_PROT_ARGS);
+       int               flags;
+#define        MAN_SCOPED       (1 << 0)
+#define        MAN_EXPLICIT     (1 << 1)       /* See blk_imp(). */
+#define        MAN_FSCOPED      (1 << 2)       /* See blk_imp(). */
+#define        MAN_NSCOPED      (1 << 3)       /* See in_line_eoln(). */
+#define        MAN_NOCLOSE      (1 << 4)       /* See blk_exp(). */
+};
+
+extern const struct man_macro *const man_macros;
+
+__BEGIN_DECLS
+
+#define                  man_pmsg(m, l, p, t) \
+                 (*(m)->msg)((t), (m)->data, (l), (p), NULL)
+#define                  man_nmsg(m, n, t) \
+                 (*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
+int              man_word_alloc(struct man *, int, int, const char *);
+int              man_block_alloc(struct man *, int, int, enum mant);
+int              man_head_alloc(struct man *, int, int, enum mant);
+int              man_body_alloc(struct man *, int, int, enum mant);
+int              man_elem_alloc(struct man *, int, int, enum mant);
+void             man_node_delete(struct man *, struct man_node *);
+void             man_hash_init(void);
+enum   mant      man_hash_find(const char *);
+int              man_macroend(struct man *);
+int              man_args(struct man *, int, int *, char *, char **);
+#define        ARGS_ERROR      (-1)
+#define        ARGS_EOLN       (0)
+#define        ARGS_WORD       (1)
+#define        ARGS_QWORD      (1)
+int              man_vmsg(struct man *, enum mandocerr,
+                       int, int, const char *, ...);
+int              man_valid_post(struct man *);
+int              man_valid_pre(struct man *, struct man_node *);
+int              man_unscope(struct man *, 
+                       const struct man_node *, enum mandocerr);
+
+__END_DECLS
+
+#endif /*!LIBMAN_H*/
diff --git a/contrib/mdocml/libmandoc.h b/contrib/mdocml/libmandoc.h
new file mode 100644 (file)
index 0000000..0e9a749
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $Id: libmandoc.h,v 1.10 2011/01/03 22:42:37 schwarze Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef LIBMANDOC_H
+#define LIBMANDOC_H
+
+__BEGIN_DECLS
+
+int             mandoc_special(char *);
+void           *mandoc_calloc(size_t, size_t);
+char           *mandoc_strdup(const char *);
+void           *mandoc_malloc(size_t);
+void           *mandoc_realloc(void *, size_t);
+char           *mandoc_getarg(char **, mandocmsg, void *, int, int *);
+time_t          mandoc_a2time(int, const char *);
+#define                 MTIME_CANONICAL        (1 << 0)
+#define                 MTIME_REDUCED          (1 << 1)
+#define                 MTIME_MDOCDATE         (1 << 2)
+#define                 MTIME_ISO_8601         (1 << 3)
+int             mandoc_eos(const char *, size_t, int);
+int             mandoc_hyph(const char *, const char *);
+
+__END_DECLS
+
+#endif /*!LIBMANDOC_H*/
diff --git a/contrib/mdocml/libmdoc.h b/contrib/mdocml/libmdoc.h
new file mode 100644 (file)
index 0000000..5a46d1c
--- /dev/null
@@ -0,0 +1,147 @@
+/*     $Id: libmdoc.h,v 1.63 2010/11/30 13:04:14 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef LIBMDOC_H
+#define LIBMDOC_H
+
+#include "mdoc.h"
+
+enum   mdoc_next {
+       MDOC_NEXT_SIBLING = 0,
+       MDOC_NEXT_CHILD
+};
+
+struct mdoc {
+       void             *data; /* private application data */
+       mandocmsg         msg; /* message callback */
+       int               flags;
+#define        MDOC_HALT        (1 << 0) /* error in parse: halt */
+#define        MDOC_LITERAL     (1 << 1) /* in a literal scope */
+#define        MDOC_PBODY       (1 << 2) /* in the document body */
+#define        MDOC_NEWLINE     (1 << 3) /* first macro/text in a line */
+#define        MDOC_PHRASELIT   (1 << 4) /* literal within a partila phrase */
+#define        MDOC_PPHRASE     (1 << 5) /* within a partial phrase */
+#define        MDOC_FREECOL     (1 << 6) /* `It' invocation should close */
+#define        MDOC_SYNOPSIS    (1 << 7) /* SYNOPSIS-style formatting */
+       enum mdoc_next    next; /* where to put the next node */
+       struct mdoc_node *last; /* the last node parsed */
+       struct mdoc_node *first; /* the first node parsed */
+       struct mdoc_meta  meta; /* document meta-data */
+       enum mdoc_sec     lastnamed;
+       enum mdoc_sec     lastsec;
+       struct regset    *regs; /* registers */
+};
+
+#define        MACRO_PROT_ARGS struct mdoc *m, \
+                       enum mdoct tok, \
+                       int line, \
+                       int ppos, \
+                       int *pos, \
+                       char *buf
+
+struct mdoc_macro {
+       int             (*fp)(MACRO_PROT_ARGS);
+       int               flags;
+#define        MDOC_CALLABLE    (1 << 0)
+#define        MDOC_PARSED      (1 << 1)
+#define        MDOC_EXPLICIT    (1 << 2)
+#define        MDOC_PROLOGUE    (1 << 3)
+#define        MDOC_IGNDELIM    (1 << 4) 
+       /* Reserved words in arguments treated as text. */
+};
+
+enum   margserr {
+       ARGS_ERROR,
+       ARGS_EOLN,
+       ARGS_WORD,
+       ARGS_PUNCT,
+       ARGS_QWORD,
+       ARGS_PHRASE,
+       ARGS_PPHRASE,
+       ARGS_PEND
+};
+
+enum   margverr {
+       ARGV_ERROR,
+       ARGV_EOLN,
+       ARGV_ARG,
+       ARGV_WORD
+};
+
+enum   mdelim {
+       DELIM_NONE = 0,
+       DELIM_OPEN,
+       DELIM_MIDDLE,
+       DELIM_CLOSE
+};
+
+extern const struct mdoc_macro *const mdoc_macros;
+
+__BEGIN_DECLS
+
+#define                  mdoc_pmsg(m, l, p, t) \
+                 (*(m)->msg)((t), (m)->data, (l), (p), NULL)
+#define                  mdoc_nmsg(m, n, t) \
+                 (*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
+int              mdoc_vmsg(struct mdoc *, enum mandocerr, 
+                       int, int, const char *, ...);
+int              mdoc_macro(MACRO_PROT_ARGS);
+int              mdoc_word_alloc(struct mdoc *, 
+                       int, int, const char *);
+int              mdoc_elem_alloc(struct mdoc *, int, int, 
+                       enum mdoct, struct mdoc_arg *);
+int              mdoc_block_alloc(struct mdoc *, int, int, 
+                       enum mdoct, struct mdoc_arg *);
+int              mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
+int              mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
+int              mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
+int              mdoc_endbody_alloc(struct mdoc *m, int line, int pos,
+                       enum mdoct tok, struct mdoc_node *body,
+                       enum mdoc_endbody end);
+void             mdoc_node_delete(struct mdoc *, struct mdoc_node *);
+void             mdoc_hash_init(void);
+enum mdoct       mdoc_hash_find(const char *);
+enum mdelim      mdoc_iscdelim(char);
+enum mdelim      mdoc_isdelim(const char *);
+size_t           mdoc_isescape(const char *);
+enum   mdoc_sec  mdoc_str2sec(const char *);
+time_t           mdoc_atotime(const char *);
+size_t           mdoc_macro2len(enum mdoct);
+const char      *mdoc_a2att(const char *);
+const char      *mdoc_a2lib(const char *);
+const char      *mdoc_a2st(const char *);
+const char      *mdoc_a2arch(const char *);
+const char      *mdoc_a2vol(const char *);
+const char      *mdoc_a2msec(const char *);
+int              mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
+int              mdoc_valid_post(struct mdoc *);
+enum margverr    mdoc_argv(struct mdoc *, int, enum mdoct,
+                       struct mdoc_arg **, int *, char *);
+void             mdoc_argv_free(struct mdoc_arg *);
+void             mdoc_argn_free(struct mdoc_arg *, int);
+enum margserr    mdoc_args(struct mdoc *, int,
+                       int *, char *, enum mdoct, char **);
+enum margserr    mdoc_zargs(struct mdoc *, int, 
+                       int *, char *, int, char **);
+#define        ARGS_DELIM      (1 << 1)
+#define        ARGS_TABSEP     (1 << 2)
+#define        ARGS_NOWARN     (1 << 3)
+
+int              mdoc_macroend(struct mdoc *);
+
+__END_DECLS
+
+#endif /*!LIBMDOC_H*/
diff --git a/contrib/mdocml/libroff.h b/contrib/mdocml/libroff.h
new file mode 100644 (file)
index 0000000..b4e043a
--- /dev/null
@@ -0,0 +1,62 @@
+/*     $Id: libroff.h,v 1.16 2011/01/04 15:02:00 kristaps Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef LIBROFF_H
+#define LIBROFF_H
+
+__BEGIN_DECLS
+
+enum   tbl_part {
+       TBL_PART_OPTS, /* in options (first line) */
+       TBL_PART_LAYOUT, /* describing layout */
+       TBL_PART_DATA, /* creating data rows */
+       TBL_PART_CDATA /* continue previous row */
+};
+
+struct tbl_node {
+       mandocmsg         msg; /* status messages */
+       void             *data; /* privdata for messages */
+       int               pos; /* invocation column */
+       int               line; /* invocation line */
+       enum tbl_part     part;
+       struct tbl        opts;
+       struct tbl_row   *first_row;
+       struct tbl_row   *last_row;
+       struct tbl_span  *first_span;
+       struct tbl_span  *last_span;
+       struct tbl_head  *first_head;
+       struct tbl_head  *last_head;
+       struct tbl_node  *next;
+};
+
+#define        TBL_MSG(tblp, type, line, col) \
+       (*(tblp)->msg)((type), (tblp)->data, (line), (col), NULL)
+
+struct tbl_node        *tbl_alloc(int, int, void *, mandocmsg);
+void            tbl_restart(int, int, struct tbl_node *);
+void            tbl_free(struct tbl_node *);
+void            tbl_reset(struct tbl_node *);
+enum rofferr    tbl_read(struct tbl_node *, int, const char *, int);
+int             tbl_option(struct tbl_node *, int, const char *);
+int             tbl_layout(struct tbl_node *, int, const char *);
+int             tbl_data(struct tbl_node *, int, const char *);
+int             tbl_cdata(struct tbl_node *, int, const char *);
+const struct tbl_span *tbl_span(const struct tbl_node *);
+void            tbl_end(struct tbl_node *);
+
+__END_DECLS
+
+#endif /*LIBROFF_H*/
diff --git a/contrib/mdocml/main.c b/contrib/mdocml/main.c
new file mode 100644 (file)
index 0000000..2be68a9
--- /dev/null
@@ -0,0 +1,1040 @@
+/*     $Id: main.c,v 1.135 2011/01/04 15:02:00 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc.h"
+#include "main.h"
+#include "mdoc.h"
+#include "man.h"
+#include "roff.h"
+
+#ifndef MAP_FILE
+#define        MAP_FILE        0
+#endif
+
+#define        REPARSE_LIMIT   1000
+#define        UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))
+
+/* FIXME: Intel's compiler?  LLVM?  pcc?  */
+
+#if !defined(__GNUC__) || (__GNUC__ < 2)
+# if !defined(lint)
+#  define __attribute__(x)
+# endif
+#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
+
+typedef        void            (*out_mdoc)(void *, const struct mdoc *);
+typedef        void            (*out_man)(void *, const struct man *);
+typedef        void            (*out_free)(void *);
+
+struct buf {
+       char             *buf;
+       size_t            sz;
+};
+
+enum   intt {
+       INTT_AUTO,
+       INTT_MDOC,
+       INTT_MAN
+};
+
+enum   outt {
+       OUTT_ASCII = 0,
+       OUTT_TREE,
+       OUTT_HTML,
+       OUTT_XHTML,
+       OUTT_LINT,
+       OUTT_PS,
+       OUTT_PDF
+};
+
+struct curparse {
+       const char       *file;         /* Current parse. */
+       int               fd;           /* Current parse. */
+       int               line;         /* Line number in the file. */
+       enum mandoclevel  wlevel;       /* Ignore messages below this. */
+       int               wstop;        /* Stop after a file with a warning. */
+       enum intt         inttype;      /* which parser to use */
+       struct man       *pman;         /* persistent man parser */
+       struct mdoc      *pmdoc;        /* persistent mdoc parser */
+       struct man       *man;          /* man parser */
+       struct mdoc      *mdoc;         /* mdoc parser */
+       struct roff      *roff;         /* roff parser (!NULL) */
+       struct regset     regs;         /* roff registers */
+       int               reparse_count; /* finite interpolation stack */
+       enum outt         outtype;      /* which output to use */
+       out_mdoc          outmdoc;      /* mdoc output ptr */
+       out_man           outman;       /* man output ptr */
+       out_free          outfree;      /* free output ptr */
+       void             *outdata;      /* data for output */
+       char              outopts[BUFSIZ]; /* buf of output opts */
+};
+
+static const char * const      mandoclevels[MANDOCLEVEL_MAX] = {
+       "SUCCESS",
+       "RESERVED",
+       "WARNING",
+       "ERROR",
+       "FATAL",
+       "BADARG",
+       "SYSERR"
+};
+
+static const enum mandocerr    mandoclimits[MANDOCLEVEL_MAX] = {
+       MANDOCERR_OK,
+       MANDOCERR_WARNING,
+       MANDOCERR_WARNING,
+       MANDOCERR_ERROR,
+       MANDOCERR_FATAL,
+       MANDOCERR_MAX,
+       MANDOCERR_MAX
+};
+
+static const char * const      mandocerrs[MANDOCERR_MAX] = {
+       "ok",
+
+       "generic warning",
+
+       /* related to the prologue */
+       "no title in document",
+       "document title should be all caps",
+       "unknown manual section",
+       "cannot parse date argument",
+       "prologue macros out of order",
+       "duplicate prologue macro",
+       "macro not allowed in prologue",
+       "macro not allowed in body",
+
+       /* related to document structure */
+       ".so is fragile, better use ln(1)",
+       "NAME section must come first",
+       "bad NAME section contents",
+       "manual name not yet set",
+       "sections out of conventional order",
+       "duplicate section name",
+       "section not in conventional manual section",
+
+       /* related to macros and nesting */
+       "skipping obsolete macro",
+       "skipping paragraph macro",
+       "blocks badly nested",
+       "child violates parent syntax",
+       "nested displays are not portable",
+       "already in literal mode",
+
+       /* related to missing macro arguments */
+       "skipping empty macro",
+       "argument count wrong",
+       "missing display type",
+       "list type must come first",
+       "tag lists require a width argument",
+       "missing font type",
+
+       /* related to bad macro arguments */
+       "skipping argument",
+       "duplicate argument",
+       "duplicate display type",
+       "duplicate list type",
+       "unknown AT&T UNIX version",
+       "bad Boolean value",
+       "unknown font",
+       "unknown standard specifier",
+       "bad width argument",
+
+       /* related to plain text */
+       "blank line in non-literal context",
+       "tab in non-literal context",
+       "end of line whitespace",
+       "bad comment style",
+       "unknown escape sequence",
+       "unterminated quoted string",
+       
+       /* related to tables */
+       "extra data cells",
+
+       "generic error",
+
+       /* related to tables */
+       "bad table syntax",
+       "bad table option",
+       "bad table layout",
+       "no table layout cells specified",
+       "no table data cells specified",
+       "ignore data in cell",
+       "data block still open",
+
+       "input stack limit exceeded, infinite loop?",
+       "skipping bad character",
+       "skipping text before the first section header",
+       "skipping unknown macro",
+       "NOT IMPLEMENTED: skipping request",
+       "line scope broken",
+       "argument count wrong",
+       "skipping end of block that is not open",
+       "missing end of block",
+       "scope open on exit",
+       "uname(3) system call failed",
+       "macro requires line argument(s)",
+       "macro requires body argument(s)",
+       "macro requires argument(s)",
+       "missing list type",
+       "line argument(s) will be lost",
+       "body argument(s) will be lost",
+
+       "generic fatal error",
+
+       "column syntax is inconsistent",
+       "NOT IMPLEMENTED: .Bd -file",
+       "line scope broken, syntax violated",
+       "argument count wrong, violates syntax",
+       "child violates parent syntax",
+       "argument count wrong, violates syntax",
+       "NOT IMPLEMENTED: .so with absolute path or \"..\"",
+       "no document body",
+       "no document prologue",
+       "static buffer exhausted",
+};
+
+static void              parsebuf(struct curparse *, struct buf, int);
+static void              pdesc(struct curparse *);
+static void              fdesc(struct curparse *);
+static void              ffile(const char *, struct curparse *);
+static int               pfile(const char *, struct curparse *);
+static int               moptions(enum intt *, char *);
+static int               mmsg(enum mandocerr, void *, 
+                               int, int, const char *);
+static void              pset(const char *, int, struct curparse *);
+static int               toptions(struct curparse *, char *);
+static void              usage(void) __attribute__((noreturn));
+static void              version(void) __attribute__((noreturn));
+static int               woptions(struct curparse *, char *);
+
+static const char       *progname;
+static enum mandoclevel  file_status = MANDOCLEVEL_OK;
+static enum mandoclevel  exit_status = MANDOCLEVEL_OK;
+
+int
+main(int argc, char *argv[])
+{
+       int              c;
+       struct curparse  curp;
+
+       progname = strrchr(argv[0], '/');
+       if (progname == NULL)
+               progname = argv[0];
+       else
+               ++progname;
+
+       memset(&curp, 0, sizeof(struct curparse));
+
+       curp.inttype = INTT_AUTO;
+       curp.outtype = OUTT_ASCII;
+       curp.wlevel  = MANDOCLEVEL_FATAL;
+
+       /* LINTED */
+       while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
+               switch (c) {
+               case ('m'):
+                       if ( ! moptions(&curp.inttype, optarg))
+                               return((int)MANDOCLEVEL_BADARG);
+                       break;
+               case ('O'):
+                       (void)strlcat(curp.outopts, optarg, BUFSIZ);
+                       (void)strlcat(curp.outopts, ",", BUFSIZ);
+                       break;
+               case ('T'):
+                       if ( ! toptions(&curp, optarg))
+                               return((int)MANDOCLEVEL_BADARG);
+                       break;
+               case ('W'):
+                       if ( ! woptions(&curp, optarg))
+                               return((int)MANDOCLEVEL_BADARG);
+                       break;
+               case ('V'):
+                       version();
+                       /* NOTREACHED */
+               default:
+                       usage();
+                       /* NOTREACHED */
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       if (NULL == *argv) {
+               curp.file = "<stdin>";
+               curp.fd = STDIN_FILENO;
+
+               fdesc(&curp);
+       }
+
+       while (*argv) {
+               ffile(*argv, &curp);
+               if (MANDOCLEVEL_OK != exit_status && curp.wstop)
+                       break;
+               ++argv;
+       }
+
+       if (curp.outfree)
+               (*curp.outfree)(curp.outdata);
+       if (curp.pmdoc)
+               mdoc_free(curp.pmdoc);
+       if (curp.pman)
+               man_free(curp.pman);
+       if (curp.roff)
+               roff_free(curp.roff);
+
+       return((int)exit_status);
+}
+
+
+static void
+version(void)
+{
+
+       (void)printf("%s %s\n", progname, VERSION);
+       exit((int)MANDOCLEVEL_OK);
+}
+
+
+static void
+usage(void)
+{
+
+       (void)fprintf(stderr, "usage: %s "
+                       "[-V] "
+                       "[-foption] "
+                       "[-mformat] "
+                       "[-Ooption] "
+                       "[-Toutput] "
+                       "[-Werr] "
+                       "[file...]\n", 
+                       progname);
+
+       exit((int)MANDOCLEVEL_BADARG);
+}
+
+static void
+ffile(const char *file, struct curparse *curp)
+{
+
+       /*
+        * Called once per input file.  Get the file ready for reading,
+        * pass it through to the parser-driver, then close it out.
+        * XXX: don't do anything special as this is only called for
+        * files; stdin goes directly to fdesc().
+        */
+
+       curp->file = file;
+
+       if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
+               perror(curp->file);
+               exit_status = MANDOCLEVEL_SYSERR;
+               return;
+       }
+
+       fdesc(curp);
+
+       if (-1 == close(curp->fd))
+               perror(curp->file);
+}
+
+static int
+pfile(const char *file, struct curparse *curp)
+{
+       const char      *savefile;
+       int              fd, savefd;
+
+       if (-1 == (fd = open(file, O_RDONLY, 0))) {
+               perror(file);
+               file_status = MANDOCLEVEL_SYSERR;
+               return(0);
+       }
+
+       savefile = curp->file;
+       savefd = curp->fd;
+
+       curp->file = file;
+       curp->fd = fd;
+
+       pdesc(curp);
+
+       curp->file = savefile;
+       curp->fd = savefd;
+
+       if (-1 == close(fd))
+               perror(file);
+
+       return(MANDOCLEVEL_FATAL > file_status ? 1 : 0);
+}
+
+
+static void
+resize_buf(struct buf *buf, size_t initial)
+{
+
+       buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
+       buf->buf = realloc(buf->buf, buf->sz);
+       if (NULL == buf->buf) {
+               perror(NULL);
+               exit((int)MANDOCLEVEL_SYSERR);
+       }
+}
+
+
+static int
+read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
+{
+       struct stat      st;
+       size_t           off;
+       ssize_t          ssz;
+
+       if (-1 == fstat(curp->fd, &st)) {
+               perror(curp->file);
+               return(0);
+       }
+
+       /*
+        * If we're a regular file, try just reading in the whole entry
+        * via mmap().  This is faster than reading it into blocks, and
+        * since each file is only a few bytes to begin with, I'm not
+        * concerned that this is going to tank any machines.
+        */
+
+       if (S_ISREG(st.st_mode)) {
+               if (st.st_size >= (1U << 31)) {
+                       fprintf(stderr, "%s: input too large\n", 
+                                       curp->file);
+                       return(0);
+               }
+               *with_mmap = 1;
+               fb->sz = (size_t)st.st_size;
+               fb->buf = mmap(NULL, fb->sz, PROT_READ, 
+                               MAP_FILE|MAP_SHARED, curp->fd, 0);
+               if (fb->buf != MAP_FAILED)
+                       return(1);
+       }
+
+       /*
+        * If this isn't a regular file (like, say, stdin), then we must
+        * go the old way and just read things in bit by bit.
+        */
+
+       *with_mmap = 0;
+       off = 0;
+       fb->sz = 0;
+       fb->buf = NULL;
+       for (;;) {
+               if (off == fb->sz) {
+                       if (fb->sz == (1U << 31)) {
+                               fprintf(stderr, "%s: input too large\n", 
+                                               curp->file);
+                               break;
+                       }
+                       resize_buf(fb, 65536);
+               }
+               ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
+               if (ssz == 0) {
+                       fb->sz = off;
+                       return(1);
+               }
+               if (ssz == -1) {
+                       perror(curp->file);
+                       break;
+               }
+               off += (size_t)ssz;
+       }
+
+       free(fb->buf);
+       fb->buf = NULL;
+       return(0);
+}
+
+
+static void
+fdesc(struct curparse *curp)
+{
+
+       /*
+        * Called once per file with an opened file descriptor.  All
+        * pre-file-parse operations (whether stdin or a file) should go
+        * here.
+        *
+        * This calls down into the nested parser, which drills down and
+        * fully parses a file and all its dependences (i.e., `so').  It
+        * then runs the cleanup validators and pushes to output.
+        */
+
+       /* Zero the parse type. */
+
+       curp->mdoc = NULL;
+       curp->man = NULL;
+       file_status = MANDOCLEVEL_OK;
+
+       /* Make sure the mandotory roff parser is initialised. */
+
+       if (NULL == curp->roff) {
+               curp->roff = roff_alloc(&curp->regs, curp, mmsg);
+               assert(curp->roff);
+       }
+
+       /* Fully parse the file. */
+
+       pdesc(curp);
+
+       if (MANDOCLEVEL_FATAL <= file_status)
+               goto cleanup;
+
+       /* NOTE a parser may not have been assigned, yet. */
+
+       if ( ! (curp->man || curp->mdoc)) {
+               fprintf(stderr, "%s: Not a manual\n", curp->file);
+               file_status = MANDOCLEVEL_FATAL;
+               goto cleanup;
+       }
+
+       /* Clean up the parse routine ASTs. */
+
+       if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
+               assert(MANDOCLEVEL_FATAL <= file_status);
+               goto cleanup;
+       }
+
+       if (curp->man && ! man_endparse(curp->man)) {
+               assert(MANDOCLEVEL_FATAL <= file_status);
+               goto cleanup;
+       }
+
+       assert(curp->roff);
+       roff_endparse(curp->roff);
+
+       /*
+        * With -Wstop and warnings or errors of at least
+        * the requested level, do not produce output.
+        */
+
+       if (MANDOCLEVEL_OK != file_status && curp->wstop)
+               goto cleanup;
+
+       /* If unset, allocate output dev now (if applicable). */
+
+       if ( ! (curp->outman && curp->outmdoc)) {
+               switch (curp->outtype) {
+               case (OUTT_XHTML):
+                       curp->outdata = xhtml_alloc(curp->outopts);
+                       break;
+               case (OUTT_HTML):
+                       curp->outdata = html_alloc(curp->outopts);
+                       break;
+               case (OUTT_ASCII):
+                       curp->outdata = ascii_alloc(curp->outopts);
+                       curp->outfree = ascii_free;
+                       break;
+               case (OUTT_PDF):
+                       curp->outdata = pdf_alloc(curp->outopts);
+                       curp->outfree = pspdf_free;
+                       break;
+               case (OUTT_PS):
+                       curp->outdata = ps_alloc(curp->outopts);
+                       curp->outfree = pspdf_free;
+                       break;
+               default:
+                       break;
+               }
+
+               switch (curp->outtype) {
+               case (OUTT_HTML):
+                       /* FALLTHROUGH */
+               case (OUTT_XHTML):
+                       curp->outman = html_man;
+                       curp->outmdoc = html_mdoc;
+                       curp->outfree = html_free;
+                       break;
+               case (OUTT_TREE):
+                       curp->outman = tree_man;
+                       curp->outmdoc = tree_mdoc;
+                       break;
+               case (OUTT_PDF):
+                       /* FALLTHROUGH */
+               case (OUTT_ASCII):
+                       /* FALLTHROUGH */
+               case (OUTT_PS):
+                       curp->outman = terminal_man;
+                       curp->outmdoc = terminal_mdoc;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* Execute the out device, if it exists. */
+
+       if (curp->man && curp->outman)
+               (*curp->outman)(curp->outdata, curp->man);
+       if (curp->mdoc && curp->outmdoc)
+               (*curp->outmdoc)(curp->outdata, curp->mdoc);
+
+ cleanup:
+
+       memset(&curp->regs, 0, sizeof(struct regset));
+
+       /* Reset the current-parse compilers. */
+
+       if (curp->mdoc)
+               mdoc_reset(curp->mdoc);
+       if (curp->man)
+               man_reset(curp->man);
+
+       assert(curp->roff);
+       roff_reset(curp->roff);
+
+       if (exit_status < file_status)
+               exit_status = file_status;
+
+       return;
+}
+
+static void
+pdesc(struct curparse *curp)
+{
+       struct buf       blk;
+       int              with_mmap;
+
+       /*
+        * Run for each opened file; may be called more than once for
+        * each full parse sequence if the opened file is nested (i.e.,
+        * from `so').  Simply sucks in the whole file and moves into
+        * the parse phase for the file.
+        */
+
+       if ( ! read_whole_file(curp, &blk, &with_mmap)) {
+               file_status = MANDOCLEVEL_SYSERR;
+               return;
+       }
+
+       /* Line number is per-file. */
+
+       curp->line = 1;
+
+       parsebuf(curp, blk, 1);
+
+       if (with_mmap)
+               munmap(blk.buf, blk.sz);
+       else
+               free(blk.buf);
+}
+
+static void
+parsebuf(struct curparse *curp, struct buf blk, int start)
+{
+       struct buf       ln;
+       enum rofferr     rr;
+       int              i, of, rc;
+       int              pos; /* byte number in the ln buffer */
+       int              lnn; /* line number in the real file */
+       unsigned char    c;
+
+       /*
+        * Main parse routine for an opened file.  This is called for
+        * each opened file and simply loops around the full input file,
+        * possibly nesting (i.e., with `so').
+        */
+
+       memset(&ln, 0, sizeof(struct buf));
+
+       lnn = curp->line; 
+       pos = 0; 
+
+       for (i = 0; i < (int)blk.sz; ) {
+               if (0 == pos && '\0' == blk.buf[i])
+                       break;
+
+               if (start) {
+                       curp->line = lnn;
+                       curp->reparse_count = 0;
+               }
+
+               while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
+                       if ('\n' == blk.buf[i]) {
+                               ++i;
+                               ++lnn;
+                               break;
+                       }
+
+                       /* 
+                        * Warn about bogus characters.  If you're using
+                        * non-ASCII encoding, you're screwing your
+                        * readers.  Since I'd rather this not happen,
+                        * I'll be helpful and drop these characters so
+                        * we don't display gibberish.  Note to manual
+                        * writers: use special characters.
+                        */
+
+                       c = (unsigned char) blk.buf[i];
+
+                       if ( ! (isascii(c) && 
+                                       (isgraph(c) || isblank(c)))) {
+                               mmsg(MANDOCERR_BADCHAR, curp, 
+                                   curp->line, pos, "ignoring byte");
+                               i++;
+                               continue;
+                       }
+
+                       /* Trailing backslash = a plain char. */
+
+                       if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
+                               if (pos >= (int)ln.sz)
+                                       resize_buf(&ln, 256);
+                               ln.buf[pos++] = blk.buf[i++];
+                               continue;
+                       }
+
+                       /* Found escape & at least one other char. */
+
+                       if ('\n' == blk.buf[i + 1]) {
+                               i += 2;
+                               /* Escaped newlines are skipped over */
+                               ++lnn;
+                               continue;
+                       }
+
+                       if ('"' == blk.buf[i + 1]) {
+                               i += 2;
+                               /* Comment, skip to end of line */
+                               for (; i < (int)blk.sz; ++i) {
+                                       if ('\n' == blk.buf[i]) {
+                                               ++i;
+                                               ++lnn;
+                                               break;
+                                       }
+                               }
+
+                               /* Backout trailing whitespaces */
+                               for (; pos > 0; --pos) {
+                                       if (ln.buf[pos - 1] != ' ')
+                                               break;
+                                       if (pos > 2 && ln.buf[pos - 2] == '\\')
+                                               break;
+                               }
+                               break;
+                       }
+
+                       /* Some other escape sequence, copy & cont. */
+
+                       if (pos + 1 >= (int)ln.sz)
+                               resize_buf(&ln, 256);
+
+                       ln.buf[pos++] = blk.buf[i++];
+                       ln.buf[pos++] = blk.buf[i++];
+               }
+
+               if (pos >= (int)ln.sz)
+                       resize_buf(&ln, 256);
+
+               ln.buf[pos] = '\0';
+
+               /*
+                * A significant amount of complexity is contained by
+                * the roff preprocessor.  It's line-oriented but can be
+                * expressed on one line, so we need at times to
+                * readjust our starting point and re-run it.  The roff
+                * preprocessor can also readjust the buffers with new
+                * data, so we pass them in wholesale.
+                */
+
+               of = 0;
+
+rerun:
+               rr = roff_parseln
+                       (curp->roff, curp->line, 
+                        &ln.buf, &ln.sz, of, &of);
+
+               switch (rr) {
+               case (ROFF_REPARSE):
+                       if (REPARSE_LIMIT >= ++curp->reparse_count)
+                               parsebuf(curp, ln, 0);
+                       else
+                               mmsg(MANDOCERR_ROFFLOOP, curp, 
+                                   curp->line, pos, NULL);
+                       pos = 0;
+                       continue;
+               case (ROFF_APPEND):
+                       pos = strlen(ln.buf);
+                       continue;
+               case (ROFF_RERUN):
+                       goto rerun;
+               case (ROFF_IGN):
+                       pos = 0;
+                       continue;
+               case (ROFF_ERR):
+                       assert(MANDOCLEVEL_FATAL <= file_status);
+                       break;
+               case (ROFF_SO):
+                       if (pfile(ln.buf + of, curp)) {
+                               pos = 0;
+                               continue;
+                       } else
+                               break;
+               default:
+                       break;
+               }
+
+               /*
+                * If we encounter errors in the recursive parsebuf()
+                * call, make sure we don't continue parsing.
+                */
+
+               if (MANDOCLEVEL_FATAL <= file_status)
+                       break;
+
+               /*
+                * If input parsers have not been allocated, do so now.
+                * We keep these instanced betwen parsers, but set them
+                * locally per parse routine since we can use different
+                * parsers with each one.
+                */
+
+               if ( ! (curp->man || curp->mdoc))
+                       pset(ln.buf + of, pos - of, curp);
+
+               /* 
+                * Lastly, push down into the parsers themselves.  One
+                * of these will have already been set in the pset()
+                * routine.
+                * If libroff returns ROFF_TBL, then add it to the
+                * currently open parse.  Since we only get here if
+                * there does exist data (see tbl_data.c), we're
+                * guaranteed that something's been allocated.
+                */
+
+               if (ROFF_TBL == rr) {
+                       assert(curp->man || curp->mdoc);
+                       if (curp->man)
+                               man_addspan(curp->man, roff_span(curp->roff));
+                       else
+                               mdoc_addspan(curp->mdoc, roff_span(curp->roff));
+
+               } else if (curp->man || curp->mdoc) {
+                       rc = curp->man ?
+                               man_parseln(curp->man, 
+                                       curp->line, ln.buf, of) :
+                               mdoc_parseln(curp->mdoc, 
+                                       curp->line, ln.buf, of);
+
+                       if ( ! rc) {
+                               assert(MANDOCLEVEL_FATAL <= file_status);
+                               break;
+                       }
+               }
+
+               /* Temporary buffers typically are not full. */
+
+               if (0 == start && '\0' == blk.buf[i])
+                       break;
+
+               /* Start the next input line. */
+
+               pos = 0;
+       }
+
+       free(ln.buf);
+}
+
+static void
+pset(const char *buf, int pos, struct curparse *curp)
+{
+       int              i;
+
+       /*
+        * Try to intuit which kind of manual parser should be used.  If
+        * passed in by command-line (-man, -mdoc), then use that
+        * explicitly.  If passed as -mandoc, then try to guess from the
+        * line: either skip dot-lines, use -mdoc when finding `.Dt', or
+        * default to -man, which is more lenient.
+        *
+        * Separate out pmdoc/pman from mdoc/man: the first persists
+        * through all parsers, while the latter is used per-parse.
+        */
+
+       if ('.' == buf[0] || '\'' == buf[0]) {
+               for (i = 1; buf[i]; i++)
+                       if (' ' != buf[i] && '\t' != buf[i])
+                               break;
+               if ('\0' == buf[i])
+                       return;
+       }
+
+       switch (curp->inttype) {
+       case (INTT_MDOC):
+               if (NULL == curp->pmdoc) 
+                       curp->pmdoc = mdoc_alloc
+                               (&curp->regs, curp, mmsg);
+               assert(curp->pmdoc);
+               curp->mdoc = curp->pmdoc;
+               return;
+       case (INTT_MAN):
+               if (NULL == curp->pman) 
+                       curp->pman = man_alloc
+                               (&curp->regs, curp, mmsg);
+               assert(curp->pman);
+               curp->man = curp->pman;
+               return;
+       default:
+               break;
+       }
+
+       if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
+               if (NULL == curp->pmdoc) 
+                       curp->pmdoc = mdoc_alloc
+                               (&curp->regs, curp, mmsg);
+               assert(curp->pmdoc);
+               curp->mdoc = curp->pmdoc;
+               return;
+       } 
+
+       if (NULL == curp->pman) 
+               curp->pman = man_alloc(&curp->regs, curp, mmsg);
+       assert(curp->pman);
+       curp->man = curp->pman;
+}
+
+static int
+moptions(enum intt *tflags, char *arg)
+{
+
+       if (0 == strcmp(arg, "doc"))
+               *tflags = INTT_MDOC;
+       else if (0 == strcmp(arg, "andoc"))
+               *tflags = INTT_AUTO;
+       else if (0 == strcmp(arg, "an"))
+               *tflags = INTT_MAN;
+       else {
+               fprintf(stderr, "%s: Bad argument\n", arg);
+               return(0);
+       }
+
+       return(1);
+}
+
+static int
+toptions(struct curparse *curp, char *arg)
+{
+
+       if (0 == strcmp(arg, "ascii"))
+               curp->outtype = OUTT_ASCII;
+       else if (0 == strcmp(arg, "lint")) {
+               curp->outtype = OUTT_LINT;
+               curp->wlevel  = MANDOCLEVEL_WARNING;
+       }
+       else if (0 == strcmp(arg, "tree"))
+               curp->outtype = OUTT_TREE;
+       else if (0 == strcmp(arg, "html"))
+               curp->outtype = OUTT_HTML;
+       else if (0 == strcmp(arg, "xhtml"))
+               curp->outtype = OUTT_XHTML;
+       else if (0 == strcmp(arg, "ps"))
+               curp->outtype = OUTT_PS;
+       else if (0 == strcmp(arg, "pdf"))
+               curp->outtype = OUTT_PDF;
+       else {
+               fprintf(stderr, "%s: Bad argument\n", arg);
+               return(0);
+       }
+
+       return(1);
+}
+
+static int
+woptions(struct curparse *curp, char *arg)
+{
+       char            *v, *o;
+       const char      *toks[6]; 
+
+       toks[0] = "stop";
+       toks[1] = "all";
+       toks[2] = "warning";
+       toks[3] = "error";
+       toks[4] = "fatal";
+       toks[5] = NULL;
+
+       while (*arg) {
+               o = arg;
+               switch (getsubopt(&arg, UNCONST(toks), &v)) {
+               case (0):
+                       curp->wstop = 1;
+                       break;
+               case (1):
+                       /* FALLTHROUGH */
+               case (2):
+                       curp->wlevel = MANDOCLEVEL_WARNING;
+                       break;
+               case (3):
+                       curp->wlevel = MANDOCLEVEL_ERROR;
+                       break;
+               case (4):
+                       curp->wlevel = MANDOCLEVEL_FATAL;
+                       break;
+               default:
+                       fprintf(stderr, "-W%s: Bad argument\n", o);
+                       return(0);
+               }
+       }
+
+       return(1);
+}
+
+static int
+mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
+{
+       struct curparse *cp;
+       enum mandoclevel level;
+
+       level = MANDOCLEVEL_FATAL;
+       while (t < mandoclimits[level])
+               /* LINTED */
+               level--;
+
+       cp = (struct curparse *)arg;
+       if (level < cp->wlevel)
+               return(1);
+
+       fprintf(stderr, "%s:%d:%d: %s: %s",
+           cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]);
+       if (msg)
+               fprintf(stderr, ": %s", msg);
+       fputc('\n', stderr);
+
+       if (file_status < level)
+               file_status = level;
+       
+       return(level < MANDOCLEVEL_FATAL);
+}
diff --git a/contrib/mdocml/main.h b/contrib/mdocml/main.h
new file mode 100644 (file)
index 0000000..bb503eb
--- /dev/null
@@ -0,0 +1,56 @@
+/*     $Id: main.h,v 1.10 2010/07/31 23:52:58 schwarze Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef        MAIN_H
+#define        MAIN_H
+
+__BEGIN_DECLS
+
+struct mdoc;
+struct man;
+
+#define        UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))
+
+
+/* 
+ * Definitions for main.c-visible output device functions, e.g., -Thtml
+ * and -Tascii.  Note that ascii_alloc() is named as such in
+ * anticipation of latin1_alloc() and so on, all of which map into the
+ * terminal output routines with different character settings.
+ */
+
+void            *html_alloc(char *);
+void            *xhtml_alloc(char *);
+void             html_mdoc(void *, const struct mdoc *);
+void             html_man(void *, const struct man *);
+void             html_free(void *);
+
+void             tree_mdoc(void *, const struct mdoc *);
+void             tree_man(void *, const struct man *);
+
+void            *ascii_alloc(char *);
+void             ascii_free(void *);
+
+void            *pdf_alloc(char *);
+void            *ps_alloc(char *);
+void             pspdf_free(void *);
+
+void             terminal_mdoc(void *, const struct mdoc *);
+void             terminal_man(void *, const struct man *);
+
+__END_DECLS
+
+#endif /*!MAIN_H*/
diff --git a/contrib/mdocml/man.3 b/contrib/mdocml/man.3
new file mode 100644 (file)
index 0000000..2b2d0a9
--- /dev/null
@@ -0,0 +1,272 @@
+.\"    $Id: man.3,v 1.29 2011/01/03 11:31:26 kristaps Exp $
+.\"
+.\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv>
+.\"
+.\" 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: January 3 2011 $
+.Dt MAN 3
+.Os
+.Sh NAME
+.Nm man ,
+.Nm man_alloc ,
+.Nm man_endparse ,
+.Nm man_free ,
+.Nm man_meta ,
+.Nm man_node ,
+.Nm man_parseln ,
+.Nm man_reset
+.Nd man macro compiler library
+.Sh SYNOPSIS
+.In mandoc.h
+.In man.h
+.Vt extern const char * const * man_macronames;
+.Ft int
+.Fo man_addspan
+.Fa "struct man *man"
+.Fa "const struct tbl_span *span"
+.Fc
+.Ft "struct man *"
+.Fo man_alloc
+.Fa "struct regset *regs"
+.Fa "void *data"
+.Fa "mandocmsg msgs"
+.Fc
+.Ft int
+.Fn man_endparse "struct man *man"
+.Ft void
+.Fn man_free "struct man *man"
+.Ft "const struct man_meta *"
+.Fn man_meta "const struct man *man"
+.Ft "const struct man_node *"
+.Fn man_node "const struct man *man"
+.Ft int
+.Fo man_parseln
+.Fa "struct man *man"
+.Fa "int line"
+.Fa "char *buf"
+.Fc
+.Ft void
+.Fn man_reset "struct man *man"
+.Sh DESCRIPTION
+The
+.Nm
+library parses lines of
+.Xr man 7
+input into an abstract syntax tree (AST).
+.Pp
+In general, applications initiate a parsing sequence with
+.Fn man_alloc ,
+parse each line in a document with
+.Fn man_parseln ,
+close the parsing session with
+.Fn man_endparse ,
+operate over the syntax tree returned by
+.Fn man_node
+and
+.Fn man_meta ,
+then free all allocated memory with
+.Fn man_free .
+The
+.Fn man_reset
+function may be used in order to reset the parser for another input
+sequence.
+.Pp
+Beyond the full set of macros defined in
+.Xr man 7 ,
+the
+.Nm
+library also accepts the following macro:
+.Pp
+.Bl -tag -width Ds -compact
+.It PD
+Has no effect.
+Handled as a current-scope line macro.
+.El
+.Ss Types
+.Bl -ohang
+.It Vt struct man
+An opaque type.
+Its values are only used privately within the library.
+.It Vt struct man_node
+A parsed node.
+See
+.Sx Abstract Syntax Tree
+for details.
+.El
+.Ss Functions
+If
+.Fn man_addspan ,
+.Fn man_parseln ,
+or
+.Fn man_endparse
+return 0, calls to any function but
+.Fn man_reset
+or
+.Fn man_free
+will raise an assertion.
+.Bl -ohang
+.It Fn man_addspan
+Add a table span to the parsing stream.
+Returns 0 on failure, 1 on success.
+.It Fn man_alloc
+Allocates a parsing structure.
+The
+.Fa data
+pointer is passed to
+.Fa msgs .
+Always returns a valid pointer.
+The pointer must be freed with
+.Fn man_free .
+.It Fn man_reset
+Reset the parser for another parse routine.
+After its use,
+.Fn man_parseln
+behaves as if invoked for the first time.
+.It Fn man_free
+Free all resources of a parser.
+The pointer is no longer valid after invocation.
+.It Fn man_parseln
+Parse a nil-terminated line of input.
+This line should not contain the trailing newline.
+Returns 0 on failure, 1 on success.
+The input buffer
+.Fa buf
+is modified by this function.
+.It Fn man_endparse
+Signals that the parse is complete.
+Returns 0 on failure, 1 on success.
+.It Fn man_node
+Returns the first node of the parse.
+.It Fn man_meta
+Returns the document's parsed meta-data.
+.El
+.Ss Variables
+The following variables are also defined:
+.Bl -ohang
+.It Va man_macronames
+An array of string-ified token names.
+.El
+.Ss Abstract Syntax Tree
+The
+.Nm
+functions produce an abstract syntax tree (AST) describing input in a
+regular form.
+It may be reviewed at any time with
+.Fn man_nodes ;
+however, if called before
+.Fn man_endparse ,
+or after
+.Fn man_endparse
+or
+.Fn man_parseln
+fail, it may be incomplete.
+.Pp
+This AST is governed by the ontological rules dictated in
+.Xr man 7
+and derives its terminology accordingly.
+.Pp
+The AST is composed of
+.Vt struct man_node
+nodes with element, root and text types as declared by the
+.Va type
+field.
+Each node also provides its parse point (the
+.Va line ,
+.Va sec ,
+and
+.Va pos
+fields), its position in the tree (the
+.Va parent ,
+.Va child ,
+.Va next
+and
+.Va prev
+fields) and some type-specific data.
+.Pp
+The tree itself is arranged according to the following normal form,
+where capitalised non-terminals represent nodes.
+.Pp
+.Bl -tag -width "ELEMENTXX" -compact
+.It ROOT
+\(<- mnode+
+.It mnode
+\(<- ELEMENT | TEXT | BLOCK
+.It BLOCK
+\(<- HEAD BODY
+.It HEAD
+\(<- mnode*
+.It BODY
+\(<- mnode*
+.It ELEMENT
+\(<- ELEMENT | TEXT*
+.It TEXT
+\(<- [[:alpha:]]*
+.El
+.Pp
+The only elements capable of nesting other elements are those with
+next-lint scope as documented in
+.Xr man 7 .
+.Sh EXAMPLES
+The following example reads lines from stdin and parses them, operating
+on the finished parse tree with
+.Fn parsed .
+This example does not error-check nor free memory upon failure.
+.Bd -literal -offset indent
+struct regset regs;
+struct man *man;
+struct man_node *node;
+char *buf;
+size_t len;
+int line;
+
+bzero(&regs, sizeof(struct regset));
+line = 1;
+man = man_alloc(&regs, NULL, NULL);
+buf = NULL;
+alloc_len = 0;
+
+while ((len = getline(&buf, &alloc_len, stdin)) >= 0) {
+    if (len && buflen[len - 1] = '\en')
+        buf[len - 1] = '\e0';
+    if ( ! man_parseln(man, line, buf))
+        errx(1, "man_parseln");
+    line++;
+}
+
+free(buf);
+
+if ( ! man_endparse(man))
+    errx(1, "man_endparse");
+if (NULL == (node = man_node(man)))
+    errx(1, "man_node");
+
+parsed(man, node);
+man_free(man);
+.Ed
+.Pp
+To compile this, execute
+.Pp
+.Dl % cc main.c libman.a libmandoc.a
+.Pp
+where
+.Pa main.c
+is the example file.
+.Sh SEE ALSO
+.Xr mandoc 1 ,
+.Xr man 7
+.Sh AUTHORS
+The
+.Nm
+library was written by
+.An Kristaps Dzonsons Aq kristaps@bsd.lv .
diff --git a/contrib/mdocml/man.7 b/contrib/mdocml/man.7
new file mode 100644 (file)
index 0000000..d8bd6ee
--- /dev/null
@@ -0,0 +1,939 @@
+.\"    $Id: man.7,v 1.94 2011/01/04 23:32:21 kristaps Exp $
+.\"
+.\" Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+.\"
+.\" 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: January 4 2011 $
+.Dt MAN 7
+.Os
+.Sh NAME
+.Nm man
+.Nd man language reference
+.Sh DESCRIPTION
+The
+.Nm man
+language was historically used to format
+.Ux
+manuals.
+This reference document describes its syntax, structure, and usage.
+.Pp
+.Bf -emphasis
+Do not use
+.Nm
+to write your manuals.
+.Ef
+Use the
+.Xr mdoc 7
+language, instead.
+.Pp
+A
+.Nm
+document follows simple rules:  lines beginning with the control
+character
+.Sq \&.
+are parsed for macros.
+Other lines are interpreted within the scope of
+prior macros:
+.Bd -literal -offset indent
+\&.SH Macro lines change control state.
+Other lines are interpreted within the current state.
+.Ed
+.Sh INPUT ENCODING
+.Nm
+documents may contain only graphable 7-bit ASCII characters, the
+space character, and the tab character.
+All manuals must have
+.Ux
+line termination.
+.Pp
+Blank lines are acceptable; where found, the output will assert a
+vertical space.
+.Ss Comments
+Text following a
+.Sq \e\*q ,
+whether in a macro or free-form text line, is ignored to the end of
+line.
+A macro line with only a control character and comment escape,
+.Sq \&.\e\*q ,
+is also ignored.
+Macro lines with only a control character and optionally whitespace are
+stripped from input.
+.Ss Special Characters
+Special characters may occur in both macro and free-form lines.
+Sequences begin with the escape character
+.Sq \e
+followed by either an open-parenthesis
+.Sq \&(
+for two-character sequences; an open-bracket
+.Sq \&[
+for n-character sequences (terminated at a close-bracket
+.Sq \&] ) ;
+or a single one-character sequence.
+See
+.Xr mandoc_char 7
+for a complete list.
+Examples include
+.Sq \e(em
+.Pq em-dash
+and
+.Sq \ee
+.Pq back-slash .
+.Ss Text Decoration
+Terms may be text-decorated using the
+.Sq \ef
+escape followed by an indicator: B (bold), I (italic), R (Roman), or P
+(revert to previous mode):
+.Pp
+.D1 \efBbold\efR \efIitalic\efP
+.Pp
+A numerical representation 3, 2, or 1 (bold, italic, and Roman,
+respectively) may be used instead.
+A text decoration is only valid, if specified in free-form text, until
+the next macro invocation; if specified within a macro, it's only valid
+until the macro closes scope.
+Note that macros like
+.Sx \&BR
+open and close a font scope with each argument.
+.Pp
+The
+.Sq \ef
+attribute is forgotten when entering or exiting a macro block.
+.Ss Whitespace
+Whitespace consists of the space character.
+In free-form lines, whitespace is preserved within a line; unescaped
+trailing spaces are stripped from input (unless in a literal context).
+Blank free-form lines, which may include spaces, are permitted and
+rendered as an empty line.
+.Pp
+In macro lines, whitespace delimits arguments and is discarded.
+If arguments are quoted, whitespace within the quotes is retained.
+.Ss Dates
+The
+.Sx \&TH
+macro is the only
+.Nm
+macro that requires a date.
+The form for this date is the ISO-8601
+standard
+.Cm YYYY-MM-DD .
+.Ss Scaling Widths
+Many macros support scaled widths for their arguments, such as
+stipulating a two-inch paragraph indentation with the following:
+.Bd -literal -offset indent
+\&.HP 2i
+.Ed
+.Pp
+The syntax for scaled widths is
+.Sq Li [+-]?[0-9]*.[0-9]*[:unit:]? ,
+where a decimal must be preceded or proceeded by at least one digit.
+Negative numbers, while accepted, are truncated to zero.
+The following scaling units are accepted:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It c
+centimetre
+.It i
+inch
+.It P
+pica (~1/6 inch)
+.It p
+point (~1/72 inch)
+.It f
+synonym for
+.Sq u
+.It v
+default vertical span
+.It m
+width of rendered
+.Sq m
+.Pq em
+character
+.It n
+width of rendered
+.Sq n
+.Pq en
+character
+.It u
+default horizontal span
+.It M
+mini-em (~1/100 em)
+.El
+.Pp
+Using anything other than
+.Sq m ,
+.Sq n ,
+.Sq u ,
+or
+.Sq v
+is necessarily non-portable across output media.
+.Pp
+If a scaling unit is not provided, the numerical value is interpreted
+under the default rules of
+.Sq v
+for vertical spaces and
+.Sq u
+for horizontal ones.
+.Em Note :
+this differs from
+.Xr mdoc 7 ,
+which, if a unit is not provided, will instead interpret the string as
+literal text.
+.Ss Sentence Spacing
+When composing a manual, make sure that sentences end at the end of
+a line.
+By doing so, front-ends will be able to apply the proper amount of
+spacing after the end of sentence (unescaped) period, exclamation mark,
+or question mark followed by zero or more non-sentence closing
+delimiters
+.Po
+.Sq \&) ,
+.Sq \&] ,
+.Sq \&' ,
+.Sq \&"
+.Pc .
+.Sh MANUAL STRUCTURE
+Each
+.Nm
+document must contain the
+.Sx \&TH
+macro describing the document's section and title.
+It may occur anywhere in the document, although conventionally it
+appears as the first macro.
+.Pp
+Beyond
+.Sx \&TH ,
+at least one macro or text node must appear in the document.
+Documents are generally structured as follows:
+.Bd -literal -offset indent
+\&.TH FOO 1 2009-10-10
+\&.SH NAME
+\efBfoo\efR \e(en a description goes here
+\&.\e\*q .SH LIBRARY
+\&.\e\*q For sections 2 & 3 only.
+\&.\e\*q Not used in OpenBSD.
+\&.SH SYNOPSIS
+\efBfoo\efR [\efB\e-options\efR] arguments...
+\&.SH DESCRIPTION
+The \efBfoo\efR utility processes files...
+\&.\e\*q .SH IMPLEMENTATION NOTES
+\&.\e\*q Not used in OpenBSD.
+\&.\e\*q .SH RETURN VALUES
+\&.\e\*q For sections 2, 3, & 9 only.
+\&.\e\*q .SH ENVIRONMENT
+\&.\e\*q For sections 1, 6, 7, & 8 only.
+\&.\e\*q .SH FILES
+\&.\e\*q .SH EXIT STATUS
+\&.\e\*q For sections 1, 6, & 8 only.
+\&.\e\*q .SH EXAMPLES
+\&.\e\*q .SH DIAGNOSTICS
+\&.\e\*q For sections 1, 4, 6, 7, & 8 only.
+\&.\e\*q .SH ERRORS
+\&.\e\*q For sections 2, 3, & 9 only.
+\&.\e\*q .SH SEE ALSO
+\&.\e\*q .BR foo ( 1 )
+\&.\e\*q .SH STANDARDS
+\&.\e\*q .SH HISTORY
+\&.\e\*q .SH AUTHORS
+\&.\e\*q .SH CAVEATS
+\&.\e\*q .SH BUGS
+\&.\e\*q .SH SECURITY CONSIDERATIONS
+\&.\e\*q Not used in OpenBSD.
+.Ed
+.Pp
+The sections in a
+.Nm
+document are conventionally ordered as they appear above.
+Sections should be composed as follows:
+.Bl -ohang -offset indent
+.It Em NAME
+The name(s) and a short description of the documented material.
+The syntax for this is generally as follows:
+.Pp
+.D1 \efBname\efR \e(en description
+.It Em LIBRARY
+The name of the library containing the documented material, which is
+assumed to be a function in a section 2 or 3 manual.
+For functions in the C library, this may be as follows:
+.Pp
+.D1 Standard C Library (libc, -lc)
+.It Em SYNOPSIS
+Documents the utility invocation syntax, function call syntax, or device
+configuration.
+.Pp
+For the first, utilities (sections 1, 6, and 8), this is
+generally structured as follows:
+.Pp
+.D1 \efBname\efR [-\efBab\efR] [-\efBc\efR\efIarg\efR] \efBpath\efR...
+.Pp
+For the second, function calls (sections 2, 3, 9):
+.Pp
+.D1 \&.B char *name(char *\efIarg\efR);
+.Pp
+And for the third, configurations (section 4):
+.Pp
+.D1 \&.B name* at cardbus ? function ?
+.Pp
+Manuals not in these sections generally don't need a
+.Em SYNOPSIS .
+.It Em DESCRIPTION
+This expands upon the brief, one-line description in
+.Em NAME .
+It usually contains a break-down of the options (if documenting a
+command).
+.It Em IMPLEMENTATION NOTES
+Implementation-specific notes should be kept here.
+This is useful when implementing standard functions that may have side
+effects or notable algorithmic implications.
+.It Em RETURN VALUES
+This section documents the return values of functions in sections 2, 3, and 9.
+.It Em ENVIRONMENT
+Documents any usages of environment variables, e.g.,
+.Xr environ 7 .
+.It Em FILES
+Documents files used.
+It's helpful to document both the file name and a short description of how
+the file is used (created, modified, etc.).
+.It Em EXIT STATUS
+This section documents the command exit status for
+section 1, 6, and 8 utilities.
+Historically, this information was described in
+.Em DIAGNOSTICS ,
+a practise that is now discouraged.
+.It Em EXAMPLES
+Example usages.
+This often contains snippets of well-formed,
+well-tested invocations.
+Make sure that examples work properly!
+.It Em DIAGNOSTICS
+Documents error conditions.
+This is most useful in section 4 manuals.
+Historically, this section was used in place of
+.Em EXIT STATUS
+for manuals in sections 1, 6, and 8; however, this practise is
+discouraged.
+.It Em ERRORS
+Documents error handling in sections 2, 3, and 9.
+.It Em SEE ALSO
+References other manuals with related topics.
+This section should exist for most manuals.
+.Pp
+.D1 \&.BR bar \&( 1 \&),
+.Pp
+Cross-references should conventionally be ordered
+first by section, then alphabetically.
+.It Em STANDARDS
+References any standards implemented or used, such as
+.Pp
+.D1 IEEE Std 1003.2 (\e(lqPOSIX.2\e(rq)
+.Pp
+If not adhering to any standards, the
+.Em HISTORY
+section should be used.
+.It Em HISTORY
+A brief history of the subject, including where support first appeared.
+.It Em AUTHORS
+Credits to the person or persons who wrote the code and/or documentation.
+Authors should generally be noted by both name and email address.
+.It Em CAVEATS
+Common misuses and misunderstandings should be explained
+in this section.
+.It Em BUGS
+Known bugs, limitations, and work-arounds should be described
+in this section.
+.It Em SECURITY CONSIDERATIONS
+Documents any security precautions that operators should consider.
+.El
+.Sh MACRO SYNTAX
+Macros are one to three characters in length and begin with a
+control character,
+.Sq \&. ,
+at the beginning of the line.
+The
+.Sq \(aq
+macro control character is also accepted.
+An arbitrary amount of whitespace (spaces or tabs) may sit between the
+control character and the macro name.
+Thus, the following are equivalent:
+.Bd -literal -offset indent
+\&.PP
+\&.\ \ \ PP
+.Ed
+.Pp
+The
+.Nm
+macros are classified by scope: line scope or block scope.
+Line macros are only scoped to the current line (and, in some
+situations, the subsequent line).
+Block macros are scoped to the current line and subsequent lines until
+closed by another block macro.
+.Ss Line Macros
+Line macros are generally scoped to the current line, with the body
+consisting of zero or more arguments.
+If a macro is scoped to the next line and the line arguments are empty,
+the next line, which must be text, is used instead.
+Thus:
+.Bd -literal -offset indent
+\&.I
+foo
+.Ed
+.Pp
+is equivalent to
+.Sq \&.I foo .
+If next-line macros are invoked consecutively, only the last is used.
+If a next-line macro is followed by a non-next-line macro, an error is
+raised, except for
+.Sx \&br ,
+.Sx \&sp ,
+and
+.Sx \&na .
+.Pp
+The syntax is as follows:
+.Bd -literal -offset indent
+\&.YO \(lBbody...\(rB
+\(lBbody...\(rB
+.Ed
+.Pp
+.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX"
+.It Em Macro Ta Em Arguments Ta Em Scope     Ta Em Notes
+.It Sx \&AT  Ta    <=1       Ta    current   Ta    \&
+.It Sx \&B   Ta    n         Ta    next-line Ta    \&
+.It Sx \&BI  Ta    n         Ta    current   Ta    \&
+.It Sx \&BR  Ta    n         Ta    current   Ta    \&
+.It Sx \&DT  Ta    0         Ta    current   Ta    \&
+.It Sx \&I   Ta    n         Ta    next-line Ta    \&
+.It Sx \&IB  Ta    n         Ta    current   Ta    \&
+.It Sx \&IR  Ta    n         Ta    current   Ta    \&
+.It Sx \&R   Ta    n         Ta    next-line Ta    \&
+.It Sx \&RB  Ta    n         Ta    current   Ta    \&
+.It Sx \&RI  Ta    n         Ta    current   Ta    \&
+.It Sx \&SB  Ta    n         Ta    next-line Ta    \&
+.It Sx \&SM  Ta    n         Ta    next-line Ta    \&
+.It Sx \&TH  Ta    >1, <6    Ta    current   Ta    \&
+.It Sx \&UC  Ta    <=1       Ta    current   Ta    \&
+.It Sx \&br  Ta    0         Ta    current   Ta    compat
+.It Sx \&fi  Ta    0         Ta    current   Ta    compat
+.It Sx \&ft  Ta    1         Ta    current   Ta    compat
+.It Sx \&in  Ta    1         Ta    current   Ta    compat
+.It Sx \&na  Ta    0         Ta    current   Ta    compat
+.It Sx \&nf  Ta    0         Ta    current   Ta    compat
+.It Sx \&sp  Ta    1         Ta    current   Ta    compat
+.El
+.Pp
+Macros marked as
+.Qq compat
+are included for compatibility with the significant corpus of existing
+manuals that mix dialects of roff.
+These macros should not be used for portable
+.Nm
+manuals.
+.Ss Block Macros
+Block macros comprise a head and body.
+As with in-line macros, the head is scoped to the current line and, in
+one circumstance, the next line (the next-line stipulations as in
+.Sx Line Macros
+apply here as well).
+.Pp
+The syntax is as follows:
+.Bd -literal -offset indent
+\&.YO \(lBhead...\(rB
+\(lBhead...\(rB
+\(lBbody...\(rB
+.Ed
+.Pp
+The closure of body scope may be to the section, where a macro is closed
+by
+.Sx \&SH ;
+sub-section, closed by a section or
+.Sx \&SS ;
+part, closed by a section, sub-section, or
+.Sx \&RE ;
+or paragraph, closed by a section, sub-section, part,
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&P ,
+.Sx \&PP ,
+or
+.Sx \&TP .
+No closure refers to an explicit block closing macro.
+.Pp
+As a rule, block macros may not be nested; thus, calling a block macro
+while another block macro scope is open, and the open scope is not
+implicitly closed, is syntactically incorrect.
+.Pp
+.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX"
+.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope  Ta Em Notes
+.It Sx \&HP  Ta    <2        Ta    current    Ta    paragraph   Ta    \&
+.It Sx \&IP  Ta    <3        Ta    current    Ta    paragraph   Ta    \&
+.It Sx \&LP  Ta    0         Ta    current    Ta    paragraph   Ta    \&
+.It Sx \&P   Ta    0         Ta    current    Ta    paragraph   Ta    \&
+.It Sx \&PP  Ta    0         Ta    current    Ta    paragraph   Ta    \&
+.It Sx \&RE  Ta    0         Ta    current    Ta    none        Ta    compat
+.It Sx \&RS  Ta    1         Ta    current    Ta    part        Ta    compat
+.It Sx \&SH  Ta    >0        Ta    next-line  Ta    section     Ta    \&
+.It Sx \&SS  Ta    >0        Ta    next-line  Ta    sub-section Ta    \&
+.It Sx \&TP  Ta    n         Ta    next-line  Ta    paragraph   Ta    \&
+.El
+.Pp
+Macros marked
+.Qq compat
+are as mentioned in
+.Sx Line Macros .
+.Pp
+If a block macro is next-line scoped, it may only be followed by in-line
+macros for decorating text.
+.Sh REFERENCE
+This section is a canonical reference to all macros, arranged
+alphabetically.
+For the scoping of individual macros, see
+.Sx MACRO SYNTAX .
+.Ss \&AT
+Sets the volume for the footer for compatibility with man pages from
+.Tn AT&T UNIX
+releases.
+The optional arguments specify which release it is from.
+.Ss \&B
+Text is rendered in bold face.
+.Pp
+See also
+.Sx \&I
+and
+.Sx \&R .
+.Ss \&BI
+Text is rendered alternately in bold face and italic.
+Thus,
+.Sq .BI this word and that
+causes
+.Sq this
+and
+.Sq and
+to render in bold face, while
+.Sq word
+and
+.Sq that
+render in italics.
+Whitespace between arguments is omitted in output.
+.Pp
+Examples:
+.Pp
+.Dl \&.BI bold italic bold italic
+.Pp
+The output of this example will be emboldened
+.Dq bold
+and italicised
+.Dq italic ,
+with spaces stripped between arguments.
+.Pp
+See also
+.Sx \&IB ,
+.Sx \&BR ,
+.Sx \&RB ,
+.Sx \&RI ,
+and
+.Sx \&IR .
+.Ss \&BR
+Text is rendered alternately in bold face and roman (the default font).
+Whitespace between arguments is omitted in output.
+.Pp
+See
+.Sx \&BI
+for an equivalent example.
+.Pp
+See also
+.Sx \&BI ,
+.Sx \&IB ,
+.Sx \&RB ,
+.Sx \&RI ,
+and
+.Sx \&IR .
+.Ss \&DT
+Has no effect.
+Included for compatibility.
+.Ss \&HP
+Begin a paragraph whose initial output line is left-justified, but
+subsequent output lines are indented, with the following syntax:
+.Bd -filled -offset indent
+.Pf \. Sx \&HP
+.Op Cm width
+.Ed
+.Pp
+The
+.Cm width
+argument must conform to
+.Sx Scaling Widths .
+If specified, it's saved for later paragraph left-margins; if unspecified, the
+saved or default width is used.
+.Pp
+See also
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&P ,
+.Sx \&PP ,
+and
+.Sx \&TP .
+.Ss \&I
+Text is rendered in italics.
+.Pp
+See also
+.Sx \&B
+and
+.Sx \&R .
+.Ss \&IB
+Text is rendered alternately in italics and bold face.
+Whitespace between arguments is omitted in output.
+.Pp
+See
+.Sx \&BI
+for an equivalent example.
+.Pp
+See also
+.Sx \&BI ,
+.Sx \&BR ,
+.Sx \&RB ,
+.Sx \&RI ,
+and
+.Sx \&IR .
+.Ss \&IP
+Begin an indented paragraph with the following syntax:
+.Bd -filled -offset indent
+.Pf \. Sx \&IP
+.Op Cm head Op Cm width
+.Ed
+.Pp
+The
+.Cm width
+argument defines the width of the left margin and is defined by
+.Sx Scaling Widths .
+It's saved for later paragraph left-margins; if unspecified, the saved or
+default width is used.
+.Pp
+The
+.Cm head
+argument is used as a leading term, flushed to the left margin.
+This is useful for bulleted paragraphs and so on.
+.Pp
+See also
+.Sx \&HP ,
+.Sx \&LP ,
+.Sx \&P ,
+.Sx \&PP ,
+and
+.Sx \&TP .
+.Ss \&IR
+Text is rendered alternately in italics and roman (the default font).
+Whitespace between arguments is omitted in output.
+.Pp
+See
+.Sx \&BI
+for an equivalent example.
+.Pp
+See also
+.Sx \&BI ,
+.Sx \&IB ,
+.Sx \&BR ,
+.Sx \&RB ,
+and
+.Sx \&RI .
+.Ss \&LP
+Begin an undecorated paragraph.
+The scope of a paragraph is closed by a subsequent paragraph,
+sub-section, section, or end of file.
+The saved paragraph left-margin width is reset to the default.
+.Pp
+See also
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&P ,
+.Sx \&PP ,
+and
+.Sx \&TP .
+.Ss \&P
+Synonym for
+.Sx \&LP .
+.Pp
+See also
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&PP ,
+and
+.Sx \&TP .
+.Ss \&PP
+Synonym for
+.Sx \&LP .
+.Pp
+See also
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&P ,
+and
+.Sx \&TP .
+.Ss \&R
+Text is rendered in roman (the default font).
+.Pp
+See also
+.Sx \&I
+and
+.Sx \&B .
+.Ss \&RB
+Text is rendered alternately in roman (the default font) and bold face.
+Whitespace between arguments is omitted in output.
+.Pp
+See
+.Sx \&BI
+for an equivalent example.
+.Pp
+See also
+.Sx \&BI ,
+.Sx \&IB ,
+.Sx \&BR ,
+.Sx \&RI ,
+and
+.Sx \&IR .
+.Ss \&RE
+Explicitly close out the scope of a prior
+.Sx \&RS .
+.Ss \&RI
+Text is rendered alternately in roman (the default font) and italics.
+Whitespace between arguments is omitted in output.
+.Pp
+See
+.Sx \&BI
+for an equivalent example.
+.Pp
+See also
+.Sx \&BI ,
+.Sx \&IB ,
+.Sx \&BR ,
+.Sx \&RB ,
+and
+.Sx \&IR .
+.Ss \&RS
+Begin a part setting the left margin.
+The left margin controls the offset, following an initial indentation,
+to un-indented text such as that of
+.Sx \&PP .
+This has the following syntax:
+.Bd -filled -offset indent
+.Pf \. Sx \&Rs
+.Op Cm width
+.Ed
+.Pp
+The
+.Cm width
+argument must conform to
+.Sx Scaling Widths .
+If not specified, the saved or default width is used.
+.Ss \&SB
+Text is rendered in small size (one point smaller than the default font)
+bold face.
+.Ss \&SH
+Begin a section.
+The scope of a section is only closed by another section or the end of
+file.
+The paragraph left-margin width is reset to the default.
+.Ss \&SM
+Text is rendered in small size (one point smaller than the default
+font).
+.Ss \&SS
+Begin a sub-section.
+The scope of a sub-section is closed by a subsequent sub-section,
+section, or end of file.
+The paragraph left-margin width is reset to the default.
+.Ss \&TH
+Sets the title of the manual page with the following syntax:
+.Bd -filled -offset indent
+.Pf \. Sx \&TH
+.Cm title section
+.Op Cm date Op Cm source Op Cm volume
+.Ed
+.Pp
+At least the upper-case document
+.Cm title
+and the manual
+.Cm section
+arguments must be provided.
+The
+.Cm date
+argument should be formatted as described in
+.Sx Dates ,
+but will be printed verbatim if it is not.
+If the date is not specified, the current date is used.
+The
+.Cm source
+string specifies the organisation providing the utility.
+The
+.Cm volume
+string replaces the default rendered volume, which is dictated by the
+manual section.
+.Pp
+Examples:
+.Pp
+.Dl \&.TH CVS 5 "1992-02-12" GNU
+.Ss \&TP
+Begin a paragraph where the head, if exceeding the indentation width, is
+followed by a newline; if not, the body follows on the same line after a
+buffer to the indentation width.
+Subsequent output lines are indented.
+The syntax is as follows:
+.Bd -filled -offset indent
+.Pf \. Sx \&TP
+.Op Cm width
+.Ed
+.Pp
+The
+.Cm width
+argument must conform to
+.Sx Scaling Widths .
+If specified, it's saved for later paragraph left-margins; if
+unspecified, the saved or default width is used.
+.Pp
+See also
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&P ,
+and
+.Sx \&PP .
+.Ss \&UC
+Sets the volume for the footer for compatibility with man pages from
+BSD releases.
+The optional first argument specifies which release it is from.
+.Ss \&br
+Breaks the current line.
+Consecutive invocations have no further effect.
+.Pp
+See also
+.Sx \&sp .
+.Ss \&fi
+End literal mode begun by
+.Sx \&nf .
+.Ss \&ft
+Change the current font mode.
+See
+.Sx Text Decoration
+for a listing of available font modes.
+.Ss \&in
+Indent relative to the current indentation:
+.Pp
+.D1 Pf \. Sx \&in Op Cm width
+.Pp
+If
+.Cm width
+is signed, the new offset is relative.
+Otherwise, it is absolute.
+This value is reset upon the next paragraph, section, or sub-section.
+.Ss \&na
+Don't align to the right margin.
+.Ss \&nf
+Begin literal mode: all subsequent free-form lines have their end of
+line boundaries preserved.
+May be ended by
+.Sx \&fi .
+.Ss \&sp
+Insert vertical spaces into output with the following syntax:
+.Bd -filled -offset indent
+.Pf \. Sx \&sp
+.Op Cm height
+.Ed
+.Pp
+Insert
+.Cm height
+spaces, which must conform to
+.Sx Scaling Widths .
+If 0, this is equivalent to the
+.Sx \&br
+macro.
+Defaults to 1, if unspecified.
+.Pp
+See also
+.Sx \&br .
+.Sh COMPATIBILITY
+This section documents areas of questionable portability between
+implementations of the
+.Nm
+language.
+.Pp
+.Bl -dash -compact
+.It
+In quoted literals, GNU troff allowed pair-wise double-quotes to produce
+a standalone double-quote in formatted output.
+It is not known whether this behaviour is exhibited by other formatters.
+.It
+troff suppresses a newline before
+.Sq \(aq
+macro output; in mandoc, it is an alias for the standard
+.Sq \&.
+control character.
+.It
+The
+.Sq \eh
+.Pq horizontal position ,
+.Sq \ev
+.Pq vertical position ,
+.Sq \em
+.Pq text colour ,
+.Sq \eM
+.Pq text filling colour ,
+.Sq \ez
+.Pq zero-length character ,
+.Sq \ew
+.Pq string length ,
+.Sq \ek
+.Pq horizontal position marker ,
+.Sq \eo
+.Pq text overstrike ,
+and
+.Sq \es
+.Pq text size
+escape sequences are all discarded in mandoc.
+.It
+The
+.Sq \ef
+scaling unit is accepted by mandoc, but rendered as the default unit.
+.It
+The
+.Sx \&sp
+macro does not accept negative values in mandoc.
+In GNU troff, this would result in strange behaviour.
+.El
+.Sh SEE ALSO
+.Xr man 1 ,
+.Xr mandoc 1 ,
+.Xr mandoc_char 7 ,
+.Xr mdoc 7 ,
+.Xr roff 7 ,
+.Xr tbl 7
+.Sh HISTORY
+The
+.Nm
+language first appeared as a macro package for the roff typesetting
+system in
+.At v7 .
+It was later rewritten by James Clark as a macro package for groff.
+The stand-alone implementation that is part of the
+.Xr mandoc 1
+utility written by Kristaps Dzonsons appeared in
+.Ox 4.6 .
+.Sh AUTHORS
+This
+.Nm
+reference was written by
+.An Kristaps Dzonsons Aq kristaps@bsd.lv .
+.Sh CAVEATS
+Do not use this language.
+Use
+.Xr mdoc 7 ,
+instead.
diff --git a/contrib/mdocml/man.c b/contrib/mdocml/man.c
new file mode 100644 (file)
index 0000000..6788c92
--- /dev/null
@@ -0,0 +1,666 @@
+/*     $Id: man.c,v 1.96 2011/01/03 11:31:26 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "libman.h"
+#include "libmandoc.h"
+
+const  char *const __man_macronames[MAN_MAX] = {                
+       "br",           "TH",           "SH",           "SS",
+       "TP",           "LP",           "PP",           "P",
+       "IP",           "HP",           "SM",           "SB",
+       "BI",           "IB",           "BR",           "RB",
+       "R",            "B",            "I",            "IR",
+       "RI",           "na",           "sp",           "nf",
+       "fi",           "RE",           "RS",           "DT",
+       "UC",           "PD",           "AT",           "in",
+       "ft"
+       };
+
+const  char * const *man_macronames = __man_macronames;
+
+static struct man_node *man_node_alloc(int, int, 
+                               enum man_type, enum mant);
+static int              man_node_append(struct man *, 
+                               struct man_node *);
+static int              man_span_alloc(struct man *, 
+                               const struct tbl_span *);
+static void             man_node_free(struct man_node *);
+static void             man_node_unlink(struct man *, 
+                               struct man_node *);
+static int              man_ptext(struct man *, int, char *, int);
+static int              man_pmacro(struct man *, int, char *, int);
+static void             man_free1(struct man *);
+static void             man_alloc1(struct man *);
+static int              man_descope(struct man *, int, int);
+
+
+const struct man_node *
+man_node(const struct man *m)
+{
+
+       assert( ! (MAN_HALT & m->flags));
+       return(m->first);
+}
+
+
+const struct man_meta *
+man_meta(const struct man *m)
+{
+
+       assert( ! (MAN_HALT & m->flags));
+       return(&m->meta);
+}
+
+
+void
+man_reset(struct man *man)
+{
+
+       man_free1(man);
+       man_alloc1(man);
+}
+
+
+void
+man_free(struct man *man)
+{
+
+       man_free1(man);
+       free(man);
+}
+
+
+struct man *
+man_alloc(struct regset *regs, void *data, mandocmsg msg)
+{
+       struct man      *p;
+
+       p = mandoc_calloc(1, sizeof(struct man));
+
+       man_hash_init();
+       p->data = data;
+       p->msg = msg;
+       p->regs = regs;
+
+       man_alloc1(p);
+       return(p);
+}
+
+
+int
+man_endparse(struct man *m)
+{
+
+       assert( ! (MAN_HALT & m->flags));
+       if (man_macroend(m))
+               return(1);
+       m->flags |= MAN_HALT;
+       return(0);
+}
+
+
+int
+man_parseln(struct man *m, int ln, char *buf, int offs)
+{
+
+       assert( ! (MAN_HALT & m->flags));
+       return(('.' == buf[offs] || '\'' == buf[offs]) ? 
+                       man_pmacro(m, ln, buf, offs) : 
+                       man_ptext(m, ln, buf, offs));
+}
+
+
+static void
+man_free1(struct man *man)
+{
+
+       if (man->first)
+               man_node_delete(man, man->first);
+       if (man->meta.title)
+               free(man->meta.title);
+       if (man->meta.source)
+               free(man->meta.source);
+       if (man->meta.rawdate)
+               free(man->meta.rawdate);
+       if (man->meta.vol)
+               free(man->meta.vol);
+       if (man->meta.msec)
+               free(man->meta.msec);
+}
+
+
+static void
+man_alloc1(struct man *m)
+{
+
+       memset(&m->meta, 0, sizeof(struct man_meta));
+       m->flags = 0;
+       m->last = mandoc_calloc(1, sizeof(struct man_node));
+       m->first = m->last;
+       m->last->type = MAN_ROOT;
+       m->last->tok = MAN_MAX;
+       m->next = MAN_NEXT_CHILD;
+}
+
+
+static int
+man_node_append(struct man *man, struct man_node *p)
+{
+
+       assert(man->last);
+       assert(man->first);
+       assert(MAN_ROOT != p->type);
+
+       switch (man->next) {
+       case (MAN_NEXT_SIBLING):
+               man->last->next = p;
+               p->prev = man->last;
+               p->parent = man->last->parent;
+               break;
+       case (MAN_NEXT_CHILD):
+               man->last->child = p;
+               p->parent = man->last;
+               break;
+       default:
+               abort();
+               /* NOTREACHED */
+       }
+       
+       assert(p->parent);
+       p->parent->nchild++;
+
+       if ( ! man_valid_pre(man, p))
+               return(0);
+
+       switch (p->type) {
+       case (MAN_HEAD):
+               assert(MAN_BLOCK == p->parent->type);
+               p->parent->head = p;
+               break;
+       case (MAN_BODY):
+               assert(MAN_BLOCK == p->parent->type);
+               p->parent->body = p;
+               break;
+       default:
+               break;
+       }
+
+       man->last = p;
+
+       switch (p->type) {
+       case (MAN_TBL):
+               /* FALLTHROUGH */
+       case (MAN_TEXT):
+               if ( ! man_valid_post(man))
+                       return(0);
+               break;
+       default:
+               break;
+       }
+
+       return(1);
+}
+
+
+static struct man_node *
+man_node_alloc(int line, int pos, enum man_type type, enum mant tok)
+{
+       struct man_node *p;
+
+       p = mandoc_calloc(1, sizeof(struct man_node));
+       p->line = line;
+       p->pos = pos;
+       p->type = type;
+       p->tok = tok;
+       return(p);
+}
+
+
+int
+man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
+{
+       struct man_node *p;
+
+       p = man_node_alloc(line, pos, MAN_ELEM, tok);
+       if ( ! man_node_append(m, p))
+               return(0);
+       m->next = MAN_NEXT_CHILD;
+       return(1);
+}
+
+
+int
+man_head_alloc(struct man *m, int line, int pos, enum mant tok)
+{
+       struct man_node *p;
+
+       p = man_node_alloc(line, pos, MAN_HEAD, tok);
+       if ( ! man_node_append(m, p))
+               return(0);
+       m->next = MAN_NEXT_CHILD;
+       return(1);
+}
+
+
+int
+man_body_alloc(struct man *m, int line, int pos, enum mant tok)
+{
+       struct man_node *p;
+
+       p = man_node_alloc(line, pos, MAN_BODY, tok);
+       if ( ! man_node_append(m, p))
+               return(0);
+       m->next = MAN_NEXT_CHILD;
+       return(1);
+}
+
+
+int
+man_block_alloc(struct man *m, int line, int pos, enum mant tok)
+{
+       struct man_node *p;
+
+       p = man_node_alloc(line, pos, MAN_BLOCK, tok);
+       if ( ! man_node_append(m, p))
+               return(0);
+       m->next = MAN_NEXT_CHILD;
+       return(1);
+}
+
+static int
+man_span_alloc(struct man *m, const struct tbl_span *span)
+{
+       struct man_node *n;
+
+       /* FIXME: grab from span */
+       n = man_node_alloc(0, 0, MAN_TBL, MAN_MAX);
+       n->span = span;
+
+       if ( ! man_node_append(m, n))
+               return(0);
+
+       m->next = MAN_NEXT_SIBLING;
+       return(1);
+}
+
+int
+man_word_alloc(struct man *m, int line, int pos, const char *word)
+{
+       struct man_node *n;
+       size_t           sv, len;
+
+       len = strlen(word);
+
+       n = man_node_alloc(line, pos, MAN_TEXT, MAN_MAX);
+       n->string = mandoc_malloc(len + 1);
+       sv = strlcpy(n->string, word, len + 1);
+
+       /* Prohibit truncation. */
+       assert(sv < len + 1);
+
+       if ( ! man_node_append(m, n))
+               return(0);
+
+       m->next = MAN_NEXT_SIBLING;
+       return(1);
+}
+
+
+/*
+ * Free all of the resources held by a node.  This does NOT unlink a
+ * node from its context; for that, see man_node_unlink().
+ */
+static void
+man_node_free(struct man_node *p)
+{
+
+       if (p->string)
+               free(p->string);
+       free(p);
+}
+
+
+void
+man_node_delete(struct man *m, struct man_node *p)
+{
+
+       while (p->child)
+               man_node_delete(m, p->child);
+
+       man_node_unlink(m, p);
+       man_node_free(p);
+}
+
+
+int
+man_addspan(struct man *m, const struct tbl_span *sp)
+{
+
+       assert( ! (MAN_HALT & m->flags));
+       if ( ! man_span_alloc(m, sp))
+               return(0);
+       return(man_descope(m, 0, 0));
+}
+
+static int
+man_descope(struct man *m, int line, int offs)
+{
+       /*
+        * Co-ordinate what happens with having a next-line scope open:
+        * first close out the element scope (if applicable), then close
+        * out the block scope (also if applicable).
+        */
+
+       if (MAN_ELINE & m->flags) {
+               m->flags &= ~MAN_ELINE;
+               if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+                       return(0);
+       }
+
+       if ( ! (MAN_BLINE & m->flags))
+               return(1);
+       m->flags &= ~MAN_BLINE;
+
+       if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+               return(0);
+       return(man_body_alloc(m, line, offs, m->last->tok));
+}
+
+
+static int
+man_ptext(struct man *m, int line, char *buf, int offs)
+{
+       int              i;
+
+       /* Ignore bogus comments. */
+
+       if ('\\' == buf[offs] && 
+                       '.' == buf[offs + 1] && 
+                       '"' == buf[offs + 2]) {
+               man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT);
+               return(1);
+       }
+
+       /* Literal free-form text whitespace is preserved. */
+
+       if (MAN_LITERAL & m->flags) {
+               if ( ! man_word_alloc(m, line, offs, buf + offs))
+                       return(0);
+               return(man_descope(m, line, offs));
+       }
+
+       /* Pump blank lines directly into the backend. */
+
+       for (i = offs; ' ' == buf[i]; i++)
+               /* Skip leading whitespace. */ ;
+
+       if ('\0' == buf[i]) {
+               /* Allocate a blank entry. */
+               if ( ! man_word_alloc(m, line, offs, ""))
+                       return(0);
+               return(man_descope(m, line, offs));
+       }
+
+       /* 
+        * Warn if the last un-escaped character is whitespace. Then
+        * strip away the remaining spaces (tabs stay!).   
+        */
+
+       i = (int)strlen(buf);
+       assert(i);
+
+       if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
+               if (i > 1 && '\\' != buf[i - 2])
+                       man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);
+
+               for (--i; i && ' ' == buf[i]; i--)
+                       /* Spin back to non-space. */ ;
+
+               /* Jump ahead of escaped whitespace. */
+               i += '\\' == buf[i] ? 2 : 1;
+
+               buf[i] = '\0';
+       }
+
+       if ( ! man_word_alloc(m, line, offs, buf + offs))
+               return(0);
+
+       /*
+        * End-of-sentence check.  If the last character is an unescaped
+        * EOS character, then flag the node as being the end of a
+        * sentence.  The front-end will know how to interpret this.
+        */
+
+       assert(i);
+       if (mandoc_eos(buf, (size_t)i, 0))
+               m->last->flags |= MAN_EOS;
+
+       return(man_descope(m, line, offs));
+}
+
+
+static int
+man_pmacro(struct man *m, int ln, char *buf, int offs)
+{
+       int              i, j, ppos;
+       enum mant        tok;
+       char             mac[5];
+       struct man_node *n;
+
+       /* Comments and empties are quickly ignored. */
+
+       offs++;
+
+       if ('\0' == buf[offs])
+               return(1);
+
+       i = offs;
+
+       /*
+        * Skip whitespace between the control character and initial
+        * text.  "Whitespace" is both spaces and tabs.
+        */
+
+       if (' ' == buf[i] || '\t' == buf[i]) {
+               i++;
+               while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
+                       i++;
+               if ('\0' == buf[i])
+                       goto out;
+       }
+
+       ppos = i;
+
+       /*
+        * Copy the first word into a nil-terminated buffer.
+        * Stop copying when a tab, space, or eoln is encountered.
+        */
+
+       j = 0;
+       while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i])
+               mac[j++] = buf[i++];
+       mac[j] = '\0';
+
+       tok = (j > 0 && j < 4) ? man_hash_find(mac) : MAN_MAX;
+       if (MAN_MAX == tok) {
+               man_vmsg(m, MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1);
+               return(1);
+       }
+
+       /* The macro is sane.  Jump to the next word. */
+
+       while (buf[i] && ' ' == buf[i])
+               i++;
+
+       /* 
+        * Trailing whitespace.  Note that tabs are allowed to be passed
+        * into the parser as "text", so we only warn about spaces here.
+        */
+
+       if ('\0' == buf[i] && ' ' == buf[i - 1])
+               man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE);
+
+       /* 
+        * Remove prior ELINE macro, as it's being clobbered by a new
+        * macro.  Note that NSCOPED macros do not close out ELINE
+        * macros---they don't print text---so we let those slip by.
+        */
+
+       if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
+                       m->flags & MAN_ELINE) {
+               n = m->last;
+               assert(MAN_TEXT != n->type);
+
+               /* Remove repeated NSCOPED macros causing ELINE. */
+
+               if (MAN_NSCOPED & man_macros[n->tok].flags)
+                       n = n->parent;
+
+               man_vmsg(m, MANDOCERR_LINESCOPE, n->line, n->pos,
+                               "%s", man_macronames[n->tok]);
+
+               man_node_delete(m, n);
+               m->flags &= ~MAN_ELINE;
+       }
+
+       /*
+        * Save the fact that we're in the next-line for a block.  In
+        * this way, embedded roff instructions can "remember" state
+        * when they exit.
+        */
+
+       if (MAN_BLINE & m->flags)
+               m->flags |= MAN_BPLINE;
+
+       /* Call to handler... */
+
+       assert(man_macros[tok].fp);
+       if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &i, buf))
+               goto err;
+
+out:
+       /* 
+        * We weren't in a block-line scope when entering the
+        * above-parsed macro, so return.
+        */
+
+       if ( ! (MAN_BPLINE & m->flags)) {
+               m->flags &= ~MAN_ILINE; 
+               return(1);
+       }
+       m->flags &= ~MAN_BPLINE;
+
+       /*
+        * If we're in a block scope, then allow this macro to slip by
+        * without closing scope around it.
+        */
+
+       if (MAN_ILINE & m->flags) {
+               m->flags &= ~MAN_ILINE;
+               return(1);
+       }
+
+       /* 
+        * If we've opened a new next-line element scope, then return
+        * now, as the next line will close out the block scope.
+        */
+
+       if (MAN_ELINE & m->flags)
+               return(1);
+
+       /* Close out the block scope opened in the prior line.  */
+
+       assert(MAN_BLINE & m->flags);
+       m->flags &= ~MAN_BLINE;
+
+       if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+               return(0);
+       return(man_body_alloc(m, ln, offs, m->last->tok));
+
+err:   /* Error out. */
+
+       m->flags |= MAN_HALT;
+       return(0);
+}
+
+
+int
+man_vmsg(struct man *man, enum mandocerr t, 
+               int ln, int pos, const char *fmt, ...)
+{
+       char             buf[256];
+       va_list          ap;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
+       va_end(ap);
+       return((*man->msg)(t, man->data, ln, pos, buf));
+}
+
+
+/*
+ * Unlink a node from its context.  If "m" is provided, the last parse
+ * point will also be adjusted accordingly.
+ */
+static void
+man_node_unlink(struct man *m, struct man_node *n)
+{
+
+       /* Adjust siblings. */
+
+       if (n->prev)
+               n->prev->next = n->next;
+       if (n->next)
+               n->next->prev = n->prev;
+
+       /* Adjust parent. */
+
+       if (n->parent) {
+               n->parent->nchild--;
+               if (n->parent->child == n)
+                       n->parent->child = n->prev ? n->prev : n->next;
+       }
+
+       /* Adjust parse point, if applicable. */
+
+       if (m && m->last == n) {
+               /*XXX: this can occur when bailing from validation. */
+               /*assert(NULL == n->next);*/
+               if (n->prev) {
+                       m->last = n->prev;
+                       m->next = MAN_NEXT_SIBLING;
+               } else {
+                       m->last = n->parent;
+                       m->next = MAN_NEXT_CHILD;
+               }
+       }
+
+       if (m && m->first == n)
+               m->first = NULL;
+}
diff --git a/contrib/mdocml/man.h b/contrib/mdocml/man.h
new file mode 100644 (file)
index 0000000..581f55f
--- /dev/null
@@ -0,0 +1,130 @@
+/*     $Id: man.h,v 1.50 2011/01/01 12:59:17 kristaps Exp $ */
+/*
+ * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifndef MAN_H
+#define MAN_H
+
+/* 
+ * What follows is a list of ALL possible macros. 
+ */
+enum   mant {
+       MAN_br = 0,
+       MAN_TH,
+       MAN_SH,
+       MAN_SS,
+       MAN_TP,
+       MAN_LP,
+       MAN_PP,
+       MAN_P,
+       MAN_IP,
+       MAN_HP,
+       MAN_SM,
+       MAN_SB,
+       MAN_BI,
+       MAN_IB,
+       MAN_BR,
+       MAN_RB,
+       MAN_R,
+       MAN_B,
+       MAN_I,
+       MAN_IR,
+       MAN_RI,
+       MAN_na,
+       MAN_sp,
+       MAN_nf,
+       MAN_fi,
+       MAN_RE,
+       MAN_RS,
+       MAN_DT,
+       MAN_UC,
+       MAN_PD,
+       MAN_AT,
+       MAN_in,
+       MAN_ft,
+       MAN_MAX
+};
+
+/* 
+ * Type of a syntax node. 
+ */
+enum   man_type {
+       MAN_TEXT,
+       MAN_ELEM,
+       MAN_ROOT,
+       MAN_BLOCK,
+       MAN_HEAD,
+       MAN_BODY,
+       MAN_TBL
+};
+
+/* 
+ * Information from prologue. 
+ */
+struct man_meta {
+       char            *msec; /* `TH' section (1, 3p, etc.) */
+       time_t           date; /* `TH' normalised date */
+       char            *rawdate; /* raw `TH' date */
+       char            *vol; /* `TH' volume */
+       char            *title; /* `TH' title (e.g., FOO) */
+       char            *source; /* `TH' source (e.g., GNU) */
+};
+
+/* 
+ * Single node in tree-linked AST. 
+ */
+struct man_node {
+       struct man_node *parent; /* parent AST node */
+       struct man_node *child; /* first child AST node */
+       struct man_node *next; /* sibling AST node */
+       struct man_node *prev; /* prior sibling AST node */
+       int              nchild; /* number children */
+       int              line;
+       int              pos;
+       enum mant        tok; /* tok or MAN__MAX if none */
+       int              flags;
+#define        MAN_VALID       (1 << 0) /* has been validated */
+#define        MAN_EOS         (1 << 2) /* at sentence boundary */
+       enum man_type    type; /* AST node type */
+       char            *string; /* TEXT node argument */
+       struct man_node *head; /* BLOCK node HEAD ptr */
+       struct man_node *body; /* BLOCK node BODY ptr */
+       const struct tbl_span *span; /* TBL */
+};
+
+/*
+ * Names of macros.  Index is enum mant.  Indexing into this returns
+ * the normalised name, e.g., man_macronames[MAN_SH] -> "SH".
+ */
+extern const char *const *man_macronames;
+
+__BEGIN_DECLS
+
+struct man;
+
+void             man_free(struct man *);
+struct man      *man_alloc(struct regset *, void *, mandocmsg);
+void             man_reset(struct man *);
+int              man_parseln(struct man *, int, char *, int);
+int              man_endparse(struct man *);
+int              man_addspan(struct man *,
+                       const struct tbl_span *);
+
+const struct man_node *man_node(const struct man *);
+const struct man_meta *man_meta(const struct man *);
+
+__END_DECLS
+
+#endif /*!MAN_H*/
diff --git a/contrib/mdocml/man_argv.c b/contrib/mdocml/man_argv.c
new file mode 100644 (file)
index 0000000..37aac03
--- /dev/null
@@ -0,0 +1,44 @@
+/*     $Id: man_argv.c,v 1.5 2011/01/03 22:42:37 schwarze Exp $ */
+/*
+ * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+
+#include "mandoc.h"
+#include "libman.h"
+#include "libmandoc.h"
+
+
+int
+man_args(struct man *m, int line, int *pos, char *buf, char **v)
+{
+       char     *start;
+
+       assert(*pos);
+       *v = start = buf + *pos;
+       assert(' ' != *start);
+
+       if ('\0' == *start)
+               return(ARGS_EOLN);
+
+       *v = mandoc_getarg(v, m->msg, m->data, line, pos);
+       return('"' == *start ? ARGS_QWORD : ARGS_WORD);
+}
diff --git a/contrib/mdocml/man_hash.c b/contrib/mdocml/man_hash.c
new file mode 100644 (file)
index 0000000..6524f36
--- /dev/null
@@ -0,0 +1,106 @@
+/*     $Id: man_hash.c,v 1.23 2010/07/31 23:52:58 schwarze Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "libman.h"
+
+#define        HASH_DEPTH       6
+
+#define        HASH_ROW(x) do { \
+               if (isupper((u_char)(x))) \
+                       (x) -= 65; \
+               else \
+                       (x) -= 97; \
+               (x) *= HASH_DEPTH; \
+       } while (/* CONSTCOND */ 0)
+
+/*
+ * Lookup table is indexed first by lower-case first letter (plus one
+ * for the period, which is stored in the last row), then by lower or
+ * uppercase second letter.  Buckets correspond to the index of the
+ * macro (the integer value of the enum stored as a char to save a bit
+ * of space).
+ */
+static u_char           table[26 * HASH_DEPTH];
+
+/*
+ * XXX - this hash has global scope, so if intended for use as a library
+ * with multiple callers, it will need re-invocation protection.
+ */
+void
+man_hash_init(void)
+{
+       int              i, j, x;
+
+       memset(table, UCHAR_MAX, sizeof(table));
+
+       assert(/* LINTED */ 
+                       MAN_MAX < UCHAR_MAX);
+
+       for (i = 0; i < (int)MAN_MAX; i++) {
+               x = man_macronames[i][0];
+
+               assert(isalpha((u_char)x));
+
+               HASH_ROW(x);
+
+               for (j = 0; j < HASH_DEPTH; j++)
+                       if (UCHAR_MAX == table[x + j]) {
+                               table[x + j] = (u_char)i;
+                               break;
+                       }
+
+               assert(j < HASH_DEPTH);
+       }
+}
+
+
+enum mant
+man_hash_find(const char *tmp)
+{
+       int              x, y, i;
+       enum mant        tok;
+
+       if ('\0' == (x = tmp[0]))
+               return(MAN_MAX);
+       if ( ! (isalpha((u_char)x)))
+               return(MAN_MAX);
+
+       HASH_ROW(x);
+
+       for (i = 0; i < HASH_DEPTH; i++) {
+               if (UCHAR_MAX == (y = table[x + i]))
+                       return(MAN_MAX);
+
+               tok = (enum mant)y;
+               if (0 == strcmp(tmp, man_macronames[tok]))
+                       return(tok);
+       }
+
+       return(MAN_MAX);
+}
diff --git a/contrib/mdocml/man_html.c b/contrib/mdocml/man_html.c
new file mode 100644 (file)
index 0000000..da6880a
--- /dev/null
@@ -0,0 +1,684 @@
+/*     $Id: man_html.c,v 1.62 2011/01/07 13:20:58 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "out.h"
+#include "html.h"
+#include "man.h"
+#include "main.h"
+
+/* TODO: preserve ident widths. */
+/* FIXME: have PD set the default vspace width. */
+
+#define        INDENT            5
+#define        HALFINDENT        3
+
+#define        MAN_ARGS          const struct man_meta *m, \
+                         const struct man_node *n, \
+                         struct mhtml *mh, \
+                         struct html *h
+
+struct mhtml {
+       int               fl;
+#define        MANH_LITERAL     (1 << 0) /* literal context */
+};
+
+struct htmlman {
+       int             (*pre)(MAN_ARGS);
+       int             (*post)(MAN_ARGS);
+};
+
+static void              print_man(MAN_ARGS);
+static void              print_man_head(MAN_ARGS);
+static void              print_man_nodelist(MAN_ARGS);
+static void              print_man_node(MAN_ARGS);
+
+static int               a2width(const struct man_node *,
+                               struct roffsu *);
+
+static int               man_alt_pre(MAN_ARGS);
+static int               man_br_pre(MAN_ARGS);
+static int               man_ign_pre(MAN_ARGS);
+static int               man_in_pre(MAN_ARGS);
+static int               man_literal_pre(MAN_ARGS);
+static void              man_root_post(MAN_ARGS);
+static int               man_root_pre(MAN_ARGS);
+static int               man_B_pre(MAN_ARGS);
+static int               man_HP_pre(MAN_ARGS);
+static int               man_I_pre(MAN_ARGS);
+static int               man_IP_pre(MAN_ARGS);
+static int               man_PP_pre(MAN_ARGS);
+static int               man_RS_pre(MAN_ARGS);
+static int               man_SH_pre(MAN_ARGS);
+static int               man_SM_pre(MAN_ARGS);
+static int               man_SS_pre(MAN_ARGS);
+
+static const struct htmlman mans[MAN_MAX] = {
+       { man_br_pre, NULL }, /* br */
+       { NULL, NULL }, /* TH */
+       { man_SH_pre, NULL }, /* SH */
+       { man_SS_pre, NULL }, /* SS */
+       { man_IP_pre, NULL }, /* TP */
+       { man_PP_pre, NULL }, /* LP */
+       { man_PP_pre, NULL }, /* PP */
+       { man_PP_pre, NULL }, /* P */
+       { man_IP_pre, NULL }, /* IP */
+       { man_HP_pre, NULL }, /* HP */ 
+       { man_SM_pre, NULL }, /* SM */
+       { man_SM_pre, NULL }, /* SB */
+       { man_alt_pre, NULL }, /* BI */
+       { man_alt_pre, NULL }, /* IB */
+       { man_alt_pre, NULL }, /* BR */
+       { man_alt_pre, NULL }, /* RB */
+       { NULL, NULL }, /* R */
+       { man_B_pre, NULL }, /* B */
+       { man_I_pre, NULL }, /* I */
+       { man_alt_pre, NULL }, /* IR */
+       { man_alt_pre, NULL }, /* RI */
+       { NULL, NULL }, /* na */
+       { man_br_pre, NULL }, /* sp */
+       { man_literal_pre, NULL }, /* nf */
+       { man_literal_pre, NULL }, /* fi */
+       { NULL, NULL }, /* RE */
+       { man_RS_pre, NULL }, /* RS */
+       { man_ign_pre, NULL }, /* DT */
+       { man_ign_pre, NULL }, /* UC */
+       { man_ign_pre, NULL }, /* PD */
+       { man_ign_pre, NULL }, /* AT */
+       { man_in_pre, NULL }, /* in */
+       { man_ign_pre, NULL }, /* ft */
+};
+
+
+void
+html_man(void *arg, const struct man *m)
+{
+       struct html     *h;
+       struct tag      *t;
+       struct mhtml     mh;
+
+       h = (struct html *)arg;
+
+       print_gen_decls(h);
+
+       memset(&mh, 0, sizeof(struct mhtml));
+
+       t = print_otag(h, TAG_HTML, 0, NULL);
+       print_man(man_meta(m), man_node(m), &mh, h);
+       print_tagq(h, t);
+
+       printf("\n");
+}
+
+
+static void
+print_man(MAN_ARGS) 
+{
+       struct tag      *t;
+
+       t = print_otag(h, TAG_HEAD, 0, NULL);
+       print_man_head(m, n, mh, h);
+       print_tagq(h, t);
+
+       t = print_otag(h, TAG_BODY, 0, NULL);
+       print_man_nodelist(m, n, mh, h);
+       print_tagq(h, t);
+}
+
+
+/* ARGSUSED */
+static void
+print_man_head(MAN_ARGS)
+{
+
+       print_gen_head(h);
+       bufinit(h);
+       buffmt(h, "%s(%s)", m->title, m->msec);
+
+       print_otag(h, TAG_TITLE, 0, NULL);
+       print_text(h, h->buf);
+}
+
+
+static void
+print_man_nodelist(MAN_ARGS)
+{
+
+       print_man_node(m, n, mh, h);
+       if (n->next)
+               print_man_nodelist(m, n->next, mh, h);
+}
+
+
+static void
+print_man_node(MAN_ARGS)
+{
+       int              child;
+       struct tag      *t;
+
+       child = 1;
+       t = h->tags.head;
+
+       bufinit(h);
+
+       /*
+        * FIXME: embedded elements within next-line scopes (e.g., `br'
+        * within an empty `B') will cause formatting to be forgotten
+        * due to scope closing out.
+        */
+
+       switch (n->type) {
+       case (MAN_ROOT):
+               child = man_root_pre(m, n, mh, h);
+               break;
+       case (MAN_TEXT):
+               print_text(h, n->string);
+               if (MANH_LITERAL & mh->fl)
+                       print_otag(h, TAG_BR, 0, NULL);
+               return;
+       case (MAN_TBL):
+               print_tbl(h, n->span);
+               break;
+       default:
+               /* 
+                * Close out scope of font prior to opening a macro
+                * scope.  Assert that the metafont is on the top of the
+                * stack (it's never nested).
+                */
+               if (HTMLFONT_NONE != h->metac) {
+                       h->metal = h->metac;
+                       h->metac = HTMLFONT_NONE;
+               }
+               if (mans[n->tok].pre)
+                       child = (*mans[n->tok].pre)(m, n, mh, h);
+               break;
+       }
+
+       if (child && n->child)
+               print_man_nodelist(m, n->child, mh, h);
+
+       /* This will automatically close out any font scope. */
+       print_stagq(h, t);
+
+       bufinit(h);
+
+       switch (n->type) {
+       case (MAN_ROOT):
+               man_root_post(m, n, mh, h);
+               break;
+       case (MAN_TBL):
+               break;
+       default:
+               if (mans[n->tok].post)
+                       (*mans[n->tok].post)(m, n, mh, h);
+               break;
+       }
+}
+
+
+static int
+a2width(const struct man_node *n, struct roffsu *su)
+{
+
+       if (MAN_TEXT != n->type)
+               return(0);
+       if (a2roffsu(n->string, su, SCALE_BU))
+               return(1);
+
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_root_pre(MAN_ARGS)
+{
+       struct htmlpair  tag[3];
+       struct tag      *t, *tt;
+       char             b[BUFSIZ], title[BUFSIZ];
+
+       b[0] = 0;
+       if (m->vol)
+               (void)strlcat(b, m->vol, BUFSIZ);
+
+       snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
+
+       PAIR_SUMMARY_INIT(&tag[0], "Document Header");
+       PAIR_CLASS_INIT(&tag[1], "head");
+       if (NULL == h->style) {
+               PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
+               t = print_otag(h, TAG_TABLE, 3, tag);
+               PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
+               print_otag(h, TAG_COL, 1, tag);
+               print_otag(h, TAG_COL, 1, tag);
+               print_otag(h, TAG_COL, 1, tag);
+       } else
+               t = print_otag(h, TAG_TABLE, 2, tag);
+
+       print_otag(h, TAG_TBODY, 0, NULL);
+
+       tt = print_otag(h, TAG_TR, 0, NULL);
+
+       PAIR_CLASS_INIT(&tag[0], "head-ltitle");
+       print_otag(h, TAG_TD, 1, tag);
+
+       print_text(h, title);
+       print_stagq(h, tt);
+
+       PAIR_CLASS_INIT(&tag[0], "head-vol");
+       if (NULL == h->style) {
+               PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
+               print_otag(h, TAG_TD, 2, tag);
+       } else 
+               print_otag(h, TAG_TD, 1, tag);
+
+       print_text(h, b);
+       print_stagq(h, tt);
+
+       PAIR_CLASS_INIT(&tag[0], "head-rtitle");
+       if (NULL == h->style) {
+               PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
+               print_otag(h, TAG_TD, 2, tag);
+       } else 
+               print_otag(h, TAG_TD, 1, tag);
+
+       print_text(h, title);
+       print_tagq(h, t);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static void
+man_root_post(MAN_ARGS)
+{
+       struct htmlpair  tag[3];
+       struct tag      *t, *tt;
+       char             b[DATESIZ];
+
+       if (m->rawdate)
+               strlcpy(b, m->rawdate, DATESIZ);
+       else
+               time2a(m->date, b, DATESIZ);
+
+       PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
+       PAIR_CLASS_INIT(&tag[1], "foot");
+       if (NULL == h->style) {
+               PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
+               t = print_otag(h, TAG_TABLE, 3, tag);
+               PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
+               print_otag(h, TAG_COL, 1, tag);
+               print_otag(h, TAG_COL, 1, tag);
+       } else
+               t = print_otag(h, TAG_TABLE, 2, tag);
+
+       tt = print_otag(h, TAG_TR, 0, NULL);
+
+       PAIR_CLASS_INIT(&tag[0], "foot-date");
+       print_otag(h, TAG_TD, 1, tag);
+
+       print_text(h, b);
+       print_stagq(h, tt);
+
+       PAIR_CLASS_INIT(&tag[0], "foot-os");
+       if (NULL == h->style) {
+               PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
+               print_otag(h, TAG_TD, 2, tag);
+       } else 
+               print_otag(h, TAG_TD, 1, tag);
+
+       if (m->source)
+               print_text(h, m->source);
+       print_tagq(h, t);
+}
+
+
+
+/* ARGSUSED */
+static int
+man_br_pre(MAN_ARGS)
+{
+       struct roffsu    su;
+       struct htmlpair  tag;
+
+       SCALE_VS_INIT(&su, 1);
+
+       if (MAN_sp == n->tok) {
+               if (n->child)
+                       a2roffsu(n->child->string, &su, SCALE_VS);
+       } else
+               su.scale = 0;
+
+       bufcat_su(h, "height", &su);
+       PAIR_STYLE_INIT(&tag, h);
+       print_otag(h, TAG_DIV, 1, &tag);
+
+       /* So the div isn't empty: */
+       print_text(h, "\\~");
+
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_SH_pre(MAN_ARGS)
+{
+       struct htmlpair  tag;
+
+       if (MAN_BLOCK == n->type) {
+               PAIR_CLASS_INIT(&tag, "section");
+               print_otag(h, TAG_DIV, 1, &tag);
+               return(1);
+       } else if (MAN_BODY == n->type)
+               return(1);
+
+       print_otag(h, TAG_H1, 0, NULL);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_alt_pre(MAN_ARGS)
+{
+       const struct man_node   *nn;
+       int              i;
+       enum htmltag     fp;
+       struct tag      *t;
+
+       for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
+               t = NULL;
+               switch (n->tok) {
+               case (MAN_BI):
+                       fp = i % 2 ? TAG_I : TAG_B;
+                       break;
+               case (MAN_IB):
+                       fp = i % 2 ? TAG_B : TAG_I;
+                       break;
+               case (MAN_RI):
+                       fp = i % 2 ? TAG_I : TAG_MAX;
+                       break;
+               case (MAN_IR):
+                       fp = i % 2 ? TAG_MAX : TAG_I;
+                       break;
+               case (MAN_BR):
+                       fp = i % 2 ? TAG_MAX : TAG_B;
+                       break;
+               case (MAN_RB):
+                       fp = i % 2 ? TAG_B : TAG_MAX;
+                       break;
+               default:
+                       abort();
+                       /* NOTREACHED */
+               }
+
+               if (i)
+                       h->flags |= HTML_NOSPACE;
+
+               if (TAG_MAX != fp)
+                       t = print_otag(h, fp, 0, NULL);
+
+               print_man_node(m, nn, mh, h);
+
+               if (t)
+                       print_tagq(h, t);
+       }
+
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_SM_pre(MAN_ARGS)
+{
+       
+       print_otag(h, TAG_SMALL, 0, NULL);
+       if (MAN_SB == n->tok)
+               print_otag(h, TAG_B, 0, NULL);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_SS_pre(MAN_ARGS)
+{
+       struct htmlpair  tag;
+
+       if (MAN_BLOCK == n->type) {
+               PAIR_CLASS_INIT(&tag, "subsection");
+               print_otag(h, TAG_DIV, 1, &tag);
+               return(1);
+       } else if (MAN_BODY == n->type)
+               return(1);
+
+       print_otag(h, TAG_H2, 0, NULL);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_PP_pre(MAN_ARGS)
+{
+
+       if (MAN_HEAD == n->type)
+               return(0);
+       else if (MAN_BODY == n->type && n->prev)
+               print_otag(h, TAG_P, 0, NULL);
+
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_IP_pre(MAN_ARGS)
+{
+       struct roffsu            su;
+       struct htmlpair          tag;
+       const struct man_node   *nn;
+
+       /*
+        * This scattering of 1-BU margins and pads is to make sure that
+        * when text overruns its box, the subsequent text isn't flush
+        * up against it.  However, the rest of the right-hand box must
+        * also be adjusted in consideration of this 1-BU space.
+        */
+
+       if (MAN_BODY == n->type) { 
+               print_otag(h, TAG_TD, 0, NULL);
+               return(1);
+       }
+
+       nn = MAN_BLOCK == n->type ? 
+               n->head->child : n->parent->head->child;
+
+       SCALE_HS_INIT(&su, INDENT);
+
+       /* Width is the second token. */
+
+       if (MAN_IP == n->tok && NULL != nn)
+               if (NULL != (nn = nn->next))
+                       a2width(nn, &su);
+
+       /* Width is the first token. */
+
+       if (MAN_TP == n->tok && NULL != nn) {
+               /* Skip past non-text children. */
+               while (nn && MAN_TEXT != nn->type)
+                       nn = nn->next;
+               if (nn)
+                       a2width(nn, &su);
+       }
+
+       if (MAN_BLOCK == n->type) {
+               print_otag(h, TAG_P, 0, NULL);
+               print_otag(h, TAG_TABLE, 0, NULL);
+               bufcat_su(h, "width", &su);
+               PAIR_STYLE_INIT(&tag, h);
+               print_otag(h, TAG_COL, 1, &tag);
+               print_otag(h, TAG_COL, 0, NULL);
+               print_otag(h, TAG_TBODY, 0, NULL);
+               print_otag(h, TAG_TR, 0, NULL);
+               return(1);
+       } 
+
+       print_otag(h, TAG_TD, 0, NULL);
+
+       /* For IP, only print the first header element. */
+
+       if (MAN_IP == n->tok && n->child)
+               print_man_node(m, n->child, mh, h);
+
+       /* For TP, only print next-line header elements. */
+
+       if (MAN_TP == n->tok)
+               for (nn = n->child; nn; nn = nn->next)
+                       if (nn->line > n->line)
+                               print_man_node(m, nn, mh, h);
+
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_HP_pre(MAN_ARGS)
+{
+       struct htmlpair  tag;
+       struct roffsu    su;
+       const struct man_node *np;
+
+       np = MAN_BLOCK == n->type ? 
+               n->head->child : 
+               n->parent->head->child;
+
+       if (NULL == np || ! a2width(np, &su))
+               SCALE_HS_INIT(&su, INDENT);
+
+       if (MAN_HEAD == n->type) {
+               print_otag(h, TAG_TD, 0, NULL);
+               return(0);
+       } else if (MAN_BLOCK == n->type) {
+               print_otag(h, TAG_P, 0, NULL);
+               print_otag(h, TAG_TABLE, 0, NULL);
+               bufcat_su(h, "width", &su);
+               PAIR_STYLE_INIT(&tag, h);
+               print_otag(h, TAG_COL, 1, &tag);
+               print_otag(h, TAG_COL, 0, NULL);
+               print_otag(h, TAG_TBODY, 0, NULL);
+               print_otag(h, TAG_TR, 0, NULL);
+               return(1);
+       }
+
+       su.scale = -su.scale;
+       bufcat_su(h, "text-indent", &su);
+       PAIR_STYLE_INIT(&tag, h);
+       print_otag(h, TAG_TD, 1, &tag);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_B_pre(MAN_ARGS)
+{
+
+       print_otag(h, TAG_B, 0, NULL);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_I_pre(MAN_ARGS)
+{
+       
+       print_otag(h, TAG_I, 0, NULL);
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_literal_pre(MAN_ARGS)
+{
+
+       if (MAN_nf == n->tok) {
+               print_otag(h, TAG_BR, 0, NULL);
+               mh->fl |= MANH_LITERAL;
+       } else
+               mh->fl &= ~MANH_LITERAL;
+
+       return(1);
+}
+
+
+/* ARGSUSED */
+static int
+man_in_pre(MAN_ARGS)
+{
+
+       print_otag(h, TAG_BR, 0, NULL);
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_ign_pre(MAN_ARGS)
+{
+
+       return(0);
+}
+
+
+/* ARGSUSED */
+static int
+man_RS_pre(MAN_ARGS)
+{
+       struct htmlpair  tag;
+       struct roffsu    su;
+
+       if (MAN_HEAD == n->type)
+               return(0);
+       else if (MAN_BODY == n->type)
+               return(1);
+
+       SCALE_HS_INIT(&su, INDENT);
+       if (n->head->child)
+               a2width(n->head->child, &su);
+
+       bufcat_su(h, "margin-left", &su);
+       PAIR_STYLE_INIT(&tag, h);
+       print_otag(h, TAG_DIV, 1, &tag);
+       return(1);
+}
diff --git a/contrib/mdocml/man_macro.c b/contrib/mdocml/man_macro.c
new file mode 100644 (file)
index 0000000..bd0ca99
--- /dev/null
@@ -0,0 +1,477 @@
+/*     $Id: man_macro.c,v 1.54 2010/12/08 10:58:22 kristaps Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "libman.h"
+
+enum   rew {
+       REW_REWIND,
+       REW_NOHALT,
+       REW_HALT
+};
+
+static int              blk_close(MACRO_PROT_ARGS);
+static int              blk_exp(MACRO_PROT_ARGS);
+static int              blk_imp(MACRO_PROT_ARGS);
+static int              in_line_eoln(MACRO_PROT_ARGS);
+
+static int              rew_scope(enum man_type, 
+                               struct man *, enum mant);
+static enum rew         rew_dohalt(enum mant, enum man_type, 
+                               const struct man_node *);
+static enum rew         rew_block(enum mant, enum man_type, 
+                               const struct man_node *);
+static int              rew_warn(struct man *, 
+                               struct man_node *, enum mandocerr);
+
+const  struct man_macro __man_macros[MAN_MAX] = {
+       { in_line_eoln, MAN_NSCOPED }, /* br */
+       { in_line_eoln, 0 }, /* TH */
+       { blk_imp, MAN_SCOPED }, /* SH */
+       { blk_imp, MAN_SCOPED }, /* SS */
+       { blk_imp, MAN_SCOPED | MAN_FSCOPED }, /* TP */
+       { blk_imp, 0 }, /* LP */
+       { blk_imp, 0 }, /* PP */
+       { blk_imp, 0 }, /* P */
+       { blk_imp, 0 }, /* IP */
+       { blk_imp, 0 }, /* HP */
+       { in_line_eoln, MAN_SCOPED }, /* SM */
+       { in_line_eoln, MAN_SCOPED }, /* SB */
+       { in_line_eoln, 0 }, /* BI */
+       { in_line_eoln, 0 }, /* IB */
+       { in_line_eoln, 0 }, /* BR */
+       { in_line_eoln, 0 }, /* RB */
+       { in_line_eoln, MAN_SCOPED }, /* R */
+       { in_line_eoln, MAN_SCOPED }, /* B */
+       { in_line_eoln, MAN_SCOPED }, /* I */
+       { in_line_eoln, 0 }, /* IR */
+       { in_line_eoln, 0 }, /* RI */
+       { in_line_eoln, MAN_NSCOPED }, /* na */
+       { in_line_eoln, MAN_NSCOPED }, /* sp */
+       { in_line_eoln, 0 }, /* nf */
+       { in_line_eoln, 0 }, /* fi */
+       { blk_close, 0 }, /* RE */
+       { blk_exp, MAN_EXPLICIT }, /* RS */
+       { in_line_eoln, 0 }, /* DT */
+       { in_line_eoln, 0 }, /* UC */
+       { in_line_eoln, 0 }, /* PD */
+       { in_line_eoln, 0 }, /* AT */
+       { in_line_eoln, 0 }, /* in */
+       { in_line_eoln, 0 }, /* ft */
+};
+
+const  struct man_macro * const man_macros = __man_macros;
+
+
+/*
+ * Warn when "n" is an explicit non-roff macro.
+ */
+static int
+rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
+{
+
+       if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
+               return(1);
+       if (MAN_VALID & n->flags)
+               return(1);
+       if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
+               return(1);
+       return(man_nmsg(m, n, er));
+}
+
+
+/*
+ * Rewind scope.  If a code "er" != MANDOCERR_MAX has been provided, it
+ * will be used if an explicit block scope is being closed out.
+ */
+int
+man_unscope(struct man *m, const struct man_node *n, 
+               enum mandocerr er)
+{
+
+       assert(n);
+
+       /* LINTED */
+       while (m->last != n) {
+               if ( ! rew_warn(m, m->last, er))
+                       return(0);
+               if ( ! man_valid_post(m))
+                       return(0);
+               m->last = m->last->parent;
+               assert(m->last);
+       }
+
+       if ( ! rew_warn(m, m->last, er))
+               return(0);
+       if ( ! man_valid_post(m))
+               return(0);
+
+       m->next = MAN_ROOT == m->last->type ? 
+               MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
+
+       return(1);
+}
+
+
+static enum rew
+rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
+{
+
+       if (MAN_BLOCK == type && ntok == n->parent->tok && 
+                       MAN_BODY == n->parent->type)
+               return(REW_REWIND);
+       return(ntok == n->tok ? REW_HALT : REW_NOHALT);
+}
+
+
+/*
+ * There are three scope levels: scoped to the root (all), scoped to the
+ * section (all less sections), and scoped to subsections (all less
+ * sections and subsections).
+ */
+static enum rew 
+rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
+{
+       enum rew         c;
+
+       /* We cannot progress beyond the root ever. */
+       if (MAN_ROOT == n->type)
+               return(REW_HALT);
+
+       assert(n->parent);
+
+       /* Normal nodes shouldn't go to the level of the root. */
+       if (MAN_ROOT == n->parent->type)
+               return(REW_REWIND);
+
+       /* Already-validated nodes should be closed out. */
+       if (MAN_VALID & n->flags)
+               return(REW_NOHALT);
+
+       /* First: rewind to ourselves. */
+       if (type == n->type && tok == n->tok)
+               return(REW_REWIND);
+
+       /* 
+        * Next follow the implicit scope-smashings as defined by man.7:
+        * section, sub-section, etc.
+        */
+
+       switch (tok) {
+       case (MAN_SH):
+               break;
+       case (MAN_SS):
+               /* Rewind to a section, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+                       return(c);
+               break;
+       case (MAN_RS):
+               /* Rewind to a subsection, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
+                       return(c);
+               /* Rewind to a section, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+                       return(c);
+               break;
+       default:
+               /* Rewind to an offsetter, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
+                       return(c);
+               /* Rewind to a subsection, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
+                       return(c);
+               /* Rewind to a section, if a block. */
+               if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+                       return(c);
+               break;
+       }
+
+       return(REW_NOHALT);
+}
+
+
+/*
+ * Rewinding entails ascending the parse tree until a coherent point,
+ * for example, the `SH' macro will close out any intervening `SS'
+ * scopes.  When a scope is closed, it must be validated and actioned.
+ */
+static int
+rew_scope(enum man_type type, struct man *m, enum mant tok)
+{
+       struct man_node *n;
+       enum rew         c;
+
+       /* LINTED */
+       for (n = m->last; n; n = n->parent) {
+               /* 
+                * Whether we should stop immediately (REW_HALT), stop
+                * and rewind until this point (REW_REWIND), or keep
+                * rewinding (REW_NOHALT).
+                */
+               c = rew_dohalt(tok, type, n);
+               if (REW_HALT == c)
+                       return(1);
+               if (REW_REWIND == c)
+                       break;
+       }
+
+       /* 
+        * Rewind until the current point.  Warn if we're a roff
+        * instruction that's mowing over explicit scopes.
+        */
+       assert(n);
+
+       return(man_unscope(m, n, MANDOCERR_MAX));
+}
+
+
+/*
+ * Close out a generic explicit macro.
+ */
+/* ARGSUSED */
+int
+blk_close(MACRO_PROT_ARGS)
+{
+       enum mant                ntok;
+       const struct man_node   *nn;
+
+       switch (tok) {
+       case (MAN_RE):
+               ntok = MAN_RS;
+               break;
+       default:
+               abort();
+               /* NOTREACHED */
+       }
+
+       for (nn = m->last->parent; nn; nn = nn->parent)
+               if (ntok == nn->tok)
+                       break;
+
+       if (NULL == nn)
+               if ( ! man_pmsg(m, line, ppos, MANDOCERR_NOSCOPE))
+                       return(0);
+
+       if ( ! rew_scope(MAN_BODY, m, ntok))
+               return(0);
+       if ( ! rew_scope(MAN_BLOCK, m, ntok))
+               return(0);
+
+       return(1);
+}
+
+
+/* ARGSUSED */
+int
+blk_exp(MACRO_PROT_ARGS)
+{
+       int              w, la;
+       char            *p;
+
+       /* 
+        * Close out prior scopes.  "Regular" explicit macros cannot be
+        * nested, but we allow roff macros to be placed just about
+        * anywhere.
+        */
+
+       if ( ! rew_scope(MAN_BODY, m, tok))
+               return(0);
+       if ( ! rew_scope(MAN_BLOCK, m, tok))
+               return(0);
+
+       if ( ! man_block_alloc(m, line, ppos, tok))
+               return(0);
+       if ( ! man_head_alloc(m, line, ppos, tok))
+               return(0);
+
+       for (;;) {
+               la = *pos;
+               w = man_args(m, line, pos, buf, &p);
+
+               if (-1 == w)
+                       return(0);
+               if (0 == w)
+                       break;
+
+               if ( ! man_word_alloc(m, line, la, p))
+                       return(0);
+       }
+
+       assert(m);
+       assert(tok != MAN_MAX);
+
+       if ( ! rew_scope(MAN_HEAD, m, tok))
+               return(0);
+       return(man_body_alloc(m, line, ppos, tok));
+}
+
+
+
+/*
+ * Parse an implicit-block macro.  These contain a MAN_HEAD and a
+ * MAN_BODY contained within a MAN_BLOCK.  Rules for closing out other
+ * scopes, such as `SH' closing out an `SS', are defined in the rew
+ * routines.
+ */
+/* ARGSUSED */
+int
+blk_imp(MACRO_PROT_ARGS)
+{
+       int              w, la;
+       char            *p;
+       struct man_node *n;
+
+       /* Close out prior scopes. */
+
+       if ( ! rew_scope(MAN_BODY, m, tok))
+               return(0);
+       if ( ! rew_scope(MAN_BLOCK, m, tok))
+               return(0);
+
+       /* Allocate new block & head scope. */
+
+       if ( ! man_block_alloc(m, line, ppos, tok))
+               return(0);
+       if ( ! man_head_alloc(m, line, ppos, tok))
+               return(0);
+
+       n = m->last;
+
+       /* Add line arguments. */
+
+       for (;;) {
+               la = *pos;
+               w = man_args(m, line, pos, buf, &p);
+
+               if (-1 == w)
+                       return(0);
+               if (0 == w)
+                       break;
+
+               if ( ! man_word_alloc(m, line, la, p))
+                       return(0);
+       }
+
+       /* Close out head and open body (unless MAN_SCOPE). */
+
+       if (MAN_SCOPED & man_macros[tok].flags) {
+               /* If we're forcing scope (`TP'), keep it open. */
+               if (MAN_FSCOPED & man_macros[tok].flags) {
+                       m->flags |= MAN_BLINE;
+                       return(1);
+               } else if (n == m->last) {
+                       m->flags |= MAN_BLINE;
+                       return(1);
+               }
+       }
+
+       if ( ! rew_scope(MAN_HEAD, m, tok))
+               return(0);
+       return(man_body_alloc(m, line, ppos, tok));
+}
+
+
+/* ARGSUSED */
+int
+in_line_eoln(MACRO_PROT_ARGS)
+{
+       int              w, la;
+       char            *p;
+       struct man_node *n;
+
+       if ( ! man_elem_alloc(m, line, ppos, tok))
+               return(0);
+
+       n = m->last;
+
+       for (;;) {
+               la = *pos;
+               w = man_args(m, line, pos, buf, &p);
+
+               if (-1 == w)
+                       return(0);
+               if (0 == w)
+                       break;
+               if ( ! man_word_alloc(m, line, la, p))
+                       return(0);
+       }
+
+       /*
+        * If no arguments are specified and this is MAN_SCOPED (i.e.,
+        * next-line scoped), then set our mode to indicate that we're
+        * waiting for terms to load into our context.
+        */
+
+       if (n == m->last && MAN_SCOPED & man_macros[tok].flags) {
+               assert( ! (MAN_NSCOPED & man_macros[tok].flags));
+               m->flags |= MAN_ELINE;
+               return(1);
+       } 
+
+       /* Set ignorable context, if applicable. */
+
+       if (MAN_NSCOPED & man_macros[tok].flags) {
+               assert( ! (MAN_SCOPED & man_macros[tok].flags));
+               m->flags |= MAN_ILINE;
+       }
+       
+       /*
+        * Rewind our element scope.  Note that when TH is pruned, we'll
+        * be back at the root, so make sure that we don't clobber as
+        * its sibling.
+        */
+
+       for ( ; m->last; m->last = m->last->parent) {
+               if (m->last == n)
+                       break;
+               if (m->last->type == MAN_ROOT)
+                       break;
+               if ( ! man_valid_post(m))
+                       return(0);
+       }
+
+       assert(m->last);
+
+       /*
+        * Same here regarding whether we're back at the root. 
+        */
+
+       if (m->last->type != MAN_ROOT && ! man_valid_post(m))
+               return(0);
+
+       m->next = MAN_ROOT == m->last->type ?
+               MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
+
+       return(1);
+}
+
+
+int
+man_macroend(struct man *m)
+{
+
+       return(man_unscope(m, m->first, MANDOCERR_SCOPEEXIT));
+}
+
diff --git a/contrib/mdocml/man_term.c b/contrib/mdocml/man_term.c
new file mode 100644 (file)
index 0000000..c0ef70d
--- /dev/null
@@ -0,0 +1,1031 @@
+/*     $Id: man_term.c,v 1.94 2011/01/04 01:23:18 schwarze Exp $ */
+/*
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc.h"
+#include "out.h"
+#include "man.h"
+#include "term.h"
+#include "chars.h"
+#include "main.h"
+
+#define        INDENT            7
+#define        HALFINDENT        3
+
+/* FIXME: have PD set the default&nbs