Import mdocml-1.12.1
authorSascha Wildner <saw@online.de>
Sun, 14 Apr 2013 08:03:58 +0000 (10:03 +0200)
committerSascha Wildner <saw@online.de>
Sun, 14 Apr 2013 08:03:58 +0000 (10:03 +0200)
96 files changed:
contrib/mdocml/Makefile
contrib/mdocml/TODO [new file with mode: 0644]
contrib/mdocml/apropos.1 [new file with mode: 0644]
contrib/mdocml/apropos.c [new file with mode: 0644]
contrib/mdocml/apropos_db.c [new file with mode: 0644]
contrib/mdocml/apropos_db.h [new file with mode: 0644]
contrib/mdocml/arch.in
contrib/mdocml/att.in
contrib/mdocml/catman.8 [new file with mode: 0644]
contrib/mdocml/catman.c [new file with mode: 0644]
contrib/mdocml/cgi.c [new file with mode: 0644]
contrib/mdocml/chars.c
contrib/mdocml/chars.in
contrib/mdocml/compat_fgetln.c [new file with mode: 0644]
contrib/mdocml/compat_getsubopt.c [new file with mode: 0644]
contrib/mdocml/compat_strlcat.c [copied from contrib/mdocml/compat.c with 70% similarity]
contrib/mdocml/compat_strlcpy.c [moved from contrib/mdocml/compat.c with 62% similarity]
contrib/mdocml/config.h.post
contrib/mdocml/config.h.pre
contrib/mdocml/demandoc.1 [new file with mode: 0644]
contrib/mdocml/demandoc.c [new file with mode: 0644]
contrib/mdocml/eqn.7
contrib/mdocml/eqn.c
contrib/mdocml/eqn_html.c [new file with mode: 0644]
contrib/mdocml/eqn_term.c [new file with mode: 0644]
contrib/mdocml/example.style.css
contrib/mdocml/html.c
contrib/mdocml/html.h
contrib/mdocml/index.css
contrib/mdocml/index.sgml
contrib/mdocml/lib.in
contrib/mdocml/libman.h
contrib/mdocml/libmandoc.h
contrib/mdocml/libmdoc.h
contrib/mdocml/libroff.h
contrib/mdocml/main.c
contrib/mdocml/main.h
contrib/mdocml/makewhatis.1 [deleted file]
contrib/mdocml/makewhatis.c [deleted file]
contrib/mdocml/man-cgi.css [new file with mode: 0644]
contrib/mdocml/man.7
contrib/mdocml/man.c
contrib/mdocml/man.cgi.7 [new file with mode: 0644]
contrib/mdocml/man.h
contrib/mdocml/man_hash.c
contrib/mdocml/man_html.c
contrib/mdocml/man_macro.c
contrib/mdocml/man_term.c
contrib/mdocml/man_validate.c
contrib/mdocml/mandoc-db.1 [deleted file]
contrib/mdocml/mandoc-db.c [deleted file]
contrib/mdocml/mandoc.1
contrib/mdocml/mandoc.3
contrib/mdocml/mandoc.c
contrib/mdocml/mandoc.h
contrib/mdocml/mandoc_char.7
contrib/mdocml/mandocdb.8 [new file with mode: 0644]
contrib/mdocml/mandocdb.c [new file with mode: 0644]
contrib/mdocml/mandocdb.h [new file with mode: 0644]
contrib/mdocml/manpath.c [new file with mode: 0644]
contrib/mdocml/manpath.h [copied from contrib/mdocml/msec.c with 56% similarity]
contrib/mdocml/mdoc.7
contrib/mdocml/mdoc.c
contrib/mdocml/mdoc_argv.c
contrib/mdocml/mdoc_hash.c
contrib/mdocml/mdoc_html.c
contrib/mdocml/mdoc_macro.c
contrib/mdocml/mdoc_man.c [new file with mode: 0644]
contrib/mdocml/mdoc_term.c
contrib/mdocml/mdoc_validate.c
contrib/mdocml/msec.c
contrib/mdocml/out.c
contrib/mdocml/out.h
contrib/mdocml/preconv.1
contrib/mdocml/preconv.c
contrib/mdocml/predefs.in
contrib/mdocml/read.c
contrib/mdocml/roff.7
contrib/mdocml/roff.c
contrib/mdocml/st.in
contrib/mdocml/style.css
contrib/mdocml/tbl.7
contrib/mdocml/tbl.c
contrib/mdocml/tbl_html.c
contrib/mdocml/tbl_layout.c
contrib/mdocml/tbl_opts.c
contrib/mdocml/tbl_term.c
contrib/mdocml/term.c
contrib/mdocml/term.h
contrib/mdocml/term_ascii.c
contrib/mdocml/term_ps.c
contrib/mdocml/test-fgetln.c [new file with mode: 0644]
contrib/mdocml/test-getsubopt.c [new file with mode: 0644]
contrib/mdocml/test-strptime.c [new file with mode: 0644]
contrib/mdocml/tree.c
contrib/mdocml/whatis.1 [new file with mode: 0644]

index c535f10..304237b 100644 (file)
@@ -1,26 +1,45 @@
 .PHONY:         clean install installwww
 .SUFFIXES:      .sgml .html .md5 .h .h.html
-.SUFFIXES:      .1       .3       .7
-.SUFFIXES:      .1.txt   .3.txt   .7.txt
-.SUFFIXES:      .1.pdf   .3.pdf   .7.pdf
-.SUFFIXES:      .1.ps    .3.ps    .7.ps
-.SUFFIXES:      .1.html  .3.html  .7.html 
-.SUFFIXES:      .1.xhtml .3.xhtml .7.xhtml 
+.SUFFIXES:      .1       .3       .7       .8
+.SUFFIXES:      .1.txt   .3.txt   .7.txt   .8.txt
+.SUFFIXES:      .1.pdf   .3.pdf   .7.pdf   .8.pdf
+.SUFFIXES:      .1.ps    .3.ps    .7.ps    .8.ps
+.SUFFIXES:      .1.html  .3.html  .7.html  .8.html
+.SUFFIXES:      .1.xhtml .3.xhtml .7.xhtml .8.xhtml
 
 # 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\""
 
-VERSION                 = 1.11.3
-VDATE           = 26 May 2011
+VERSION                 = 1.12.1
+VDATE           = 23 March 2012
+
 # IFF your system supports multi-byte functions (setlocale(), wcwidth(),
 # putwchar()) AND has __STDC_ISO_10646__ (that is, wchar_t is simply a
 # UCS-4 value) should you define USE_WCHAR.  If you define it and your
 # system DOESN'T support this, -Tlocale will produce garbage.
 # If you don't define it, -Tlocale is a synonym for -Tacsii.
-CFLAGS         += -g -DUSE_WCHAR -DHAVE_CONFIG_H -DVERSION="\"$(VERSION)\""
+#
+CFLAGS         += -DUSE_WCHAR
+
+# If your system has manpath(1), uncomment this.  This is most any
+# system that's not OpenBSD or NetBSD.  If uncommented, apropos(1),
+# mandocdb(8), and man.cgi will popen(3) manpath(1) to get the MANPATH
+# variable.
+#CFLAGS                += -DUSE_MANPATH
+
+# If your system supports static binaries only, uncomment this.  This
+# appears only to be BSD UNIX systems (Mac OS X has no support and Linux
+# requires -pthreads for static libdb).
+STATIC          = -static
+
+CFLAGS         += -g -DHAVE_CONFIG_H -DVERSION="\"$(VERSION)\""
 CFLAGS         += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
 PREFIX          = /usr/local
+WWWPREFIX       = /var/www
+HTDOCDIR        = $(WWWPREFIX)/htdocs
+CGIBINDIR       = $(WWWPREFIX)/cgi-bin
 BINDIR          = $(PREFIX)/bin
 INCLUDEDIR      = $(PREFIX)/include/mandoc
 LIBDIR          = $(PREFIX)/lib/mandoc
@@ -30,22 +49,47 @@ INSTALL              = install
 INSTALL_PROGRAM         = $(INSTALL) -m 0755
 INSTALL_DATA    = $(INSTALL) -m 0444
 INSTALL_LIB     = $(INSTALL) -m 0644
+INSTALL_SOURCE  = $(INSTALL) -m 0644
 INSTALL_MAN     = $(INSTALL_DATA)
 
-all: mandoc preconv
+# Non-BSD systems (Linux, etc.) need -ldb to compile mandocdb and
+# apropos.
+# However, if you don't have -ldb at all (or it's not native), then
+# comment out apropos and mandocdb. 
+#
+#DBLIB          = -ldb
+DBBIN           = apropos mandocdb man.cgi catman whatis
+DBLN            = llib-lapropos.ln llib-lmandocdb.ln llib-lman.cgi.ln llib-lcatman.ln
+
+all: mandoc preconv demandoc $(DBBIN)
 
 SRCS            = Makefile \
+                  TODO \
+                  apropos.1 \
+                  apropos.c \
+                  apropos_db.c \
+                  apropos_db.h \
                   arch.c \
                   arch.in \
                   att.c \
                   att.in \
+                  catman.8 \
+                  catman.c \
+                  cgi.c \
                   chars.c \
                   chars.in \
-                  compat.c \
+                  compat_fgetln.c \
+                  compat_getsubopt.c \
+                  compat_strlcat.c \
+                  compat_strlcpy.c \
                   config.h.post \
                   config.h.pre \
+                  demandoc.1 \
+                  demandoc.c \
                   eqn.7 \
                   eqn.c \
+                  eqn_html.c \
+                  eqn_term.c \
                   example.style.css \
                   external.png \
                   html.c \
@@ -60,9 +104,11 @@ SRCS                 = Makefile \
                   libroff.h \
                   main.c \
                   main.h \
-                  man.h \
                   man.7 \
                   man.c \
+                  man.cgi.7 \
+                  man-cgi.css \
+                  man.h \
                   man_hash.c \
                   man_html.c \
                   man_macro.c \
@@ -72,16 +118,20 @@ SRCS                = Makefile \
                   mandoc.3 \
                   mandoc.c \
                   mandoc.h \
-                  makewhatis.1 \
-                  makewhatis.c \
                   mandoc_char.7 \
-                  mdoc.h \
+                  mandocdb.8 \
+                  mandocdb.c \
+                  mandocdb.h \
+                  manpath.c \
+                  manpath.h \
                   mdoc.7 \
                   mdoc.c \
+                  mdoc.h \
                   mdoc_argv.c \
                   mdoc_hash.c \
                   mdoc_html.c \
                   mdoc_macro.c \
+                  mdoc_man.c \
                   mdoc_term.c \
                   mdoc_validate.c \
                   msec.c \
@@ -108,12 +158,16 @@ SRCS               = Makefile \
                   term.h \
                   term_ascii.c \
                   term_ps.c \
+                  test-fgetln.c \
+                  test-getsubopt.c \
                   test-mmap.c \
                   test-strlcat.c \
                   test-strlcpy.c \
+                  test-strptime.c \
                   tree.c \
                   vol.c \
-                  vol.in
+                  vol.in \
+                  whatis.1
 
 LIBMAN_OBJS     = man.o \
                   man_hash.o \
@@ -132,7 +186,6 @@ LIBMDOC_OBJS         = arch.o \
                   mdoc_hash.o \
                   mdoc_macro.o \
                   mdoc_validate.o \
-                  msec.o \
                   st.o \
                   vol.o
 LIBMDOC_LNS     = arch.ln \
@@ -143,7 +196,6 @@ LIBMDOC_LNS  = arch.ln \
                   mdoc_hash.ln \
                   mdoc_macro.ln \
                   mdoc_validate.ln \
-                  msec.ln \
                   st.ln \
                   vol.ln
 
@@ -165,14 +217,25 @@ LIBMANDOC_OBJS     = $(LIBMAN_OBJS) \
                   $(LIBROFF_OBJS) \
                   chars.o \
                   mandoc.o \
+                  msec.o \
                   read.o
 LIBMANDOC_LNS   = $(LIBMAN_LNS) \
                   $(LIBMDOC_LNS) \
                   $(LIBROFF_LNS) \
                   chars.ln \
                   mandoc.ln \
+                  msec.ln \
                   read.ln
 
+COMPAT_OBJS     = compat_fgetln.o \
+                  compat_getsubopt.o \
+                  compat_strlcat.o \
+                  compat_strlcpy.o
+COMPAT_LNS      = compat_fgetln.ln \
+                  compat_getsubopt.ln \
+                  compat_strlcat.ln \
+                  compat_strlcpy.ln
+
 arch.o arch.ln: arch.in
 att.o att.ln: att.in
 chars.o chars.ln: chars.in
@@ -187,22 +250,31 @@ $(LIBMDOC_OBJS) $(LIBMDOC_LNS): libmdoc.h
 $(LIBROFF_OBJS) $(LIBROFF_LNS): libroff.h
 $(LIBMANDOC_OBJS) $(LIBMANDOC_LNS): mandoc.h mdoc.h man.h libmandoc.h config.h
 
-MANDOC_HTML_OBJS = html.o \
+$(COMPAT_OBJS) $(COMPAT_LNS): config.h
+
+MANDOC_HTML_OBJS = eqn_html.o \
+                  html.o \
                   man_html.o \
                   mdoc_html.o \
                   tbl_html.o
-MANDOC_HTML_LNS         = html.ln \
+MANDOC_HTML_LNS         = eqn_html.ln \
+                  html.ln \
                   man_html.ln \
                   mdoc_html.ln \
                   tbl_html.ln
 
-MANDOC_TERM_OBJS = man_term.o \
+MANDOC_MAN_OBJS  = mdoc_man.o
+MANDOC_MAN_LNS   = mdoc_man.ln
+
+MANDOC_TERM_OBJS = eqn_term.o \
+                  man_term.o \
                   mdoc_term.o \
                   term.o \
                   term_ascii.o \
                   term_ps.o \
                   tbl_term.o
-MANDOC_TERM_LNS         = man_term.ln \
+MANDOC_TERM_LNS         = eqn_term.ln \
+                  man_term.ln \
                   mdoc_term.ln \
                   term.ln \
                   term_ascii.ln \
@@ -210,11 +282,13 @@ MANDOC_TERM_LNS    = man_term.ln \
                   tbl_term.ln
 
 MANDOC_OBJS     = $(MANDOC_HTML_OBJS) \
+                  $(MANDOC_MAN_OBJS) \
                   $(MANDOC_TERM_OBJS) \
                   main.o \
                   out.o \
                   tree.o
 MANDOC_LNS      = $(MANDOC_HTML_LNS) \
+                  $(MANDOC_MAN_LNS) \
                   $(MANDOC_TERM_LNS) \
                   main.ln \
                   out.ln \
@@ -224,28 +298,76 @@ $(MANDOC_HTML_OBJS) $(MANDOC_HTML_LNS): html.h
 $(MANDOC_TERM_OBJS) $(MANDOC_TERM_LNS): term.h
 $(MANDOC_OBJS) $(MANDOC_LNS): main.h mandoc.h mdoc.h man.h config.h out.h
 
-compat.o compat.ln: config.h
+MANDOCDB_OBJS   = mandocdb.o manpath.o
+MANDOCDB_LNS    = mandocdb.ln manpath.ln
 
-MAKEWHATIS_OBJS         = makewhatis.o
-MAKEWHATIS_LNS  = makewhatis.ln
-
-$(MAKEWHATIS_OBJS) $(MAKEWHATIS_LNS): mandoc.h mdoc.h man.h config.h
+$(MANDOCDB_OBJS) $(MANDOCDB_LNS): mandocdb.h mandoc.h mdoc.h man.h config.h manpath.h
 
 PRECONV_OBJS    = preconv.o
 PRECONV_LNS     = preconv.ln
 
 $(PRECONV_OBJS) $(PRECONV_LNS): config.h
 
-INDEX_MANS      = makewhatis.1.html \
-                  makewhatis.1.xhtml \
-                  makewhatis.1.ps \
-                  makewhatis.1.pdf \
-                  makewhatis.1.txt \
+APROPOS_OBJS    = apropos.o apropos_db.o manpath.o
+APROPOS_LNS     = apropos.ln apropos_db.ln manpath.ln
+
+$(APROPOS_OBJS) $(APROPOS_LNS): config.h mandoc.h apropos_db.h manpath.h mandocdb.h
+
+CGI_OBJS        = $(MANDOC_HTML_OBJS) \
+                  $(MANDOC_MAN_OBJS) \
+                  $(MANDOC_TERM_OBJS) \
+                  cgi.o \
+                  apropos_db.o \
+                  manpath.o \
+                  out.o \
+                  tree.o
+
+CGI_LNS                 = $(MANDOC_HTML_LNS) \
+                  $(MANDOC_MAN_LNS) \
+                  $(MANDOC_TERM_LNS) \
+                  cgi.ln \
+                  apropos_db.ln \
+                  manpath.ln \
+                  out.ln \
+                  tree.ln
+
+$(CGI_OBJS) $(CGI_LNS): main.h mdoc.h man.h out.h config.h mandoc.h apropos_db.h manpath.h mandocdb.h
+
+CATMAN_OBJS     = catman.o manpath.o
+CATMAN_LNS      = catman.ln manpath.ln
+
+$(CATMAN_OBJS) $(CATMAN_LNS): config.h mandoc.h manpath.h mandocdb.h
+
+DEMANDOC_OBJS   = demandoc.o
+DEMANDOC_LNS    = demandoc.ln
+
+$(DEMANDOC_OBJS) $(DEMANDOC_LNS): config.h
+
+INDEX_MANS      = apropos.1.html \
+                  apropos.1.xhtml \
+                  apropos.1.ps \
+                  apropos.1.pdf \
+                  apropos.1.txt \
+                  catman.8.html \
+                  catman.8.xhtml \
+                  catman.8.ps \
+                  catman.8.pdf \
+                  catman.8.txt \
+                  demandoc.1.html \
+                  demandoc.1.xhtml \
+                  demandoc.1.ps \
+                  demandoc.1.pdf \
+                  demandoc.1.txt \
                   mandoc.1.html \
                   mandoc.1.xhtml \
                   mandoc.1.ps \
                   mandoc.1.pdf \
                   mandoc.1.txt \
+                  whatis.1.html \
+                  whatis.1.xhtml \
+                  whatis.1.ps \
+                  whatis.1.pdf \
+                  whatis.1.txt \
                   mandoc.3.html \
                   mandoc.3.xhtml \
                   mandoc.3.ps \
@@ -261,6 +383,11 @@ INDEX_MANS  = makewhatis.1.html \
                   man.7.ps \
                   man.7.pdf \
                   man.7.txt \
+                  man.cgi.7.html \
+                  man.cgi.7.xhtml \
+                  man.cgi.7.ps \
+                  man.cgi.7.pdf \
+                  man.cgi.7.txt \
                   mandoc_char.7.html \
                   mandoc_char.7.xhtml \
                   mandoc_char.7.ps \
@@ -285,7 +412,12 @@ INDEX_MANS  = makewhatis.1.html \
                   tbl.7.xhtml \
                   tbl.7.ps \
                   tbl.7.pdf \
-                  tbl.7.txt
+                  tbl.7.txt \
+                  mandocdb.8.html \
+                  mandocdb.8.xhtml \
+                  mandocdb.8.ps \
+                  mandocdb.8.pdf \
+                  mandocdb.8.txt
 
 $(INDEX_MANS): mandoc
 
@@ -298,20 +430,38 @@ INDEX_OBJS         = $(INDEX_MANS) \
 
 www: index.html
 
-lint: llib-llibmandoc.ln llib-lmandoc.ln llib-lpreconv.ln
+lint: llib-lmandoc.ln llib-lpreconv.ln llib-ldemandoc.ln $(DBLN)
 
 clean:
        rm -f libmandoc.a $(LIBMANDOC_OBJS)
        rm -f llib-llibmandoc.ln $(LIBMANDOC_LNS)
-       rm -f makewhatis $(MAKEWHATIS_OBJS)
-       rm -f llib-lmakewhatis.ln $(MAKEWHATIS_LNS)
+       rm -f mandocdb $(MANDOCDB_OBJS)
+       rm -f llib-lmandocdb.ln $(MANDOCDB_LNS)
        rm -f preconv $(PRECONV_OBJS)
        rm -f llib-lpreconv.ln $(PRECONV_LNS)
+       rm -f apropos whatis $(APROPOS_OBJS)
+       rm -f llib-lapropos.ln $(APROPOS_LNS)
+       rm -f man.cgi $(CGI_OBJS)
+       rm -f llib-lman.cgi.ln $(CGI_LNS)
+       rm -f catman $(CATMAN_OBJS)
+       rm -f llib-lcatman.ln $(CATMAN_LNS)
+       rm -f demandoc $(DEMANDOC_OBJS)
+       rm -f llib-ldemandoc.ln $(DEMANDOC_LNS)
        rm -f mandoc $(MANDOC_OBJS)
        rm -f llib-lmandoc.ln $(MANDOC_LNS)
-       rm -f config.h config.log compat.o compat.ln
-       rm -f mdocml.tar.gz
+       rm -f config.h config.log $(COMPAT_OBJS) $(COMPAT_LNS)
+       rm -f mdocml.tar.gz mdocml-win32.zip mdocml-win64.zip mdocml-macosx.zip
        rm -f index.html $(INDEX_OBJS)
+       rm -rf test-fgetln.dSYM
+       rm -rf test-strlcpy.dSYM
+       rm -rf test-strlcat.dSYM 
+       rm -rf test-strptime.dSYM 
+       rm -rf test-mmap.dSYM 
+       rm -rf test-getsubopt.dSYM
+       rm -rf apropos.dSYM
+       rm -rf catman.dSYM
+       rm -rf mandocdb.dSYM
+       rm -rf whatis.dSYM
 
 install: all
        mkdir -p $(DESTDIR)$(BINDIR)
@@ -321,16 +471,24 @@ install: all
        mkdir -p $(DESTDIR)$(MANDIR)/man1
        mkdir -p $(DESTDIR)$(MANDIR)/man3
        mkdir -p $(DESTDIR)$(MANDIR)/man7
-       $(INSTALL_PROGRAM) mandoc preconv $(DESTDIR)$(BINDIR)
+       $(INSTALL_PROGRAM) mandoc preconv demandoc $(DESTDIR)$(BINDIR)
        $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR)
-       $(INSTALL_LIB) mandoc.h $(DESTDIR)$(INCLUDEDIR)
-       $(INSTALL_MAN) mandoc.1 preconv.1 $(DESTDIR)$(MANDIR)/man1
+       $(INSTALL_LIB) man.h mdoc.h mandoc.h $(DESTDIR)$(INCLUDEDIR)
+       $(INSTALL_MAN) mandoc.1 preconv.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1
        $(INSTALL_MAN) mandoc.3 $(DESTDIR)$(MANDIR)/man3
        $(INSTALL_MAN) man.7 mdoc.7 roff.7 eqn.7 tbl.7 mandoc_char.7 $(DESTDIR)$(MANDIR)/man7
        $(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR)
 
+installcgi: all
+       mkdir -p $(DESTDIR)$(CGIBINDIR)
+       mkdir -p $(DESTDIR)$(HTDOCDIR)
+       $(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR)
+       $(INSTALL_DATA) example.style.css $(DESTDIR)$(HTDOCDIR)/man.css
+       $(INSTALL_DATA) man-cgi.css $(DESTDIR)$(HTDOCDIR)
+
 installwww: www
        mkdir -p $(PREFIX)/snapshots
+       mkdir -p $(PREFIX)/binaries
        $(INSTALL_DATA) index.html external.png index.css $(PREFIX)
        $(INSTALL_DATA) $(INDEX_MANS) style.css $(PREFIX)
        $(INSTALL_DATA) mandoc.h.html man.h.html mdoc.h.html $(PREFIX)
@@ -339,46 +497,115 @@ installwww: www
        $(INSTALL_DATA) mdocml.tar.gz $(PREFIX)/snapshots/mdocml-$(VERSION).tar.gz
        $(INSTALL_DATA) mdocml.md5 $(PREFIX)/snapshots/mdocml-$(VERSION).md5
 
-libmandoc.a: compat.o $(LIBMANDOC_OBJS)
-       $(AR) rs $@ compat.o $(LIBMANDOC_OBJS)
+libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
+       $(AR) rs $@ $(COMPAT_OBJS) $(LIBMANDOC_OBJS)
 
-llib-llibmandoc.ln: compat.ln $(LIBMANDOC_LNS)
-       $(LINT) $(LINTFLAGS) -Clibmandoc compat.ln $(LIBMANDOC_LNS)
+llib-llibmandoc.ln: $(COMPAT_LNS) $(LIBMANDOC_LNS)
+       $(LINT) $(LINTFLAGS) -Clibmandoc $(COMPAT_LNS) $(LIBMANDOC_LNS)
 
 mandoc: $(MANDOC_OBJS) libmandoc.a
-       $(CC) -o $@ $(MANDOC_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) -o $@ $(MANDOC_OBJS) libmandoc.a
 
-llib-lmandoc.ln: $(MANDOC_LNS)
-       $(LINT) $(LINTFLAGS) -Cmandoc $(MANDOC_LNS)
+llib-lmandoc.ln: $(MANDOC_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Cmandoc $(MANDOC_LNS) llib-llibmandoc.ln
 
-# You'll need -ldb for Linux.
-makewhatis: $(MAKEWHATIS_OBJS) libmandoc.a
-       $(CC) -o $@ $(MAKEWHATIS_OBJS) libmandoc.a
+mandocdb: $(MANDOCDB_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) -o $@ $(MANDOCDB_OBJS) libmandoc.a $(DBLIB)
 
-llib-lmakewhatis.ln: $(MAKEWHATIS_LNS)
-       $(LINT) $(LINTFLAGS) -Cmakewhatis $(MAKEWHATIS_LNS)
+llib-lmandocdb.ln: $(MANDOCDB_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Cmandocdb $(MANDOCDB_LNS) llib-llibmandoc.ln
 
 preconv: $(PRECONV_OBJS)
-       $(CC) -o $@ $(PRECONV_OBJS)
+       $(CC) $(LDFLAGS) -o $@ $(PRECONV_OBJS)
+
+llib-lpreconv.ln: $(PRECONV_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Cpreconv $(PRECONV_LNS) llib-llibmandoc.ln
+
+whatis: apropos
+       cp -f apropos whatis
+
+apropos: $(APROPOS_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) -o $@ $(APROPOS_OBJS) libmandoc.a $(DBLIB)
 
-llib-lpreconv.ln: $(PRECONV_LNS)
-       $(LINT) $(LINTFLAGS) -Cpreconv $(PRECONV_LNS)
+llib-lapropos.ln: $(APROPOS_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Capropos $(APROPOS_LNS) llib-llibmandoc.ln
+
+catman: $(CATMAN_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) -o $@ $(CATMAN_OBJS) libmandoc.a $(DBLIB)
+
+llib-lcatman.ln: $(CATMAN_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Ccatman $(CATMAN_LNS) llib-llibmandoc.ln
+
+man.cgi: $(CGI_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) $(STATIC) -o $@ $(CGI_OBJS) libmandoc.a $(DBLIB)
+
+llib-lman.cgi.ln: $(CGI_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Cman.cgi $(CGI_LNS) llib-llibmandoc.ln
+
+demandoc: $(DEMANDOC_OBJS) libmandoc.a
+       $(CC) $(LDFLAGS) -o $@ $(DEMANDOC_OBJS) libmandoc.a
+
+llib-ldemandoc.ln: $(DEMANDOC_LNS) llib-llibmandoc.ln
+       $(LINT) $(LINTFLAGS) -Cdemandoc $(DEMANDOC_LNS) llib-llibmandoc.ln
 
 mdocml.md5: mdocml.tar.gz
        md5 mdocml.tar.gz >$@
 
 mdocml.tar.gz: $(SRCS)
        mkdir -p .dist/mdocml-$(VERSION)/
-       $(INSTALL) -m 0444 $(SRCS) .dist/mdocml-$(VERSION)
+       $(INSTALL_SOURCE) $(SRCS) .dist/mdocml-$(VERSION)
        ( cd .dist/ && tar zcf ../$@ ./ )
        rm -rf .dist/
 
+mdocml-win32.zip: $(SRCS)
+       mkdir -p .win32/mdocml-$(VERSION)/
+       $(INSTALL_SOURCE) $(SRCS) .win32
+       cp .win32/Makefile .win32/Makefile.old
+       egrep -v -e DUSE_WCHAR -e ^DBBIN .win32/Makefile.old >.win32/Makefile
+       ( cd .win32; \
+               CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar CFLAGS='-DOSNAME=\"Windows\"' make; \
+               make install PREFIX=mdocml-$(VERSION) ; \
+               zip -r ../$@ mdocml-$(VERSION) )
+       rm -rf .win32
+
+mdocml-win64.zip: $(SRCS)
+       mkdir -p .win64/mdocml-$(VERSION)/
+       $(INSTALL_SOURCE) $(SRCS) .win64
+       cp .win64/Makefile .win64/Makefile.old
+       egrep -v -e DUSE_WCHAR -e ^DBBIN .win64/Makefile.old >.win64/Makefile
+       ( cd .win64; \
+               CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar CFLAGS='-DOSNAME=\"Windows\"' make; \
+               make install PREFIX=mdocml-$(VERSION) ; \
+               zip -r ../$@ mdocml-$(VERSION) )
+       rm -rf .win64
+
+mdocml-macosx.zip: $(SRCS)
+       mkdir -p .macosx/mdocml-$(VERSION)/
+       $(INSTALL_SOURCE) $(SRCS) .macosx
+       ( cd .macosx; \
+               CFLAGS="-arch i386 -arch x86_64 -arch ppc" LDFLAGS="-arch i386 -arch x86_64 -arch ppc" make; \
+               make install PREFIX=mdocml-$(VERSION) ; \
+               zip -r ../$@ mdocml-$(VERSION) )
+       rm -rf .macosx
+
 index.html: $(INDEX_OBJS)
 
 config.h: config.h.pre config.h.post
        rm -f config.log
        ( cat config.h.pre; \
          echo; \
+         if $(CC) $(CFLAGS) -Werror -o test-fgetln test-fgetln.c >> config.log 2>&1; then \
+               echo '#define HAVE_FGETLN'; \
+               rm test-fgetln; \
+         fi; \
+         if $(CC) $(CFLAGS) -Werror -o test-strptime test-strptime.c >> config.log 2>&1; then \
+               echo '#define HAVE_STRPTIME'; \
+               rm test-strptime; \
+         fi; \
+         if $(CC) $(CFLAGS) -Werror -o test-getsubopt test-getsubopt.c >> config.log 2>&1; then \
+               echo '#define HAVE_GETSUBOPT'; \
+               rm test-getsubopt; \
+         fi; \
          if $(CC) $(CFLAGS) -Werror -o test-strlcat test-strlcat.c >> config.log 2>&1; then \
                echo '#define HAVE_STRLCAT'; \
                rm test-strlcat; \
@@ -398,19 +625,19 @@ config.h: config.h.pre config.h.post
 .h.h.html:
        highlight -I $< >$@
 
-.1.1.txt .3.3.txt .7.7.txt:
+.1.1.txt .3.3.txt .7.7.txt .8.8.txt:
        ./mandoc -Tascii -Wall,stop $< | col -b >$@
 
-.1.1.html .3.3.html .7.7.html:
+.1.1.html .3.3.html .7.7.html .8.8.html:
        ./mandoc -Thtml -Wall,stop -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< >$@
 
-.1.1.ps .3.3.ps .7.7.ps:
+.1.1.ps .3.3.ps .7.7.ps .8.8.ps:
        ./mandoc -Tps -Wall,stop $< >$@
 
-.1.1.xhtml .3.3.xhtml .7.7.xhtml:
+.1.1.xhtml .3.3.xhtml .7.7.xhtml .8.8.xhtml:
        ./mandoc -Txhtml -Wall,stop -Ostyle=style.css,man=%N.%S.xhtml,includes=%I.html $< >$@
 
-.1.1.pdf .3.3.pdf .7.7.pdf:
+.1.1.pdf .3.3.pdf .7.7.pdf .8.8.pdf:
        ./mandoc -Tpdf -Wall,stop $< >$@
 
 .sgml.html:
diff --git a/contrib/mdocml/TODO b/contrib/mdocml/TODO
new file mode 100644 (file)
index 0000000..a870136
--- /dev/null
@@ -0,0 +1,372 @@
+************************************************************************
+* Official mandoc TODO.
+* $Id: TODO,v 1.129 2012/03/04 23:53:37 schwarze Exp $
+************************************************************************
+
+************************************************************************
+* parser bugs
+************************************************************************
+
+- ".\}" on its own line gets translated to bare ".\&"
+  which forces pset() into man(7)
+  and then triggers an unknown macro error
+  reported by naddy@  Sun, 3 Jul 2011 21:52:24 +0200
+
+************************************************************************
+* formatter bugs
+************************************************************************
+
+- tbl(7): Horizontal and vertical lines are formatted badly:
+  With the box option, there is too much white space at the end of cells.
+  Horizontal lines from "=" lines are a bit too long.
+  yuri dot pankov at gmail dot com  Thu, 14 Apr 2011 05:45:26 +0400
+
+************************************************************************
+* missing features
+************************************************************************
+
+--- missing roff features ----------------------------------------------
+
+- The pod2man preamble wants \h'...' with quoted numerical arguments,
+  see for example AUTHORS in MooseX::Getopt.3p, p5-MooseX-Getopt.
+  reported by Andreas Voegele <mail at andreasvoegele dot com>
+  Tue, 22 Nov 2011 15:34:47 +0100 on ports@
+
+- .if n \{
+  .br\}
+  should cause an extra space to be raised.
+
+- .ad (adjust margins)
+  .ad l -- adjust left margin only (flush left)
+  .ad r -- adjust right margin only (flush right)
+  .ad c -- center text on line
+  .ad b -- adjust both margins (alias: .ad n)
+  .na   -- temporarily disable adjustment without changing the mode
+  .ad   -- re-enable adjustment without changing the mode
+  Adjustment mode is ignored while in no-fill mode (.nf).
+
+- .it (line traps) occur in mysql(1), yasm_arch(7)
+  generated by DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+  reported by brad@  Sat, 15 Jan 2011 15:48:18 -0500
+
+- .ns (no-space mode) occurs in xine-config(1)
+  reported by brad@  Sat, 15 Jan 2011 15:45:23 -0500
+
+- xloadimage(1) wants .ti (temporary indent), rep by naddy@
+
+- .ta (tab settings) occurs in ircbug(1) and probably gnats(1)
+  reported by brad@  Sat, 15 Jan 2011 15:50:51 -0500
+
+- \c (interrupted text) occurs in chat(8)
+
+- using undefined strings or macros defines them to be empty
+  wl@  Mon, 14 Nov 2011 14:37:01 +0000
+
+--- missing mdoc features ----------------------------------------------
+
+- fix bad block nesting involving multiple identical explicit blocks
+  see the OpenBSD mdoc_macro.c 1.47 commit message
+
+- .Bl -column .Xo support is missing
+  ultimate goal:
+  restore .Xr and .Dv to
+  lib/libc/compat-43/sigvec.3
+  lib/libc/gen/signal.3
+  lib/libc/sys/sigaction.2
+
+- edge case: decide how to deal with blk_full bad nesting, e.g.
+  .Sh .Nm .Bk .Nm .Ek .Sh found by jmc@ in ssh-keygen(1)
+  from jmc@  Wed, 14 Jul 2010 18:10:32 +0100
+
+- \\ is now implemented correctly
+  * when defining strings and macros using .ds and .de
+  * when parsing roff(7) and man(7) macro arguments
+  It does not yet work in mdoc(7) macro arguments
+  because libmdoc does not yet use mandoc_getarg().
+  Also check what happens in plain text, it must be identical to \e.
+
+- .Bd -filled should not be the same as .Bd -ragged, but align both
+  the left and right margin.  In groff, it is implemented in terms
+  of .ad b, which we don't have either.  Found in cksum(1).
+
+- implement blank `Bl -column', such as
+  .Bl -column
+  .It foo Ta bar
+  .El
+
+- explicitly disallow nested `Bl -column', which would clobber internal
+  flags defined for struct mdoc_macro
+
+- In .Bl -column .It, the end of the line probably has to be regarded
+  as an implicit .Ta, if there could be one, see the following mildly
+  ugly code from login.conf(5):
+    .Bl -column minpasswordlen program xetcxmotd
+    .It path Ta path Ta value of Dv _PATH_DEFPATH
+    .br
+    Default search path.
+  reported by Michal Mazurek <akfaew at jasminek dot net>
+  via jmc@ Thu, 7 Apr 2011 16:00:53 +0059
+
+- inside `.Bl -column' phrases, punctuation is handled like normal
+  text, e.g. `.Bl -column .It Fl x . Ta ...' should give "-x -."
+
+- inside `.Bl -column' phrases, TERMP_IGNDELIM handling by `Pf'
+  is not safe, e.g. `.Bl -column .It Pf a b .' gives "ab."
+  but should give "ab ."
+
+- set a meaningful default if no `Bl' list type is assigned
+
+- have a blank `It' head for `Bl -tag' not puke
+
+- prohibit `Nm' from having non-text HEAD children
+  (e.g., NetBSD mDNSShared/dns-sd.1)
+  (mdoc_html.c and mdoc_term.c `Nm' handlers can be slightly simplified)
+
+- When there is free text in the SYNOPSIS and that free text contains
+  the .Nm macro, groff somehow understands to treat the .Nm as an in-line
+  macro, while mandoc treats it as a block macro and breaks the line.
+  No idea how the logic for distinguishing in-line and block instances
+  should be, needs investigation.
+  uqs@  Thu, 2 Jun 2011 11:03:51 +0200
+  uqs@  Thu, 2 Jun 2011 11:33:35 +0200
+
+--- missing man features -----------------------------------------------
+
+- groff an-ext.tmac macros (.UR, .UE) occur in xine(5)
+  reported by brad@  Sat, 15 Jan 2011 15:45:23 -0500
+
+- -T[x]html doesn't stipulate non-collapsing spaces in literal mode
+
+--- missing tbl features -----------------------------------------------
+
+- implement basic non-parametric .de to support e.g. sox(1)
+  reported by naddy@ Sat, 16 Oct 2010 23:51:57 +0200
+  *** sox(1) still doesn't work, tbl(1) errors need investigation
+
+- allow standalone `.' to be interpreted as an end-of-layout
+  delimiter instead of being thrown away as a no-op roff line
+  reported by Yuri Pankov, Wed 18 May 2011 11:34:59 CEST
+
+--- missing misc features ----------------------------------------------
+
+- clean up escape sequence handling, creating three classes:
+  (1) fully implemented, or parsed and ignored without loss of content
+  (2) unimplemented, potentially causing loss of content
+      or serious mangling of formatting (e.g. \n) -> ERROR
+      see textproc/mgdiff(1) for nice examples
+  (3) undefined, just output the character -> perhaps WARNING
+
+- The \t escape sequence is the same as a literal tab, see for example
+  the ASCII table in hexdump(1) where
+    .Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo
+    .It \&000\ nul\t001\ soh\t002\ stx\t003\ etx\t004\ eot\t005\ enq
+  produces
+    000 nul  001 soh  002 stx  003 etx  004 eot  005 enq
+  and the example in oldrdist(1)
+
+- look at pages generated from reStructeredText, e.g. devel/mercurial hg(1)
+  These are a weird mixture of man(7) and custom autogenerated low-level
+  roff stuff.  Figure out to what extent we can cope.
+  For details, see http://docutils.sourceforge.net/rst.html
+  noted by stsp@  Sat, 24 Apr 2010 09:17:55 +0200
+  reminded by nicm@  Mon, 3 May 2010 09:52:41 +0100
+
+- check compatibility with Plan9:
+  http://swtch.com/usr/local/plan9/tmac/tmac.an
+  http://swtch.com/plan9port/man/man7/man.html
+  "Anthony J. Bentley" <anthonyjbentley@gmail.com> 28 Dec 2010 21:58:40 -0700
+
+************************************************************************
+* formatting issues: ugly output
+************************************************************************
+
+- a column list with blank `Ta' cells triggers a spurrious
+  start-with-whitespace printing of a newline
+
+- double quotes inside double quotes are escaped by doubling them
+  implement this in mdoc(7), too
+  so far, we only have it in roff(7) and man(7)
+  reminded by millert@  Thu, 09 Dec 2010 17:29:52 -0500
+
+- perl(1) SYNOPSIS looks bad; reported by deraadt@
+  1) man(7) seems to need SYNOPSIS .Nm blocks, too
+
+- In .Bl -column,
+  .It Em Authentication<tab>Key Length
+  ought to render "Key Length" with emphasis, too,
+  see OpenBSD iked.conf(5).
+  reported again Nicolas Joly via wiz@ Wed, 12 Oct 2011 00:20:00 +0200
+
+- empty phrases in .Bl column produce too few blanks
+  try e.g. .Bl -column It Ta Ta
+  reported by millert Fri, 02 Apr 2010 16:13:46 -0400
+
+- .%T can have trailing punctuation.  Currently, it puts the trailing
+  punctuation into a trailing MDOC_TEXT element inside its own scope.
+  That element should rather be outside its scope, such that the
+  punctuation does not get underlines.  This is not trivial to
+  implement because .%T then needs some features of in_line_eoln() -
+  slurp all arguments into one single text element - and one feature
+  of in_line() - put trailing punctuation out of scope.
+  Found in mount_nfs(8) and exports(5), search for "Appendix".
+
+- in enclosures, mandoc sometimes fancies a bogus end of sentence
+  reminded by jmc@  Thu, 23 Sep 2010 18:13:39 +0059
+
+************************************************************************
+* formatting issues: gratuitous differences
+************************************************************************
+
+- .Rv (and probably .Ex) print different text if an `Nm' has been named
+  or not (run a manual without `Nm blah' to see this).  I'm not sure
+  that this exists in the wild, but it's still an error.
+
+- In .Bl -bullet, the groff bullet is "+\b+\bo\bo", the mandoc bullet
+  is just "o\bo".
+  see for example OpenBSD ksh(1)
+
+- The characters "|" and "\*(Ba" should never be bold,
+  not even in the middle of a word, e.g. ".Cm b\*(Bac" in
+  "mknod [-m mode] name b|c major minor"
+  in OpenBSD ksh(1)
+
+- A bogus .Pp between two .It must not produce a double blank line,
+  see between -R and -r in OpenBSD rm(1), before "update" in mount(8),
+  or in DIAGNOSTICS in init(8), or before "is always true" in ksh(1).
+  The same happens with .Pp just before .El, see bgpd.conf(5).
+  Also have `It' complain if `Pp' is invoked at certain times (not
+  -compact?).
+
+- .Pp between two .It in .Bl -column should produce one,
+  not two blank lines, see e.g. login.conf(5).
+  reported by jmc@  Sun, 17 Apr 2011 14:04:58 +0059
+  reported again by sthen@  Wed, 18 Jan 2012 02:09:39 +0000 (UTC)
+
+- If the *first* line after .It is .Pp, break the line right after
+  the tag, do not pad with space characters before breaking.
+  See the description of the a, c, and i commands in sed(1).
+
+- If the first line after .It is .D1, do not assert a blank line
+  in between, see for example tmux(1).
+  reported by nicm@  13 Jan 2011 00:18:57 +0000
+
+- .Nx 1.0a
+  should be "NetBSD 1.0A", not "NetBSD 1.0a",
+  see OpenBSD ccdconfig(8).
+
+- In .Bl -tag, if a tag exceeds the right margin and must be continued
+  on the next line, it must be indented by -width, not width+1;
+  see "rule block|pass" in OpenBSD ifconfig(8).
+
+- When the -width string contains macros, the macros must be rendered
+  before measuring the width, for example
+    .Bl -tag -width ".Dv message"
+  in magic(5), located in src/usr.bin/file, is the same
+  as -width 7n, not -width 11n.
+  The same applies to .Bl -column column widths;
+  reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar
+
+- The \& zero-width character counts as output.
+  That is, when it is alone on a line between two .Pp,
+  we want three blank lines, not two as in mandoc.
+
+- When .Fn arguments exceed one output line, all but the first
+  should be indented, see e.g. rpc(3);
+  reported by jmc@ on discuss@  Fri, 29 Oct 2010 13:48:33 +0100
+  reported again by Nicolas Joly via wiz@  Sun, 18 Sep 2011 18:24:40 +0200
+  Also, we don't want to break the line within the argument of:
+  .Fa "chtype tl"
+
+- .Ns should work when called at the end of an input line, see
+  the following code in vi(1):
+    .It Xo
+    .Op Ar line
+    .Cm a Ns Op Cm ppend Ns
+    .Op Cm !\&
+    .Xc
+    The input text is appended after the specified line.
+
+- Header lines of excessive length:
+  Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c
+  and document it in mdoc(7) and man(7) COMPATIBILITY
+  found while talking to Chris Bennett
+
+- In man(7), the sequence
+    .HP
+    one line of regular text
+    .SH
+  should not produce two blank lines before the .SH,
+  see for example named-checkconf(8).
+
+- In man(7), the sequence
+    .SH HEADER
+    <blank line>
+    .PP
+    regular text
+  should not produce any blank lines between the header and the text,
+  see for example rsync(1).
+  Reported by naddy@  Mon, 28 Mar 2011 20:45:42 +0200
+
+- In man(7), the sequence
+    regular text
+    .IP
+    .IP "tag"
+    indented text
+  should produce one, not four blank lines between the regular text
+  and the tag, see for example rsync(1).
+  Likewise,
+    regular text
+    .IP
+    indented text
+  should produce one, not two blank lines in between, and
+    regular text
+    .IP
+    .RS
+    .IP tag
+    indented text
+  should produce one, not three blank lines.
+  Reported by naddy@  Mon, 28 Mar 2011 20:45:42 +0200
+
+- trailing whitespace must be ignored even when followed by a font escape,
+  see for example 
+    makes
+    \fBdig \fR
+    operate in batch mode
+  in dig(1).
+
+************************************************************************
+* error reporting issues
+************************************************************************
+
+- .TP directly followed by .RS gives an assertion.
+
+************************************************************************
+* performance issues
+************************************************************************
+
+Several areas can be cleaned up to make mandoc even faster.  These are 
+
+- improve hashing mechanism for macros (quite important: performance)
+
+- improve hashing mechanism for characters (not as important)
+
+- the PDF file is HUGE: this can be reduced by using relative offsets
+
+- instead of re-initialising the roff predefined-strings set before each
+  parse, create a read-only version the first time and copy it 
+
+************************************************************************
+* structural issues
+************************************************************************
+
+- We use the input line number at several places to distinguish
+  same-line from different-line input.  That plainly doesn't work
+  with user-defined macros, leading to random breakage.
+
+- Find better ways to prevent endless loops
+  in roff(7) macro and string expansion.
+- Finish cleanup of date handling.
+  Decide which formats should be recognized where.
+  Update both mdoc(7) and man(7) documentation.
+  Triggered by  Tim van der Molen  Tue, 22 Feb 2011 20:30:45 +0100
diff --git a/contrib/mdocml/apropos.1 b/contrib/mdocml/apropos.1
new file mode 100644 (file)
index 0000000..7dea132
--- /dev/null
@@ -0,0 +1,328 @@
+.\"    $Id: apropos.1,v 1.17 2012/03/24 01:46:25 kristaps Exp $
+.\"
+.\" Copyright (c) 2011, 2012 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: March 24 2012 $
+.Dt APROPOS 1
+.Os
+.Sh NAME
+.Nm apropos
+.Nd search manual page databases
+.Sh SYNOPSIS
+.Nm
+.Op Fl C Ar file
+.Op Fl M Ar manpath
+.Op Fl m Ar manpath
+.Op Fl S Ar arch
+.Op Fl s Ar section
+.Ar expression ...
+.Sh DESCRIPTION
+The
+.Nm
+utility queries manual page databases generated by
+.Xr mandocdb 8 ,
+evaluating on
+.Ar expression
+for each file in each database.
+.Pp
+By default,
+.Nm
+searches for
+.Xr mandocdb 8
+databases in the default paths stipulated by
+.Xr man 1 ,
+parses terms as case-sensitive regular expressions
+over manual names and descriptions.
+Multiple terms imply pairwise
+.Fl o .
+If standard output is a TTY, a result may be selected from a list and
+its manual displayed with the pager.
+.Pp
+Its arguments are as follows:
+.Bl -tag -width Ds
+.It Fl C Ar file
+Specify an alternative configuration
+.Ar file
+in
+.Xr man.conf 5
+format.
+.It Fl M Ar manpath
+Use the colon-separated path instead of the default list of paths
+searched for
+.Xr mandocdb 8
+databases.
+Invalid paths, or paths without manual databases, are ignored.
+.It Fl m Ar manpath
+Prepend the colon-separated paths to the list of paths searched
+for
+.Xr mandocdb 8
+databases.
+Invalid paths, or paths without manual databases, are ignored.
+.It Fl S Ar arch
+Search only for a particular architecture.
+.It Fl s Ar cat
+Search only for a manual section.
+See
+.Xr man 1
+for a listing of manual sections.
+.El
+.Pp
+An
+.Ar expression
+consists of search terms joined by logical operators
+.Fl a
+.Pq and
+and
+.Fl o
+.Pq or .
+The
+.Fl a
+operator has precedence over
+.Fl o
+and both are evaluated left-to-right.
+.Bl -tag -width Ds
+.It \&( Ar expr No \&)
+True if the subexpression
+.Ar expr
+is true.
+.It Ar expr1 Fl a Ar expr2
+True if both
+.Ar expr1
+and
+.Ar expr2
+are true (logical
+.Qq and ) .
+.It Ar expr1 Oo Fl o Oc Ar expr2
+True if
+.Ar expr1
+and/or
+.Ar expr2
+evaluate to true (logical
+.Qq or ) .
+.It Ar term
+True if
+.Ar term
+is satisfied.
+This has syntax
+.Li [key[,key]*(=~)]?val ,
+where operand
+.Cm key
+is an
+.Xr mdoc 7
+macro to query and
+.Cm val
+is its value.
+See
+.Sx Macro Keys
+for a list of available keys.
+Operator
+.Li \&=
+evaluates a substring, while
+.Li \&~
+evaluates a regular expression.
+.It Fl i Ar term
+If
+.Ar term
+is a regular expression, it
+is evaluated case-insensitively.
+Has no effect on substring terms.
+.El
+.Pp
+Results are sorted by manual title, with output formatted as
+.Pp
+.D1 title(sec) \- description
+.Pp
+Where
+.Qq title
+is the manual's title (note multiple manual names may exist for one
+title),
+.Qq sec
+is the manual section, and
+.Qq description
+is the manual's short description.
+If an architecture is specified for the manual, it is displayed as
+.Pp
+.D1 title(cat/arch) \- description
+.Pp
+If on a TTY, results are prefixed with a numeric identifier.
+.Pp
+.D1 [index] title(cat) \- description
+.Pp
+One may choose a manual be entering the index at the prompt.
+Valid choices are displayed using
+.Ev MANPAGER ,
+or failing that ,
+.Ev PAGER
+or just
+.Xr more 1 .
+Source pages are formatted with
+.Xr mandoc 1 ;
+preformatted pages with
+.Xr cat 1 .
+.Ss Macro Keys
+Queries evaluate over a subset of
+.Xr mdoc 7
+macros indexed by
+.Xr mandocdb 8 .
+In addition to the macro keys listed below, the special key
+.Cm any
+may be used to match any available macro key.
+.Pp
+Names and description:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&Nm Ta manual name
+.It Li \&Nd Ta one-line manual description
+.El
+.Pp
+Sections and cross references:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&Sh Ta section header (excluding standard sections)
+.It Li \&Ss Ta subsection header
+.It Li \&Xr Ta cross reference to another manual page
+.It Li \&Rs Ta bibliographic reference
+.El
+.Pp
+Semantic markup for command line utilities:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&Fl Ta command line options (flags)
+.It Li \&Cm Ta command modifier
+.It Li \&Ar Ta command argument
+.It Li \&Ic Ta internal or interactive command
+.It Li \&Ev Ta environmental variable
+.It Li \&Pa Ta file system path
+.El
+.Pp
+Semantic markup for function libraries:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&Lb Ta function library name
+.It Li \&In Ta include file
+.It Li \&Ft Ta function return type
+.It Li \&Fn Ta function name
+.It Li \&Fa Ta function argument type and name
+.It Li \&Vt Ta variable type
+.It Li \&Va Ta variable name
+.It Li \&Dv Ta defined variable or preprocessor constant
+.It Li \&Er Ta error constant
+.It Li \&Ev Ta environmental variable
+.El
+.Pp
+Various semantic markup:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&An Ta author name
+.It Li \&Lk Ta hyperlink
+.It Li \&Mt Ta Do mailto Dc hyperlink
+.It Li \&Cd Ta kernel configuration declaration
+.It Li \&Ms Ta mathematical symbol
+.It Li \&Tn Ta tradename
+.El
+.Pp
+Physical markup:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&Em Ta italic font or underline
+.It Li \&Sy Ta boldface font
+.It Li \&Li Ta typewriter font
+.El
+.Pp
+Text production:
+.Bl -column "xLix" description -offset indent -compact
+.It Li \&St Ta reference to a standards document
+.It Li \&At Ta At No version reference
+.It Li \&Bx Ta Bx No version reference
+.It Li \&Bsx Ta Bsx No version reference
+.It Li \&Nx Ta Nx No version reference
+.It Li \&Fx Ta Fx No version reference
+.It Li \&Ox Ta Ox No version reference
+.It Li \&Dx Ta Dx No version reference
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev MANPAGER
+Default pager for manuals.
+If this is unset, falls back to
+.Ev Pager .
+.It Ev PAGER
+The second choice for a manual pager.
+If this is unset, use
+.Xr more 1 .
+.It Ev MANPATH
+Colon-separated paths modifying the default list of paths searched for
+manual databases.
+Invalid paths, or paths without manual databases, are ignored.
+Overridden by
+.Fl M .
+If
+.Ev MANPATH
+begins with a
+.Sq \&: ,
+it is appended to the default list;
+else if it ends with
+.Sq \&: ,
+it is prepended to the default list; else if it contains
+.Sq \&:: ,
+the default list is inserted between the colons.
+If none of these conditions are met, it overrides the default list.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/man.conf" -compact
+.It Pa whatis.db
+name of the
+.Xr mandocdb 8
+keyword database
+.It Pa whatis.index
+name of the
+.Xr mandocdb 8
+filename database
+.It Pa /etc/man.conf
+default
+.Xr man 1
+configuration file
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Search for
+.Qq mdoc
+as a substring and regular expression
+within each manual name and description:
+.Pp
+.Dl $ apropos mdoc
+.Dl $ apropos ~^mdoc$
+.Pp
+Include matches for
+.Qq roff
+and
+.Qq man
+for the regular expression case:
+.Pp
+.Dl $ apropos ~^mdoc$ roff man
+.Dl $ apropos ~^mdoc$ \-o roff \-o man
+.Pp
+Search for
+.Qq optind
+and
+.Qq optarg
+as variable names in the library category:
+.Pp
+.Dl $ apropos \-s 3 Va~^optind \-a Va~^optarg$
+.Sh SEE ALSO
+.Xr more 1
+.Xr re_format 7 ,
+.Xr mandocdb 8
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Kristaps Dzonsons ,
+.Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/apropos.c b/contrib/mdocml/apropos.c
new file mode 100644 (file)
index 0000000..9c3cae9
--- /dev/null
@@ -0,0 +1,239 @@
+/*     $Id: apropos.c,v 1.30 2012/03/24 02:18:51 kristaps Exp $ */
+/*
+ * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * 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/param.h>
+
+#include <assert.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "apropos_db.h"
+#include "mandoc.h"
+#include "manpath.h"
+
+#define        SINGLETON(_res, _sz) \
+       ((_sz) && (_res)[0].matched && \
+        (1 == (_sz) || 0 == (_res)[1].matched))
+#define        EMPTYSET(_res, _sz) \
+       ((0 == (_sz)) || 0 == (_res)[0].matched)
+
+static int      cmp(const void *, const void *);
+static void     list(struct res *, size_t, void *);
+static void     usage(void);
+
+static char    *progname;
+
+int
+main(int argc, char *argv[])
+{
+       int              ch, rc, whatis, usecat;
+       struct res      *res;
+       struct manpaths  paths;
+       const char      *prog;
+       pid_t            pid;
+       char             path[PATH_MAX];
+       int              fds[2];
+       size_t           terms, ressz, sz;
+       struct opts      opts;
+       struct expr     *e;
+       char            *defpaths, *auxpaths, *conf_file, *cp;
+       extern int       optind;
+       extern char     *optarg;
+
+       progname = strrchr(argv[0], '/');
+       if (progname == NULL)
+               progname = argv[0];
+       else
+               ++progname;
+
+       whatis = 0 == strncmp(progname, "whatis", 6);
+
+       memset(&paths, 0, sizeof(struct manpaths));
+       memset(&opts, 0, sizeof(struct opts));
+
+       usecat = 0;
+       ressz = 0;
+       res = NULL;
+       auxpaths = defpaths = NULL;
+       conf_file = NULL;
+       e = NULL;
+       path[0] = '\0';
+
+       while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:")))
+               switch (ch) {
+               case ('C'):
+                       conf_file = optarg;
+                       break;
+               case ('M'):
+                       defpaths = optarg;
+                       break;
+               case ('m'):
+                       auxpaths = optarg;
+                       break;
+               case ('S'):
+                       opts.arch = optarg;
+                       break;
+               case ('s'):
+                       opts.cat = optarg;
+                       break;
+               default:
+                       usage();
+                       return(EXIT_FAILURE);
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       if (0 == argc) 
+               return(EXIT_SUCCESS);
+
+       rc = 0;
+
+       manpath_parse(&paths, conf_file, defpaths, auxpaths);
+
+       e = whatis ? termcomp(argc, argv, &terms) :
+                    exprcomp(argc, argv, &terms);
+               
+       if (NULL == e) {
+               fprintf(stderr, "%s: Bad expression\n", progname);
+               goto out;
+       }
+
+       rc = apropos_search
+               (paths.sz, paths.paths, &opts, 
+                e, terms, NULL, &ressz, &res, list);
+
+       terms = 1;
+
+       if (0 == rc) {
+               fprintf(stderr, "%s: Bad database\n", progname);
+               goto out;
+       } else if ( ! isatty(STDOUT_FILENO) || EMPTYSET(res, ressz))
+               goto out;
+
+       if ( ! SINGLETON(res, ressz)) {
+               printf("Which manpage would you like [1]? ");
+               fflush(stdout);
+               if (NULL != (cp = fgetln(stdin, &sz)) && 
+                               sz > 1 && '\n' == cp[--sz]) {
+                       if ((ch = atoi(cp)) <= 0)
+                               goto out;
+                       terms = (size_t)ch;
+               }
+       }
+
+       if (--terms < ressz && res[terms].matched) {
+               chdir(paths.paths[res[terms].volume]);
+               strlcpy(path, res[terms].file, PATH_MAX);
+               usecat = RESTYPE_CAT == res[terms].type;
+       }
+out:
+       manpath_free(&paths);
+       resfree(res, ressz);
+       exprfree(e);
+
+       if ('\0' == path[0])
+               return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
+
+       if (-1 == pipe(fds)) {
+               perror(NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       if (-1 == (pid = fork())) {
+               perror(NULL);
+               exit(EXIT_FAILURE);
+       } else if (pid > 0) {
+               dup2(fds[0], STDIN_FILENO);
+               close(fds[1]);
+               prog = NULL != getenv("MANPAGER") ? 
+                       getenv("MANPAGER") :
+                       (NULL != getenv("PAGER") ? 
+                        getenv("PAGER") : "more");
+               execlp(prog, prog, (char *)NULL);
+               perror(prog);
+               return(EXIT_FAILURE);
+       }
+
+       dup2(fds[1], STDOUT_FILENO);
+       close(fds[0]);
+       prog = usecat ? "cat" : "mandoc";
+       execlp(prog, prog, path, (char *)NULL);
+       perror(prog);
+       return(EXIT_FAILURE);
+}
+
+/* ARGSUSED */
+static void
+list(struct res *res, size_t sz, void *arg)
+{
+       size_t           i;
+
+       qsort(res, sz, sizeof(struct res), cmp);
+
+       if (EMPTYSET(res, sz) || SINGLETON(res, sz))
+               return;
+
+       if ( ! isatty(STDOUT_FILENO))
+               for (i = 0; i < sz && res[i].matched; i++)
+                       printf("%s(%s%s%s) - %.70s\n", 
+                                       res[i].title, res[i].cat,
+                                       *res[i].arch ? "/" : "",
+                                       *res[i].arch ? res[i].arch : "",
+                                       res[i].desc);
+       else
+               for (i = 0; i < sz && res[i].matched; i++)
+                       printf("[%zu] %s(%s%s%s) - %.70s\n", i + 1,
+                                       res[i].title, res[i].cat,
+                                       *res[i].arch ? "/" : "",
+                                       *res[i].arch ? res[i].arch : "",
+                                       res[i].desc);
+}
+
+static int
+cmp(const void *p1, const void *p2)
+{
+       const struct res *r1 = p1;
+       const struct res *r2 = p2;
+
+       if (0 == r1->matched)
+               return(1);
+       else if (0 == r2->matched)
+               return(1);
+
+       return(strcasecmp(r1->title, r2->title));
+}
+
+static void
+usage(void)
+{
+
+       fprintf(stderr, "usage: %s "
+                       "[-C file] "
+                       "[-M manpath] "
+                       "[-m manpath] "
+                       "[-S arch] "
+                       "[-s section] "
+                       "expression ...\n",
+                       progname);
+}
diff --git a/contrib/mdocml/apropos_db.c b/contrib/mdocml/apropos_db.c
new file mode 100644 (file)
index 0000000..8aea771
--- /dev/null
@@ -0,0 +1,876 @@
+/*     $Id: apropos_db.c,v 1.31 2012/03/24 01:46:25 kristaps Exp $ */
+/*
+ * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * 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 <assert.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+# include <endian.h>
+# include <db_185.h>
+#elif defined(__APPLE__)
+# include <libkern/OSByteOrder.h>
+# include <db.h>
+#else
+# include <db.h>
+#endif
+
+#include "mandocdb.h"
+#include "apropos_db.h"
+#include "mandoc.h"
+
+#define        RESFREE(_x) \
+       do { \
+               free((_x)->file); \
+               free((_x)->cat); \
+               free((_x)->title); \
+               free((_x)->arch); \
+               free((_x)->desc); \
+               free((_x)->matches); \
+       } while (/*CONSTCOND*/0)
+
+struct expr {
+       int              regex; /* is regex? */
+       int              index; /* index in match array */
+       uint64_t         mask; /* type-mask */
+       int              and; /* is rhs of logical AND? */
+       char            *v; /* search value */
+       regex_t          re; /* compiled re, if regex */
+       struct expr     *next; /* next in sequence */
+       struct expr     *subexpr;
+};
+
+struct type {
+       uint64_t         mask;
+       const char      *name;
+};
+
+struct rectree {
+       struct res      *node; /* record array for dir tree */
+       int              len; /* length of record array */
+};
+
+static const struct type types[] = {
+       { TYPE_An, "An" },
+       { TYPE_Ar, "Ar" },
+       { TYPE_At, "At" },
+       { TYPE_Bsx, "Bsx" },
+       { TYPE_Bx, "Bx" },
+       { TYPE_Cd, "Cd" },
+       { TYPE_Cm, "Cm" },
+       { TYPE_Dv, "Dv" },
+       { TYPE_Dx, "Dx" },
+       { TYPE_Em, "Em" },
+       { TYPE_Er, "Er" },
+       { TYPE_Ev, "Ev" },
+       { TYPE_Fa, "Fa" },
+       { TYPE_Fl, "Fl" },
+       { TYPE_Fn, "Fn" },
+       { TYPE_Fn, "Fo" },
+       { TYPE_Ft, "Ft" },
+       { TYPE_Fx, "Fx" },
+       { TYPE_Ic, "Ic" },
+       { TYPE_In, "In" },
+       { TYPE_Lb, "Lb" },
+       { TYPE_Li, "Li" },
+       { TYPE_Lk, "Lk" },
+       { TYPE_Ms, "Ms" },
+       { TYPE_Mt, "Mt" },
+       { TYPE_Nd, "Nd" },
+       { TYPE_Nm, "Nm" },
+       { TYPE_Nx, "Nx" },
+       { TYPE_Ox, "Ox" },
+       { TYPE_Pa, "Pa" },
+       { TYPE_Rs, "Rs" },
+       { TYPE_Sh, "Sh" },
+       { TYPE_Ss, "Ss" },
+       { TYPE_St, "St" },
+       { TYPE_Sy, "Sy" },
+       { TYPE_Tn, "Tn" },
+       { TYPE_Va, "Va" },
+       { TYPE_Va, "Vt" },
+       { TYPE_Xr, "Xr" },
+       { UINT64_MAX, "any" },
+       { 0, NULL }
+};
+
+static DB      *btree_open(void);
+static int      btree_read(const DBT *, const DBT *,
+                       const struct mchars *,
+                       uint64_t *, recno_t *, char **);
+static int      expreval(const struct expr *, int *);
+static void     exprexec(const struct expr *,
+                       const char *, uint64_t, struct res *);
+static int      exprmark(const struct expr *,
+                       const char *, uint64_t, int *);
+static struct expr *exprexpr(int, char *[], int *, int *, size_t *);
+static struct expr *exprterm(char *, int);
+static DB      *index_open(void);
+static int      index_read(const DBT *, const DBT *, int,
+                       const struct mchars *, struct res *);
+static void     norm_string(const char *,
+                       const struct mchars *, char **);
+static size_t   norm_utf8(unsigned int, char[7]);
+static int      single_search(struct rectree *, const struct opts *,
+                       const struct expr *, size_t terms,
+                       struct mchars *, int);
+
+/*
+ * Open the keyword mandoc-db database.
+ */
+static DB *
+btree_open(void)
+{
+       BTREEINFO        info;
+       DB              *db;
+
+       memset(&info, 0, sizeof(BTREEINFO));
+       info.lorder = 4321;
+       info.flags = R_DUP;
+
+       db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info);
+       if (NULL != db)
+               return(db);
+
+       return(NULL);
+}
+
+/*
+ * Read a keyword from the database and normalise it.
+ * Return 0 if the database is insane, else 1.
+ */
+static int
+btree_read(const DBT *k, const DBT *v, const struct mchars *mc,
+               uint64_t *mask, recno_t *rec, char **buf)
+{
+       uint64_t         vbuf[2];
+
+       /* Are our sizes sane? */
+       if (k->size < 2 || sizeof(vbuf) != v->size)
+               return(0);
+
+       /* Is our string nil-terminated? */
+       if ('\0' != ((const char *)k->data)[(int)k->size - 1])
+               return(0);
+
+       norm_string((const char *)k->data, mc, buf);
+       memcpy(vbuf, v->data, v->size);
+       *mask = betoh64(vbuf[0]);
+       *rec  = betoh64(vbuf[1]);
+       return(1);
+}
+
+/*
+ * Take a Unicode codepoint and produce its UTF-8 encoding.
+ * This isn't the best way to do this, but it works.
+ * The magic numbers are from the UTF-8 packaging.
+ * They're not as scary as they seem: read the UTF-8 spec for details.
+ */
+static size_t
+norm_utf8(unsigned int cp, char out[7])
+{
+       int              rc;
+
+       rc = 0;
+
+       if (cp <= 0x0000007F) {
+               rc = 1;
+               out[0] = (char)cp;
+       } else if (cp <= 0x000007FF) {
+               rc = 2;
+               out[0] = (cp >> 6  & 31) | 192;
+               out[1] = (cp       & 63) | 128;
+       } else if (cp <= 0x0000FFFF) {
+               rc = 3;
+               out[0] = (cp >> 12 & 15) | 224;
+               out[1] = (cp >> 6  & 63) | 128;
+               out[2] = (cp       & 63) | 128;
+       } else if (cp <= 0x001FFFFF) {
+               rc = 4;
+               out[0] = (cp >> 18 & 7) | 240;
+               out[1] = (cp >> 12 & 63) | 128;
+               out[2] = (cp >> 6  & 63) | 128;
+               out[3] = (cp       & 63) | 128;
+       } else if (cp <= 0x03FFFFFF) {
+               rc = 5;
+               out[0] = (cp >> 24 & 3) | 248;
+               out[1] = (cp >> 18 & 63) | 128;
+               out[2] = (cp >> 12 & 63) | 128;
+               out[3] = (cp >> 6  & 63) | 128;
+               out[4] = (cp       & 63) | 128;
+       } else if (cp <= 0x7FFFFFFF) {
+               rc = 6;
+               out[0] = (cp >> 30 & 1) | 252;
+               out[1] = (cp >> 24 & 63) | 128;
+               out[2] = (cp >> 18 & 63) | 128;
+               out[3] = (cp >> 12 & 63) | 128;
+               out[4] = (cp >> 6  & 63) | 128;
+               out[5] = (cp       & 63) | 128;
+       } else
+               return(0);
+
+       out[rc] = '\0';
+       return((size_t)rc);
+}
+
+/*
+ * Normalise strings from the index and database.
+ * These strings are escaped as defined by mandoc_char(7) along with
+ * other goop in mandoc.h (e.g., soft hyphens).
+ * This function normalises these into a nice UTF-8 string.
+ * Returns 0 if the database is fucked.
+ */
+static void
+norm_string(const char *val, const struct mchars *mc, char **buf)
+{
+       size_t            sz, bsz;
+       char              utfbuf[7];
+       const char       *seq, *cpp;
+       int               len, u, pos;
+       enum mandoc_esc   esc;
+       static const char res[] = { '\\', '\t',
+                               ASCII_NBRSP, ASCII_HYPH, '\0' };
+
+       /* Pre-allocate by the length of the input */
+
+       bsz = strlen(val) + 1;
+       *buf = mandoc_realloc(*buf, bsz);
+       pos = 0;
+
+       while ('\0' != *val) {
+               /*
+                * Halt on the first escape sequence.
+                * This also halts on the end of string, in which case
+                * we just copy, fallthrough, and exit the loop.
+                */
+               if ((sz = strcspn(val, res)) > 0) {
+                       memcpy(&(*buf)[pos], val, sz);
+                       pos += (int)sz;
+                       val += (int)sz;
+               }
+
+               if (ASCII_HYPH == *val) {
+                       (*buf)[pos++] = '-';
+                       val++;
+                       continue;
+               } else if ('\t' == *val || ASCII_NBRSP == *val) {
+                       (*buf)[pos++] = ' ';
+                       val++;
+                       continue;
+               } else if ('\\' != *val)
+                       break;
+
+               /* Read past the slash. */
+
+               val++;
+               u = 0;
+
+               /*
+                * Parse the escape sequence and see if it's a
+                * predefined character or special character.
+                */
+
+               esc = mandoc_escape(&val, &seq, &len);
+               if (ESCAPE_ERROR == esc)
+                       break;
+
+               /*
+                * XXX - this just does UTF-8, but we need to know
+                * beforehand whether we should do text substitution.
+                */
+
+               switch (esc) {
+               case (ESCAPE_SPECIAL):
+                       if (0 != (u = mchars_spec2cp(mc, seq, len)))
+                               break;
+                       /* FALLTHROUGH */
+               default:
+                       continue;
+               }
+
+               /*
+                * If we have a Unicode codepoint, try to convert that
+                * to a UTF-8 byte string.
+                */
+
+               cpp = utfbuf;
+               if (0 == (sz = norm_utf8(u, utfbuf)))
+                       continue;
+
+               /* Copy the rendered glyph into the stream. */
+
+               sz = strlen(cpp);
+               bsz += sz;
+
+               *buf = mandoc_realloc(*buf, bsz);
+
+               memcpy(&(*buf)[pos], cpp, sz);
+               pos += (int)sz;
+       }
+
+       (*buf)[pos] = '\0';
+}
+
+/*
+ * Open the filename-index mandoc-db database.
+ * Returns NULL if opening failed.
+ */
+static DB *
+index_open(void)
+{
+       DB              *db;
+
+       db = dbopen(MANDOC_IDX, O_RDONLY, 0, DB_RECNO, NULL);
+       if (NULL != db)
+               return(db);
+
+       return(NULL);
+}
+
+/*
+ * Safely unpack from an index file record into the structure.
+ * Returns 1 if an entry was unpacked, 0 if the database is insane.
+ */
+static int
+index_read(const DBT *key, const DBT *val, int index,
+               const struct mchars *mc, struct res *rec)
+{
+       size_t           left;
+       char            *np, *cp;
+       char             type;
+
+#define        INDEX_BREAD(_dst) \
+       do { \
+               if (NULL == (np = memchr(cp, '\0', left))) \
+                       return(0); \
+               norm_string(cp, mc, &(_dst)); \
+               left -= (np - cp) + 1; \
+               cp = np + 1; \
+       } while (/* CONSTCOND */ 0)
+
+       if (0 == (left = val->size))
+               return(0);
+
+       cp = val->data;
+       assert(sizeof(recno_t) == key->size);
+       memcpy(&rec->rec, key->data, key->size);
+       rec->volume = index;
+
+       if ('d' == (type = *cp++))
+               rec->type = RESTYPE_MDOC;
+       else if ('a' == type)
+               rec->type = RESTYPE_MAN;
+       else if ('c' == type)
+               rec->type = RESTYPE_CAT;
+       else
+               return(0);
+
+       left--;
+       INDEX_BREAD(rec->file);
+       INDEX_BREAD(rec->cat);
+       INDEX_BREAD(rec->title);
+       INDEX_BREAD(rec->arch);
+       INDEX_BREAD(rec->desc);
+       return(1);
+}
+
+/*
+ * Search mandocdb databases in paths for expression "expr".
+ * Filter out by "opts".
+ * Call "res" with the results, which may be zero.
+ * Return 0 if there was a database error, else return 1.
+ */
+int
+apropos_search(int pathsz, char **paths, const struct opts *opts,
+               const struct expr *expr, size_t terms, void *arg,
+               size_t *sz, struct res **resp,
+               void (*res)(struct res *, size_t, void *))
+{
+       struct rectree   tree;
+       struct mchars   *mc;
+       int              i, rc;
+
+       memset(&tree, 0, sizeof(struct rectree));
+
+       rc = 0;
+       mc = mchars_alloc();
+       *sz = 0;
+       *resp = NULL;
+
+       /*
+        * Main loop.  Change into the directory containing manpage
+        * databases.  Run our expession over each database in the set.
+        */
+
+       for (i = 0; i < pathsz; i++) {
+               if (chdir(paths[i]))
+                       continue;
+               if (single_search(&tree, opts, expr, terms, mc, i))
+                       continue;
+
+               resfree(tree.node, tree.len);
+               mchars_free(mc);
+               return(0);
+       }
+
+       (*res)(tree.node, tree.len, arg);
+       *sz = tree.len;
+       *resp = tree.node;
+       mchars_free(mc);
+       return(1);
+}
+
+static int
+single_search(struct rectree *tree, const struct opts *opts,
+               const struct expr *expr, size_t terms,
+               struct mchars *mc, int vol)
+{
+       int              root, leaf, ch;
+       DBT              key, val;
+       DB              *btree, *idx;
+       char            *buf;
+       struct res      *rs;
+       struct res       r;
+       uint64_t         mask;
+       recno_t          rec;
+
+       root    = -1;
+       leaf    = -1;
+       btree   = NULL;
+       idx     = NULL;
+       buf     = NULL;
+       rs      = tree->node;
+
+       memset(&r, 0, sizeof(struct res));
+
+       if (NULL == (btree = btree_open()))
+               return(1);
+
+       if (NULL == (idx = index_open())) {
+               (*btree->close)(btree);
+               return(1);
+       }
+
+       while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) {
+               if ( ! btree_read(&key, &val, mc, &mask, &rec, &buf))
+                       break;
+
+               /*
+                * See if this keyword record matches any of the
+                * expressions we have stored.
+                */
+               if ( ! exprmark(expr, buf, mask, NULL))
+                       continue;
+
+               /*
+                * O(log n) scan for prior records.  Since a record
+                * number is unbounded, this has decent performance over
+                * a complex hash function.
+                */
+
+               for (leaf = root; leaf >= 0; )
+                       if (rec > rs[leaf].rec &&
+                                       rs[leaf].rhs >= 0)
+                               leaf = rs[leaf].rhs;
+                       else if (rec < rs[leaf].rec &&
+                                       rs[leaf].lhs >= 0)
+                               leaf = rs[leaf].lhs;
+                       else
+                               break;
+
+               /*
+                * If we find a record, see if it has already evaluated
+                * to true.  If it has, great, just keep going.  If not,
+                * try to evaluate it now and continue anyway.
+                */
+
+               if (leaf >= 0 && rs[leaf].rec == rec) {
+                       if (0 == rs[leaf].matched)
+                               exprexec(expr, buf, mask, &rs[leaf]);
+                       continue;
+               }
+
+               /*
+                * We have a new file to examine.
+                * Extract the manpage's metadata from the index
+                * database, then begin partial evaluation.
+                */
+
+               key.data = &rec;
+               key.size = sizeof(recno_t);
+
+               if (0 != (*idx->get)(idx, &key, &val, 0))
+                       break;
+
+               r.lhs = r.rhs = -1;
+               if ( ! index_read(&key, &val, vol, mc, &r))
+                       break;
+
+               /* XXX: this should be elsewhere, I guess? */
+
+               if (opts->cat && strcasecmp(opts->cat, r.cat))
+                       continue;
+
+               if (opts->arch && *r.arch)
+                       if (strcasecmp(opts->arch, r.arch))
+                               continue;
+
+               tree->node = rs = mandoc_realloc
+                       (rs, (tree->len + 1) * sizeof(struct res));
+
+               memcpy(&rs[tree->len], &r, sizeof(struct res));
+               memset(&r, 0, sizeof(struct res));
+               rs[tree->len].matches =
+                       mandoc_calloc(terms, sizeof(int));
+
+               exprexec(expr, buf, mask, &rs[tree->len]);
+
+               /* Append to our tree. */
+
+               if (leaf >= 0) {
+                       if (rec > rs[leaf].rec)
+                               rs[leaf].rhs = tree->len;
+                       else
+                               rs[leaf].lhs = tree->len;
+               } else
+                       root = tree->len;
+
+               tree->len++;
+       }
+
+       (*btree->close)(btree);
+       (*idx->close)(idx);
+
+       free(buf);
+       RESFREE(&r);
+       return(1 == ch);
+}
+
+void
+resfree(struct res *rec, size_t sz)
+{
+       size_t           i;
+
+       for (i = 0; i < sz; i++)
+               RESFREE(&rec[i]);
+       free(rec);
+}
+
+/*
+ * Compile a list of straight-up terms.
+ * The arguments are re-written into ~[[:<:]]term[[:>:]], or "term"
+ * surrounded by word boundaries, then pumped through exprterm().
+ * Terms are case-insensitive.
+ * This emulates whatis(1) behaviour.
+ */
+struct expr *
+termcomp(int argc, char *argv[], size_t *tt)
+{
+       char            *buf;
+       int              pos;
+       struct expr     *e, *next;
+       size_t           sz;
+
+       buf = NULL;
+       e = NULL;
+       *tt = 0;
+
+       for (pos = argc - 1; pos >= 0; pos--) {
+               sz = strlen(argv[pos]) + 18;
+               buf = mandoc_realloc(buf, sz);
+               strlcpy(buf, "Nm~[[:<:]]", sz);
+               strlcat(buf, argv[pos], sz);
+               strlcat(buf, "[[:>:]]", sz);
+               if (NULL == (next = exprterm(buf, 0))) {
+                       free(buf);
+                       exprfree(e);
+                       return(NULL);
+               }
+               next->next = e;
+               e = next;
+               (*tt)++;
+       }
+
+       free(buf);
+       return(e);
+}
+
+/*
+ * Compile a sequence of logical expressions.
+ * See apropos.1 for a grammar of this sequence.
+ */
+struct expr *
+exprcomp(int argc, char *argv[], size_t *tt)
+{
+       int              pos, lvl;
+       struct expr     *e;
+
+       pos = lvl = 0;
+       *tt = 0;
+
+       e = exprexpr(argc, argv, &pos, &lvl, tt);
+
+       if (0 == lvl && pos >= argc)
+               return(e);
+
+       exprfree(e);
+       return(NULL);
+}
+
+/*
+ * Compile an array of tokens into an expression.
+ * An informal expression grammar is defined in apropos(1).
+ * Return NULL if we fail doing so.  All memory will be cleaned up.
+ * Return the root of the expression sequence if alright.
+ */
+static struct expr *
+exprexpr(int argc, char *argv[], int *pos, int *lvl, size_t *tt)
+{
+       struct expr     *e, *first, *next;
+       int              log;
+
+       first = next = NULL;
+
+       for ( ; *pos < argc; (*pos)++) {
+               e = next;
+
+               /*
+                * Close out a subexpression.
+                */
+
+               if (NULL != e && 0 == strcmp(")", argv[*pos])) {
+                       if (--(*lvl) < 0)
+                               goto err;
+                       break;
+               }
+
+               /*
+                * Small note: if we're just starting, don't let "-a"
+                * and "-o" be considered logical operators: they're
+                * just tokens unless pairwise joining, in which case we
+                * record their existence (or assume "OR").
+                */
+               log = 0;
+
+               if (NULL != e && 0 == strcmp("-a", argv[*pos]))
+                       log = 1;
+               else if (NULL != e && 0 == strcmp("-o", argv[*pos]))
+                       log = 2;
+
+               if (log > 0 && ++(*pos) >= argc)
+                       goto err;
+
+               /*
+                * Now we parse the term part.  This can begin with
+                * "-i", in which case the expression is case
+                * insensitive.
+                */
+
+               if (0 == strcmp("(", argv[*pos])) {
+                       ++(*pos);
+                       ++(*lvl);
+                       next = mandoc_calloc(1, sizeof(struct expr));
+                       next->subexpr = exprexpr(argc, argv, pos, lvl, tt);
+                       if (NULL == next->subexpr) {
+                               free(next);
+                               next = NULL;
+                       }
+               } else if (0 == strcmp("-i", argv[*pos])) {
+                       if (++(*pos) >= argc)
+                               goto err;
+                       next = exprterm(argv[*pos], 0);
+               } else
+                       next = exprterm(argv[*pos], 1);
+
+               if (NULL == next)
+                       goto err;
+
+               next->and = log == 1;
+               next->index = (int)(*tt)++;
+
+               /* Append to our chain of expressions. */
+
+               if (NULL == first) {
+                       assert(NULL == e);
+                       first = next;
+               } else {
+                       assert(NULL != e);
+                       e->next = next;
+               }
+       }
+
+       return(first);
+err:
+       exprfree(first);
+       return(NULL);
+}
+
+/*
+ * Parse a terminal expression with the grammar as defined in
+ * apropos(1).
+ * Return NULL if we fail the parse.
+ */
+static struct expr *
+exprterm(char *buf, int cs)
+{
+       struct expr      e;
+       struct expr     *p;
+       char            *key;
+       int              i;
+
+       memset(&e, 0, sizeof(struct expr));
+
+       /* Choose regex or substring match. */
+
+       if (NULL == (e.v = strpbrk(buf, "=~"))) {
+               e.regex = 0;
+               e.v = buf;
+       } else {
+               e.regex = '~' == *e.v;
+               *e.v++ = '\0';
+       }
+
+       /* Determine the record types to search for. */
+
+       e.mask = 0;
+       if (buf < e.v) {
+               while (NULL != (key = strsep(&buf, ","))) {
+                       i = 0;
+                       while (types[i].mask &&
+                                       strcmp(types[i].name, key))
+                               i++;
+                       e.mask |= types[i].mask;
+               }
+       }
+       if (0 == e.mask)
+               e.mask = TYPE_Nm | TYPE_Nd;
+
+       if (e.regex) {
+               i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE);
+               if (regcomp(&e.re, e.v, i))
+                       return(NULL);
+       }
+
+       e.v = mandoc_strdup(e.v);
+
+       p = mandoc_calloc(1, sizeof(struct expr));
+       memcpy(p, &e, sizeof(struct expr));
+       return(p);
+}
+
+void
+exprfree(struct expr *p)
+{
+       struct expr     *pp;
+
+       while (NULL != p) {
+               if (p->subexpr)
+                       exprfree(p->subexpr);
+               if (p->regex)
+                       regfree(&p->re);
+               free(p->v);
+               pp = p->next;
+               free(p);
+               p = pp;
+       }
+}
+
+static int
+exprmark(const struct expr *p, const char *cp,
+               uint64_t mask, int *ms)
+{
+
+       for ( ; p; p = p->next) {
+               if (p->subexpr) {
+                       if (exprmark(p->subexpr, cp, mask, ms))
+                               return(1);
+                       continue;
+               } else if ( ! (mask & p->mask))
+                       continue;
+
+               if (p->regex) {
+                       if (regexec(&p->re, cp, 0, NULL, 0))
+                               continue;
+               } else if (NULL == strcasestr(cp, p->v))
+                       continue;
+
+               if (NULL == ms)
+                       return(1);
+               else
+                       ms[p->index] = 1;
+       }
+
+       return(0);
+}
+
+static int
+expreval(const struct expr *p, int *ms)
+{
+       int              match;
+
+       /*
+        * AND has precedence over OR.  Analysis is left-right, though
+        * it doesn't matter because there are no side-effects.
+        * Thus, step through pairwise ANDs and accumulate their Boolean
+        * evaluation.  If we encounter a single true AND collection or
+        * standalone term, the whole expression is true (by definition
+        * of OR).
+        */
+
+       for (match = 0; p && ! match; p = p->next) {
+               /* Evaluate a subexpression, if applicable. */
+               if (p->subexpr && ! ms[p->index])
+                       ms[p->index] = expreval(p->subexpr, ms);
+
+               match = ms[p->index];
+               for ( ; p->next && p->next->and; p = p->next) {
+                       /* Evaluate a subexpression, if applicable. */
+                       if (p->next->subexpr && ! ms[p->next->index])
+                               ms[p->next->index] =
+                                       expreval(p->next->subexpr, ms);
+                       match = match && ms[p->next->index];
+               }
+       }
+
+       return(match);
+}
+
+/*
+ * First, update the array of terms for which this expression evaluates
+ * to true.
+ * Second, logically evaluate all terms over the updated array of truth
+ * values.
+ * If this evaluates to true, mark the expression as satisfied.
+ */
+static void
+exprexec(const struct expr *e, const char *cp,
+               uint64_t mask, struct res *r)
+{
+
+       assert(0 == r->matched);
+       exprmark(e, cp, mask, r->matches);
+       r->matched = expreval(e, r->matches);
+}
diff --git a/contrib/mdocml/apropos_db.h b/contrib/mdocml/apropos_db.h
new file mode 100644 (file)
index 0000000..72d4c20
--- /dev/null
@@ -0,0 +1,73 @@
+/*     $Id: apropos_db.h,v 1.13 2012/03/24 01:46:25 kristaps Exp $ */
+/*
+ * Copyright (c) 2011, 2012 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 APROPOS_H
+#define APROPOS_H
+
+enum   restype {
+       RESTYPE_MAN, /* man(7) file */
+       RESTYPE_MDOC, /* mdoc(7) file */
+       RESTYPE_CAT /* pre-formatted file */
+};
+
+struct res {
+       enum restype     type; /* input file type */
+       char            *file; /* file in file-system */
+       char            *cat; /* category (3p, 3, etc.) */
+       char            *title; /* title (FOO, etc.) */
+       char            *arch; /* arch (or empty string) */
+       char            *desc; /* description (from Nd) */
+       unsigned int     rec; /* record in index */
+       /*
+        * The index volume.  This indexes into the array of directories
+        * searched for manual page databases.
+        */
+       unsigned int     volume;
+       /*
+        * The following fields are used internally.
+        *
+        * Maintain a binary tree for checking the uniqueness of `rec'
+        * when adding elements to the results array.
+        * Since the results array is dynamic, use offset in the array
+        * instead of a pointer to the structure.
+        */
+       int              lhs;
+       int              rhs;
+       int              matched; /* expression is true */
+       int             *matches; /* partial truth evaluations */
+};
+
+struct opts {
+       const char      *arch; /* restrict to architecture */
+       const char      *cat; /* restrict to manual section */
+};
+
+__BEGIN_DECLS
+
+struct expr;
+
+int             apropos_search(int, char **, const struct opts *,
+                       const struct expr *, size_t, 
+                       void *, size_t *, struct res **,
+                       void (*)(struct res *, size_t, void *));
+struct expr    *exprcomp(int, char *[], size_t *);
+void            exprfree(struct expr *);
+void            resfree(struct res *, size_t);
+struct expr    *termcomp(int, char *[], size_t *);
+
+__END_DECLS
+
+#endif /*!APROPOS_H*/
index 41543a9..5113446 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: arch.in,v 1.10 2010/09/27 06:56:44 kristaps Exp $ */
+/*     $Id: arch.in,v 1.12 2012/01/28 14:02:17 joerg Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
  *
  * REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7!
  */
 
+LINE("acorn26",                "Acorn26")
+LINE("acorn32",                "Acorn32")
+LINE("algor",          "Algor")
 LINE("alpha",          "Alpha")
 LINE("amd64",          "AMD64")
 LINE("amiga",          "Amiga")
+LINE("amigappc",       "AmigaPPC")
 LINE("arc",            "ARC")
 LINE("arm",            "ARM")
+LINE("arm26",          "ARM26")
+LINE("arm32",          "ARM32")
 LINE("armish",         "ARMISH")
 LINE("aviion",         "AViiON")
+LINE("atari",          "ATARI")
+LINE("beagle",         "Beagle")
+LINE("bebox",          "BeBox")
+LINE("cats",           "cats")
+LINE("cesfic",         "CESFIC")
+LINE("cobalt",         "Cobalt")
+LINE("dreamcast",      "Dreamcast")
+LINE("emips",          "EMIPS")
+LINE("evbarm",         "evbARM")
+LINE("evbmips",                "evbMIPS")
+LINE("evbppc",         "evbPPC")
+LINE("evbsh3",         "evbSH3")
+LINE("ews4800mips",    "EWS4800MIPS")
 LINE("hp300",          "HP300")
+LINE("hp700",          "HP700")
+LINE("hpcarm",         "HPCARM")
+LINE("hpcmips",                "HPCMIPS")
+LINE("hpcsh",          "HPCSH")
 LINE("hppa",           "HPPA")
 LINE("hppa64",         "HPPA64")
+LINE("ia64",           "ia64")
 LINE("i386",           "i386")
+LINE("ibmnws",         "IBMNWS")
+LINE("iyonix",         "Iyonix")
 LINE("landisk",                "LANDISK")
 LINE("loongson",       "Loongson")
+LINE("luna68k",                "Luna68k")
 LINE("luna88k",                "Luna88k")
+LINE("m68k",           "m68k")
 LINE("mac68k",         "Mac68k")
 LINE("macppc",         "MacPPC")
+LINE("mips",           "MIPS")
 LINE("mips64",         "MIPS64")
+LINE("mipsco",         "MIPSCo")
+LINE("mmeye",          "mmEye")
 LINE("mvme68k",                "MVME68k")
 LINE("mvme88k",                "MVME88k")
 LINE("mvmeppc",                "MVMEPPC")
+LINE("netwinder",      "NetWinder")
+LINE("news68k",                "NeWS68k")
+LINE("newsmips",       "NeWSMIPS")
+LINE("next68k",                "NeXT68k")
+LINE("ofppc",          "OFPPC")
+LINE("palm",           "Palm")
+LINE("pc532",          "PC532")
+LINE("playstation2",   "PlayStation2")
 LINE("pmax",           "PMAX")
+LINE("pmppc",          "pmPPC")
+LINE("powerpc",                "PowerPC")
+LINE("prep",           "PReP")
+LINE("rs6000",         "RS6000")
+LINE("sandpoint",      "Sandpoint")
+LINE("sbmips",         "SBMIPS")
 LINE("sgi",            "SGI")
+LINE("sgimips",                "SGIMIPS")
+LINE("sh3",            "SH3")
+LINE("shark",          "Shark")
 LINE("socppc",         "SOCPPC")
+LINE("solbourne",      "Solbourne")
 LINE("sparc",          "SPARC")
 LINE("sparc64",                "SPARC64")
+LINE("sun2",           "Sun2")
 LINE("sun3",           "Sun3")
+LINE("tahoe",          "Tahoe")
 LINE("vax",            "VAX")
+LINE("x68k",           "X68k")
+LINE("x86",            "x86")
+LINE("x86_64",         "x86_64")
+LINE("xen",            "Xen")
 LINE("zaurus",         "Zaurus")
index 95af2ef..b4ef822 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: att.in,v 1.7 2011/04/24 17:56:44 schwarze Exp $ */
+/*     $Id: att.in,v 1.8 2011/07/31 17:30:33 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -32,6 +32,7 @@ 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("III",            "AT&T System\\~III UNIX")
 LINE("V",              "AT&T System\\~V UNIX")
 LINE("V.1",            "AT&T System\\~V Release\\~1 UNIX")
 LINE("V.2",            "AT&T System\\~V Release\\~2 UNIX")
diff --git a/contrib/mdocml/catman.8 b/contrib/mdocml/catman.8
new file mode 100644 (file)
index 0000000..f5246f9
--- /dev/null
@@ -0,0 +1,111 @@
+.\"    $Id: catman.8,v 1.5 2011/12/25 19:35:44 kristaps Exp $
+.\"
+.\" Copyright (c) 2011 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: December 25 2011 $
+.Dt CATMAN 8
+.Os
+.Sh NAME
+.Nm catman
+.Nd update a man.cgi manpage cache
+.Sh SYNOPSIS
+.Nm catman
+.Op Fl fv
+.Op Fl C Ar file
+.Op Fl M Ar manpath
+.Op Fl m Ar manpath
+.Op Fl o Ar path
+.Sh DESCRIPTION
+The
+.Nm
+utility updates cached manpages for a jailed
+.Xr man.cgi 7 .
+.Pp
+By default,
+.Nm
+searches for
+.Xr mandocdb 8
+databases in the default paths stipulated by
+.Xr man 1
+and updates the cache in
+.Pa /var/www/cache/man.cgi .
+.Pp
+Its arguments are as follows:
+.Bl -tag -width Ds
+.It Fl f
+Force an update to all files.
+.It Fl v
+Print each file being updated.
+.It Fl C Ar file
+Specify an alternative configuration
+.Ar file
+in
+.Xr man.conf 5
+format.
+.It Fl M Ar manpath
+Use the colon-separated path instead of the default list of paths
+searched for
+.Xr mandocdb 8
+databases.
+Invalid paths, or paths without manual databases, are ignored.
+.It Fl m Ar manpath
+Prepend the colon-separated paths to the list of paths searched
+for
+.Xr mandocdb 8
+databases.
+Invalid paths, or paths without manual databases, are ignored.
+.It Fl o Ar path
+Update into the directory tree under
+.Ar path .
+.El
+.Pp
+Cache updates occur when a
+.Xr mandocdb 8
+database is older than the cached copy unless
+.Fl f
+is specified, in which case files are always considered out of date.
+Cached manual pages are only updated if older than the master copy.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev MANPATH
+Colon-separated paths modifying the default list of paths searched for
+manual databases.
+Invalid paths, or paths without manual databases, are ignored.
+Overridden by
+.Fl M .
+If
+.Ev MANPATH
+begins with a
+.Sq \&: ,
+it is appended to the default list;
+else if it ends with
+.Sq \&: ,
+it is prepended to the default list; else if it contains
+.Sq \&:: ,
+the default list is inserted between the colons.
+If none of these conditions are met, it overrides the default list.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr mandoc 1 ,
+.Xr man.cgi 7 ,
+.Xr mandocdb 8
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Kristaps Dzonsons ,
+.Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/catman.c b/contrib/mdocml/catman.c
new file mode 100644 (file)
index 0000000..1d313ea
--- /dev/null
@@ -0,0 +1,511 @@
+/*     $Id: catman.c,v 1.10 2012/01/03 15:17:20 kristaps Exp $ */
+/*
+ * Copyright (c) 2011 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/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __linux__
+# include <db_185.h>
+#else
+# include <db.h>
+#endif
+
+#include "manpath.h"
+#include "mandocdb.h"
+
+#define        xstrlcpy(_dst, _src, _sz) \
+       do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
+               fprintf(stderr, "%s: Path too long", (_dst)); \
+               exit(EXIT_FAILURE); \
+       } while (/* CONSTCOND */0)
+
+#define        xstrlcat(_dst, _src, _sz) \
+       do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
+               fprintf(stderr, "%s: Path too long", (_dst)); \
+               exit(EXIT_FAILURE); \
+       } while (/* CONSTCOND */0)
+
+static int              indexhtml(char *, size_t, char *, size_t);
+static int              manup(const struct manpaths *, char *);
+static int              mkpath(char *, mode_t, mode_t);
+static int              treecpy(char *, char *);
+static int              update(char *, char *);
+static void             usage(void);
+
+static const char      *progname;
+static int              verbose;
+static int              force;
+
+int
+main(int argc, char *argv[])
+{
+       int              ch;
+       char            *aux, *base, *conf_file;
+       struct manpaths  dirs;
+       char             buf[MAXPATHLEN];
+       extern char     *optarg;
+       extern int       optind;
+
+       progname = strrchr(argv[0], '/');
+       if (progname == NULL)
+               progname = argv[0];
+       else
+               ++progname;
+
+       aux = base = conf_file = NULL;
+       xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);
+
+       while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))
+               switch (ch) {
+               case ('C'):
+                       conf_file = optarg;
+                       break;
+               case ('f'):
+                       force = 1;
+                       break;
+               case ('m'):
+                       aux = optarg;
+                       break;
+               case ('M'):
+                       base = optarg;
+                       break;
+               case ('o'):
+                       xstrlcpy(buf, optarg, MAXPATHLEN);
+                       break;
+               case ('v'):
+                       verbose++;
+                       break;
+               default:
+                       usage();
+                       return(EXIT_FAILURE);
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc > 0) {
+               usage();
+               return(EXIT_FAILURE);
+       }
+
+       memset(&dirs, 0, sizeof(struct manpaths));
+       manpath_parse(&dirs, conf_file, base, aux);
+       ch = manup(&dirs, buf);
+       manpath_free(&dirs);
+       return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void
+usage(void)
+{
+       
+       fprintf(stderr, "usage: %s "
+                       "[-fv] "
+                       "[-C file] "
+                       "[-o path] "
+                       "[-m manpath] "
+                       "[-M manpath]\n",
+                       progname);
+}
+
+/*
+ * If "src" file doesn't exist (errors out), return -1.  Otherwise,
+ * return 1 if "src" is newer (which also happens "dst" doesn't exist)
+ * and 0 otherwise.
+ */
+static int
+isnewer(const char *dst, const char *src)
+{
+       struct stat      s1, s2;
+
+       if (-1 == stat(src, &s1))
+               return(-1);
+       if (force)
+               return(1);
+
+       return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
+}
+
+/*
+ * Copy the contents of one file into another.
+ * Returns 0 on failure, 1 on success.
+ */
+static int
+filecpy(const char *dst, const char *src)
+{
+       char             buf[BUFSIZ];
+       int              sfd, dfd, rc;
+       ssize_t          rsz, wsz;
+
+       sfd = dfd = -1;
+       rc = 0;
+
+       if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
+               perror(dst);
+               goto out;
+       } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
+               perror(src);
+               goto out;
+       } 
+
+       while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
+               if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
+                       perror(dst);
+                       goto out;
+               } else if (wsz < rsz) {
+                       fprintf(stderr, "%s: Short write\n", dst);
+                       goto out;
+               }
+       
+       if (rsz < 0)
+               perror(src);
+       else
+               rc = 1;
+out:
+       if (-1 != sfd)
+               close(sfd);
+       if (-1 != dfd)
+               close(dfd);
+
+       return(rc);
+}
+
+/*
+ * Pass over the recno database and re-create HTML pages if they're
+ * found to be out of date.
+ * Returns -1 on fatal error, 1 on success.
+ */
+static int
+indexhtml(char *src, size_t ssz, char *dst, size_t dsz)
+{
+       DB              *idx;
+       DBT              key, val;
+       int              c, rc;
+       unsigned int     fl;
+       const char      *f;
+       char            *d;
+       char             fname[MAXPATHLEN];
+       pid_t            pid;
+
+       pid = -1;
+
+       xstrlcpy(fname, dst, MAXPATHLEN);
+       xstrlcat(fname, "/", MAXPATHLEN);
+       xstrlcat(fname, MANDOC_IDX, MAXPATHLEN);
+
+       idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
+       if (NULL == idx) {
+               perror(fname);
+               return(-1);
+       }
+
+       fl = R_FIRST;
+       while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {
+               fl = R_NEXT;
+               /*
+                * If the record is zero-length, then it's unassigned.
+                * Skip past these.
+                */
+               if (0 == val.size)
+                       continue;
+
+               f = (const char *)val.data + 1;
+               if (NULL == memchr(f, '\0', val.size - 1))
+                       break;
+
+               src[(int)ssz] = dst[(int)dsz] = '\0';
+
+               xstrlcat(dst, "/", MAXPATHLEN);
+               xstrlcat(dst, f, MAXPATHLEN);
+
+               xstrlcat(src, "/", MAXPATHLEN);
+               xstrlcat(src, f, MAXPATHLEN);
+
+               if (-1 == (rc = isnewer(dst, src))) {
+                       fprintf(stderr, "%s: File missing\n", f);
+                       break;
+               } else if (0 == rc)
+                       continue;
+
+               d = strrchr(dst, '/');
+               assert(NULL != d);
+               *d = '\0';
+
+               if (-1 == mkpath(dst, 0755, 0755)) {
+                       perror(dst);
+                       break;
+               }
+
+               *d = '/';
+
+               if ( ! filecpy(dst, src))
+                       break;
+               if (verbose)
+                       printf("%s\n", dst);
+       }
+
+       (*idx->close)(idx);
+
+       if (c < 0)
+               perror(fname);
+       else if (0 == c) 
+               fprintf(stderr, "%s: Corrupt index\n", fname);
+
+       return(1 == c ? 1 : -1);
+}
+
+/*
+ * Copy both recno and btree databases into the destination.
+ * Call in to begin recreating HTML files.
+ * Return -1 on fatal error and 1 if the update went well.
+ */
+static int
+update(char *dst, char *src)
+{
+       size_t           dsz, ssz;
+
+       dsz = strlen(dst);
+       ssz = strlen(src);
+
+       xstrlcat(src, "/", MAXPATHLEN);
+       xstrlcat(dst, "/", MAXPATHLEN);
+
+       xstrlcat(src, MANDOC_DB, MAXPATHLEN);
+       xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
+
+       if ( ! filecpy(dst, src))
+               return(-1);
+       if (verbose)
+               printf("%s\n", dst);
+
+       dst[(int)dsz] = src[(int)ssz] = '\0';
+
+       xstrlcat(src, "/", MAXPATHLEN);
+       xstrlcat(dst, "/", MAXPATHLEN);
+
+       xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
+       xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
+
+       if ( ! filecpy(dst, src))
+               return(-1);
+       if (verbose)
+               printf("%s\n", dst);
+
+       dst[(int)dsz] = src[(int)ssz] = '\0';
+
+       return(indexhtml(src, ssz, dst, dsz));
+}
+
+/*
+ * See if btree or recno databases in the destination are out of date
+ * with respect to a single manpath component.
+ * Return -1 on fatal error, 0 if the source is no longer valid (and
+ * shouldn't be listed), and 1 if the update went well.
+ */
+static int
+treecpy(char *dst, char *src)
+{
+       size_t           dsz, ssz;
+       int              rc;
+
+       dsz = strlen(dst);
+       ssz = strlen(src);
+
+       xstrlcat(src, "/", MAXPATHLEN);
+       xstrlcat(dst, "/", MAXPATHLEN);
+
+       xstrlcat(src, MANDOC_IDX, MAXPATHLEN);
+       xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);
+
+       if (-1 == (rc = isnewer(dst, src)))
+               return(0);
+
+       dst[(int)dsz] = src[(int)ssz] = '\0';
+
+       if (1 == rc)
+               return(update(dst, src));
+
+       xstrlcat(src, "/", MAXPATHLEN);
+       xstrlcat(dst, "/", MAXPATHLEN);
+
+       xstrlcat(src, MANDOC_DB, MAXPATHLEN);
+       xstrlcat(dst, MANDOC_DB, MAXPATHLEN);
+
+       if (-1 == (rc = isnewer(dst, src)))
+               return(0);
+       else if (rc == 0)
+               return(1);
+
+       dst[(int)dsz] = src[(int)ssz] = '\0';
+
+       return(update(dst, src));
+}
+
+/*
+ * Update the destination's file-tree with respect to changes in the
+ * source manpath components.
+ * "Change" is defined by an updated index or btree database.
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+manup(const struct manpaths *dirs, char *base)
+{
+       char             dst[MAXPATHLEN],
+                        src[MAXPATHLEN];
+       const char      *path;
+       int              i, c;
+       size_t           sz;
+       FILE            *f;
+
+       /* Create the path and file for the catman.conf file. */
+
+       sz = strlen(base);
+       xstrlcpy(dst, base, MAXPATHLEN);
+       xstrlcat(dst, "/etc", MAXPATHLEN);
+       if (-1 == mkpath(dst, 0755, 0755)) {
+               perror(dst);
+               return(0);
+       }
+
+       xstrlcat(dst, "/catman.conf", MAXPATHLEN);
+       if (NULL == (f = fopen(dst, "w"))) {
+               perror(dst);
+               return(0);
+       } else if (verbose)
+               printf("%s\n", dst);
+
+       for (i = 0; i < dirs->sz; i++) {
+               path = dirs->paths[i];
+               dst[(int)sz] = '\0';
+               xstrlcat(dst, path, MAXPATHLEN);
+               if (-1 == mkpath(dst, 0755, 0755)) {
+                       perror(dst);
+                       break;
+               }
+
+               xstrlcpy(src, path, MAXPATHLEN);
+               if (-1 == (c = treecpy(dst, src)))
+                       break;
+               else if (0 == c)
+                       continue;
+
+               /*
+                * We want to use a relative path here because manpath.h
+                * will realpath() when invoked with man.cgi, and we'll
+                * make sure to chdir() into the cache directory before.
+                *
+                * This allows the cache directory to be in an arbitrary
+                * place, working in both chroot() and non-chroot()
+                * "safe" modes.
+                */
+               assert('/' == path[0]);
+               fprintf(f, "_whatdb %s/whatis.db\n", path + 1);
+       }
+
+       fclose(f);
+       return(i == dirs->sz);
+}
+
+/*
+ * Copyright (c) 1983, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+static int
+mkpath(char *path, mode_t mode, mode_t dir_mode)
+{
+       struct stat sb;
+       char *slash;
+       int done, exists;
+
+       slash = path;
+
+       for (;;) {
+               /* LINTED */
+               slash += strspn(slash, "/");
+               /* LINTED */
+               slash += strcspn(slash, "/");
+
+               done = (*slash == '\0');
+               *slash = '\0';
+
+               /* skip existing path components */
+               exists = !stat(path, &sb);
+               if (!done && exists && S_ISDIR(sb.st_mode)) {
+                       *slash = '/';
+                       continue;
+               }
+
+               if (mkdir(path, done ? mode : dir_mode) == 0) {
+                       if (mode > 0777 && chmod(path, mode) < 0)
+                               return (-1);
+               } else {
+                       if (!exists) {
+                               /* Not there */
+                               return (-1);
+                       }
+                       if (!S_ISDIR(sb.st_mode)) {
+                               /* Is there, but isn't a directory */
+                               errno = ENOTDIR;
+                               return (-1);
+                       }
+               }
+
+               if (done)
+                       break;
+
+               *slash = '/';
+       }
+
+       return (0);
+}
diff --git a/contrib/mdocml/cgi.c b/contrib/mdocml/cgi.c
new file mode 100644 (file)
index 0000000..2f5870f
--- /dev/null
@@ -0,0 +1,1203 @@
+/*     $Id: cgi.c,v 1.42 2012/03/24 01:46:25 kristaps Exp $ */
+/*
+ * Copyright (c) 2011, 2012 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/param.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "apropos_db.h"
+#include "mandoc.h"
+#include "mdoc.h"
+#include "man.h"
+#include "main.h"
+#include "manpath.h"
+#include "mandocdb.h"
+
+#ifdef __linux__
+# include <db_185.h>
+#else
+# include <db.h>
+#endif
+
+enum   page {
+       PAGE_INDEX,
+       PAGE_SEARCH,
+       PAGE_SHOW,
+       PAGE__MAX
+};
+
+struct paths {
+       char            *name;
+       char            *path;
+};
+
+/*
+ * A query as passed to the search function.
+ */
+struct query {
+       const char      *arch; /* architecture */
+       const char      *sec; /* manual section */
+       const char      *expr; /* unparsed expression string */
+       int              manroot; /* manroot index (or -1)*/
+       int              legacy; /* whether legacy mode */
+};
+
+struct req {
+       struct query     q;
+       struct paths    *p;
+       size_t           psz;
+       enum page        page;
+};
+
+static int              atou(const char *, unsigned *);
+static void             catman(const struct req *, const char *);
+static int              cmp(const void *, const void *);
+static void             format(const struct req *, const char *);
+static void             html_print(const char *);
+static void             html_printquery(const struct req *);
+static void             html_putchar(char);
+static int              http_decode(char *);
+static void             http_parse(struct req *, char *);
+static void             http_print(const char *);
+static void             http_putchar(char);
+static void             http_printquery(const struct req *);
+static int              pathstop(DIR *);
+static void             pathgen(DIR *, char *, struct req *);
+static void             pg_index(const struct req *, char *);
+static void             pg_search(const struct req *, char *);
+static void             pg_show(const struct req *, char *);
+static void             resp_bad(void);
+static void             resp_baddb(void);
+static void             resp_error400(void);
+static void             resp_error404(const char *);
+static void             resp_begin_html(int, const char *);
+static void             resp_begin_http(int, const char *);
+static void             resp_end_html(void);
+static void             resp_index(const struct req *);
+static void             resp_search(struct res *, size_t, void *);
+static void             resp_searchform(const struct req *);
+
+static const char       *progname; /* cgi script name */
+static const char       *cache; /* cache directory */
+static const char       *css; /* css directory */
+static const char       *host; /* hostname */
+
+static const char * const pages[PAGE__MAX] = {
+       "index", /* PAGE_INDEX */ 
+       "search", /* PAGE_SEARCH */
+       "show", /* PAGE_SHOW */
+};
+
+/*
+ * This is just OpenBSD's strtol(3) suggestion.
+ * I use it instead of strtonum(3) for portability's sake.
+ */
+static int
+atou(const char *buf, unsigned *v)
+{
+       char            *ep;
+       long             lval;
+
+       errno = 0;
+       lval = strtol(buf, &ep, 10);
+       if (buf[0] == '\0' || *ep != '\0')
+               return(0);
+       if ((errno == ERANGE && (lval == LONG_MAX || 
+                                       lval == LONG_MIN)) ||
+                       (lval > INT_MAX || lval < 0))
+               return(0);
+
+       *v = (unsigned int)lval;
+       return(1);
+}
+
+/*
+ * Print a character, escaping HTML along the way.
+ * This will pass non-ASCII straight to output: be warned!
+ */
+static void
+html_putchar(char c)
+{
+
+       switch (c) {
+       case ('"'):
+               printf("&quote;");
+               break;
+       case ('&'):
+               printf("&amp;");
+               break;
+       case ('>'):
+               printf("&gt;");
+               break;
+       case ('<'):
+               printf("&lt;");
+               break;
+       default:
+               putchar((unsigned char)c);
+               break;
+       }
+}
+static void
+http_printquery(const struct req *req)
+{
+
+       printf("&expr=");
+       http_print(req->q.expr ? req->q.expr : "");
+       printf("&sec=");
+       http_print(req->q.sec ? req->q.sec : "");
+       printf("&arch=");
+       http_print(req->q.arch ? req->q.arch : "");
+}
+
+
+static void
+html_printquery(const struct req *req)
+{
+
+       printf("&amp;expr=");
+       html_print(req->q.expr ? req->q.expr : "");
+       printf("&amp;sec=");
+       html_print(req->q.sec ? req->q.sec : "");
+       printf("&amp;arch=");
+       html_print(req->q.arch ? req->q.arch : "");
+}
+
+static void
+http_print(const char *p)
+{
+
+       if (NULL == p)
+               return;
+       while ('\0' != *p)
+               http_putchar(*p++);
+}
+
+/*
+ * Call through to html_putchar().
+ * Accepts NULL strings.
+ */
+static void
+html_print(const char *p)
+{
+       
+       if (NULL == p)
+               return;
+       while ('\0' != *p)
+               html_putchar(*p++);
+}
+
+/*
+ * Parse out key-value pairs from an HTTP request variable.
+ * This can be either a cookie or a POST/GET string, although man.cgi
+ * uses only GET for simplicity.
+ */
+static void
+http_parse(struct req *req, char *p)
+{
+       char            *key, *val, *manroot;
+       int              i, legacy;
+
+       memset(&req->q, 0, sizeof(struct query));
+
+       legacy = -1;
+       manroot = NULL;
+
+       while ('\0' != *p) {
+               key = p;
+               val = NULL;
+
+               p += (int)strcspn(p, ";&");
+               if ('\0' != *p)
+                       *p++ = '\0';
+               if (NULL != (val = strchr(key, '=')))
+                       *val++ = '\0';
+
+               if ('\0' == *key || NULL == val || '\0' == *val)
+                       continue;
+
+               /* Just abort handling. */
+
+               if ( ! http_decode(key))
+                       break;
+               if (NULL != val && ! http_decode(val))
+                       break;
+
+               if (0 == strcmp(key, "expr"))
+                       req->q.expr = val;
+               else if (0 == strcmp(key, "query"))
+                       req->q.expr = val;
+               else if (0 == strcmp(key, "sec"))
+                       req->q.sec = val;
+               else if (0 == strcmp(key, "sektion"))
+                       req->q.sec = val;
+               else if (0 == strcmp(key, "arch"))
+                       req->q.arch = val;
+               else if (0 == strcmp(key, "manpath"))
+                       manroot = val;
+               else if (0 == strcmp(key, "apropos"))
+                       legacy = 0 == strcmp(val, "0");
+       }
+
+       /* Test for old man.cgi compatibility mode. */
+
+       req->q.legacy = legacy > 0;
+
+       /* 
+        * Section "0" means no section when in legacy mode.
+        * For some man.cgi scripts, "default" arch is none.
+        */
+
+       if (req->q.legacy && NULL != req->q.sec)
+               if (0 == strcmp(req->q.sec, "0"))
+                       req->q.sec = NULL;
+       if (req->q.legacy && NULL != req->q.arch)
+               if (0 == strcmp(req->q.arch, "default"))
+                       req->q.arch = NULL;
+
+       /* Default to first manroot. */
+
+       if (NULL != manroot) {
+               for (i = 0; i < (int)req->psz; i++)
+                       if (0 == strcmp(req->p[i].name, manroot))
+                               break;
+               req->q.manroot = i < (int)req->psz ? i : -1;
+       }
+}
+
+static void
+http_putchar(char c)
+{
+
+       if (isalnum((unsigned char)c)) {
+               putchar((unsigned char)c);
+               return;
+       } else if (' ' == c) {
+               putchar('+');
+               return;
+       }
+       printf("%%%.2x", c);
+}
+
+/*
+ * HTTP-decode a string.  The standard explanation is that this turns
+ * "%4e+foo" into "n foo" in the regular way.  This is done in-place
+ * over the allocated string.
+ */
+static int
+http_decode(char *p)
+{
+       char             hex[3];
+       int              c;
+
+       hex[2] = '\0';
+
+       for ( ; '\0' != *p; p++) {
+               if ('%' == *p) {
+                       if ('\0' == (hex[0] = *(p + 1)))
+                               return(0);
+                       if ('\0' == (hex[1] = *(p + 2)))
+                               return(0);
+                       if (1 != sscanf(hex, "%x", &c))
+                               return(0);
+                       if ('\0' == c)
+                               return(0);
+
+                       *p = (char)c;
+                       memmove(p + 1, p + 3, strlen(p + 3) + 1);
+               } else
+                       *p = '+' == *p ? ' ' : *p;
+       }
+
+       *p = '\0';
+       return(1);
+}
+
+static void
+resp_begin_http(int code, const char *msg)
+{
+
+       if (200 != code)
+               printf("Status: %d %s\n", code, msg);
+
+       puts("Content-Type: text/html; charset=utf-8\n"
+            "Cache-Control: no-cache\n"
+            "Pragma: no-cache\n"
+            "");
+
+       fflush(stdout);
+}
+
+static void
+resp_begin_html(int code, const char *msg)
+{
+
+       resp_begin_http(code, msg);
+
+       printf("<!DOCTYPE HTML PUBLIC "
+              " \"-//W3C//DTD HTML 4.01//EN\""
+              " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
+              "<HTML>\n"
+              "<HEAD>\n"
+              "<META HTTP-EQUIV=\"Content-Type\""
+              " CONTENT=\"text/html; charset=utf-8\">\n"
+              "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\""
+              " TYPE=\"text/css\" media=\"all\">\n"
+              "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\""
+              " TYPE=\"text/css\" media=\"all\">\n"
+              "<TITLE>System Manpage Reference</TITLE>\n"
+              "</HEAD>\n"
+              "<BODY>\n"
+              "<!-- Begin page content. //-->\n", css, css);
+}
+
+static void
+resp_end_html(void)
+{
+
+       puts("</BODY>\n"
+            "</HTML>");
+}
+
+static void
+resp_searchform(const struct req *req)
+{
+       int              i;
+
+       puts("<!-- Begin search form. //-->");
+       printf("<DIV ID=\"mancgi\">\n"
+              "<FORM ACTION=\"%s/search.html\" METHOD=\"get\">\n"
+              "<FIELDSET>\n"
+              "<LEGEND>Search Parameters</LEGEND>\n"
+              "<INPUT TYPE=\"submit\" "
+              " VALUE=\"Search\"> for manuals satisfying \n"
+              "<INPUT TYPE=\"text\" NAME=\"expr\" VALUE=\"",
+              progname);
+       html_print(req->q.expr ? req->q.expr : "");
+       printf("\">, section "
+              "<INPUT TYPE=\"text\""
+              " SIZE=\"4\" NAME=\"sec\" VALUE=\"");
+       html_print(req->q.sec ? req->q.sec : "");
+       printf("\">, arch "
+              "<INPUT TYPE=\"text\""
+              " SIZE=\"8\" NAME=\"arch\" VALUE=\"");
+       html_print(req->q.arch ? req->q.arch : "");
+       printf("\">");
+       if (req->psz > 1) {
+               puts(", <SELECT NAME=\"manpath\">");
+               for (i = 0; i < (int)req->psz; i++) {
+                       printf("<OPTION %s VALUE=\"",
+                               (i == req->q.manroot) ||
+                               (0 == i && -1 == req->q.manroot) ?
+                               "SELECTED=\"selected\"" : "");
+                       html_print(req->p[i].name);
+                       printf("\">");
+                       html_print(req->p[i].name);
+                       puts("</OPTION>");
+               }
+               puts("</SELECT>");
+       }
+       puts(".\n"
+            "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"
+            "</FIELDSET>\n"
+            "</FORM>\n"
+            "</DIV>");
+       puts("<!-- End search form. //-->");
+}
+
+static void
+resp_index(const struct req *req)
+{
+
+       resp_begin_html(200, NULL);
+       resp_searchform(req);
+       resp_end_html();
+}
+
+static void
+resp_error400(void)
+{
+
+       resp_begin_html(400, "Query Malformed");
+       printf("<H1>Malformed Query</H1>\n"
+              "<P>\n"
+              "The query your entered was malformed.\n"
+              "Try again from the\n"
+              "<A HREF=\"%s/index.html\">main page</A>.\n"
+              "</P>", progname);
+       resp_end_html();
+}
+
+static void
+resp_error404(const char *page)
+{
+
+       resp_begin_html(404, "Not Found");
+       puts("<H1>Page Not Found</H1>\n"
+            "<P>\n"
+            "The page you're looking for, ");
+       printf("<B>");
+       html_print(page);
+       printf("</B>,\n"
+              "could not be found.\n"
+              "Try searching from the\n"
+              "<A HREF=\"%s/index.html\">main page</A>.\n"
+              "</P>", progname);
+       resp_end_html();
+}
+
+static void
+resp_bad(void)
+{
+       resp_begin_html(500, "Internal Server Error");
+       puts("<P>Generic badness happened.</P>");
+       resp_end_html();
+}
+
+static void
+resp_baddb(void)
+{
+
+       resp_begin_html(500, "Internal Server Error");
+       puts("<P>Your database is broken.</P>");
+       resp_end_html();
+}
+
+static void
+resp_search(struct res *r, size_t sz, void *arg)
+{
+       size_t           i, matched;
+       const struct req *req;
+
+       req = (const struct req *)arg;
+
+       if (sz > 0)
+               assert(req->q.manroot >= 0);
+
+       for (matched = i = 0; i < sz; i++)
+               if (r[i].matched)
+                       matched++;
+       
+       if (1 == matched) {
+               for (i = 0; i < sz; i++)
+                       if (r[i].matched)
+                               break;
+               /*
+                * If we have just one result, then jump there now
+                * without any delay.
+                */
+               puts("Status: 303 See Other");
+               printf("Location: http://%s%s/show/%d/%u/%u.html?",
+                               host, progname, req->q.manroot,
+                               r[i].volume, r[i].rec);
+               http_printquery(req);
+               puts("\n"
+                    "Content-Type: text/html; charset=utf-8\n");
+               return;
+       }
+
+       resp_begin_html(200, NULL);
+       resp_searchform(req);
+
+       puts("<DIV CLASS=\"results\">");
+
+       if (0 == matched) {
+               puts("<P>\n"
+                    "No results found.\n"
+                    "</P>\n"
+                    "</DIV>");
+               resp_end_html();
+               return;
+       }
+
+       qsort(r, sz, sizeof(struct res), cmp);
+
+       puts("<TABLE>");
+
+       for (i = 0; i < sz; i++) {
+               if ( ! r[i].matched)
+                       continue;
+               printf("<TR>\n"
+                      "<TD CLASS=\"title\">\n"
+                      "<A HREF=\"%s/show/%d/%u/%u.html?", 
+                               progname, req->q.manroot,
+                               r[i].volume, r[i].rec);
+               html_printquery(req);
+               printf("\">");
+               html_print(r[i].title);
+               putchar('(');
+               html_print(r[i].cat);
+               if (r[i].arch && '\0' != *r[i].arch) {
+                       putchar('/');
+                       html_print(r[i].arch);
+               }
+               printf(")</A>\n"
+                      "</TD>\n"
+                      "<TD CLASS=\"desc\">");
+               html_print(r[i].desc);
+               puts("</TD>\n"
+                    "</TR>");
+       }
+
+       puts("</TABLE>\n"
+            "</DIV>");
+       resp_end_html();
+}
+
+/* ARGSUSED */
+static void
+pg_index(const struct req *req, char *path)
+{
+
+       resp_index(req);
+}
+
+static void
+catman(const struct req *req, const char *file)
+{
+       FILE            *f;
+       size_t           len;
+       int              i;
+       char            *p;
+       int              italic, bold;
+
+       if (NULL == (f = fopen(file, "r"))) {
+               resp_baddb();
+               return;
+       }
+
+       resp_begin_html(200, NULL);
+       resp_searchform(req);
+       puts("<DIV CLASS=\"catman\">\n"
+            "<PRE>");
+
+       while (NULL != (p = fgetln(f, &len))) {
+               bold = italic = 0;
+               for (i = 0; i < (int)len - 1; i++) {
+                       /* 
+                        * This means that the catpage is out of state.
+                        * Ignore it and keep going (although the
+                        * catpage is bogus).
+                        */
+
+                       if ('\b' == p[i] || '\n' == p[i])
+                               continue;
+
+                       /*
+                        * Print a regular character.
+                        * Close out any bold/italic scopes.
+                        * If we're in back-space mode, make sure we'll
+                        * have something to enter when we backspace.
+                        */
+
+                       if ('\b' != p[i + 1]) {
+                               if (italic)
+                                       printf("</I>");
+                               if (bold)
+                                       printf("</B>");
+                               italic = bold = 0;
+                               html_putchar(p[i]);
+                               continue;
+                       } else if (i + 2 >= (int)len)
+                               continue;
+
+                       /* Italic mode. */
+
+                       if ('_' == p[i]) {
+                               if (bold)
+                                       printf("</B>");
+                               if ( ! italic)
+                                       printf("<I>");
+                               bold = 0;
+                               italic = 1;
+                               i += 2;
+                               html_putchar(p[i]);
+                               continue;
+                       }
+
+                       /* 
+                        * Handle funny behaviour troff-isms.
+                        * These grok'd from the original man2html.c.
+                        */
+
+                       if (('+' == p[i] && 'o' == p[i + 2]) ||
+                                       ('o' == p[i] && '+' == p[i + 2]) ||
+                                       ('|' == p[i] && '=' == p[i + 2]) ||
+                                       ('=' == p[i] && '|' == p[i + 2]) ||
+                                       ('*' == p[i] && '=' == p[i + 2]) ||
+                                       ('=' == p[i] && '*' == p[i + 2]) ||
+                                       ('*' == p[i] && '|' == p[i + 2]) ||
+                                       ('|' == p[i] && '*' == p[i + 2]))  {
+                               if (italic)
+                                       printf("</I>");
+                               if (bold)
+                                       printf("</B>");
+                               italic = bold = 0;
+                               putchar('*');
+                               i += 2;
+                               continue;
+                       } else if (('|' == p[i] && '-' == p[i + 2]) ||
+                                       ('-' == p[i] && '|' == p[i + 1]) ||
+                                       ('+' == p[i] && '-' == p[i + 1]) ||
+                                       ('-' == p[i] && '+' == p[i + 1]) ||
+                                       ('+' == p[i] && '|' == p[i + 1]) ||
+                                       ('|' == p[i] && '+' == p[i + 1]))  {
+                               if (italic)
+                                       printf("</I>");
+                               if (bold)
+                                       printf("</B>");
+                               italic = bold = 0;
+                               putchar('+');
+                               i += 2;
+                               continue;
+                       }
+
+                       /* Bold mode. */
+                       
+                       if (italic)
+                               printf("</I>");
+                       if ( ! bold)
+                               printf("<B>");
+                       bold = 1;
+                       italic = 0;
+                       i += 2;
+                       html_putchar(p[i]);
+               }
+
+               /* 
+                * Clean up the last character.
+                * We can get to a newline; don't print that. 
+                */
+
+               if (italic)
+                       printf("</I>");
+               if (bold)
+                       printf("</B>");
+
+               if (i == (int)len - 1 && '\n' != p[i])
+                       html_putchar(p[i]);
+
+               putchar('\n');
+       }
+
+       puts("</PRE>\n"
+            "</DIV>\n"
+            "</BODY>\n"
+            "</HTML>");
+
+       fclose(f);
+}
+
+static void
+format(const struct req *req, const char *file)
+{
+       struct mparse   *mp;
+       int              fd;
+       struct mdoc     *mdoc;
+       struct man      *man;
+       void            *vp;
+       enum mandoclevel rc;
+       char             opts[MAXPATHLEN + 128];
+
+       if (-1 == (fd = open(file, O_RDONLY, 0))) {
+               resp_baddb();
+               return;
+       }
+
+       mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
+       rc = mparse_readfd(mp, fd, file);
+       close(fd);
+
+       if (rc >= MANDOCLEVEL_FATAL) {
+               resp_baddb();
+               return;
+       }
+
+       snprintf(opts, sizeof(opts), "fragment,"
+                       "man=%s/search.html?sec=%%S&expr=%%N,"
+                       /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/,
+                       progname);
+
+       mparse_result(mp, &mdoc, &man);
+       if (NULL == man && NULL == mdoc) {
+               resp_baddb();
+               mparse_free(mp);
+               return;
+       }
+
+       resp_begin_html(200, NULL);
+       resp_searchform(req);
+
+       vp = html_alloc(opts);
+
+       if (NULL != mdoc)
+               html_mdoc(vp, mdoc);
+       else
+               html_man(vp, man);
+
+       puts("</BODY>\n"
+            "</HTML>");
+
+       html_free(vp);
+       mparse_free(mp);
+}
+
+static void
+pg_show(const struct req *req, char *path)
+{
+       struct manpaths  ps;
+       size_t           sz;
+       char            *sub;
+       char             file[MAXPATHLEN];
+       const char      *cp;
+       int              rc, catm;
+       unsigned int     vol, rec, mr;
+       DB              *idx;
+       DBT              key, val;
+
+       idx = NULL;
+
+       /* Parse out mroot, volume, and record from the path. */
+
+       if (NULL == path || NULL == (sub = strchr(path, '/'))) {
+               resp_error400();
+               return;
+       } 
+       *sub++ = '\0';
+       if ( ! atou(path, &mr)) {
+               resp_error400();
+               return;
+       }
+       path = sub;
+       if (NULL == (sub = strchr(path, '/'))) {
+               resp_error400();
+               return;
+       }
+       *sub++ = '\0';
+       if ( ! atou(path, &vol) || ! atou(sub, &rec)) {
+               resp_error400();
+               return;
+       } else if (mr >= (unsigned int)req->psz) {
+               resp_error400();
+               return;
+       }
+
+       /*
+        * Begin by chdir()ing into the manroot.
+        * This way we can pick up the database files, which are
+        * relative to the manpath root.
+        */
+
+       if (-1 == chdir(req->p[(int)mr].path)) {
+               perror(req->p[(int)mr].path);
+               resp_baddb();
+               return;
+       }
+
+       memset(&ps, 0, sizeof(struct manpaths));
+       manpath_manconf(&ps, "etc/catman.conf");
+
+       if (vol >= (unsigned int)ps.sz) {
+               resp_error400();
+               goto out;
+       }
+
+       sz = strlcpy(file, ps.paths[vol], MAXPATHLEN);
+       assert(sz < MAXPATHLEN);
+       strlcat(file, "/", MAXPATHLEN);
+       strlcat(file, MANDOC_IDX, MAXPATHLEN);
+
+       /* Open the index recno(3) database. */
+
+       idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL);
+       if (NULL == idx) {
+               perror(file);
+               resp_baddb();
+               goto out;
+       }
+
+       key.data = &rec;
+       key.size = 4;
+
+       if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) {
+               rc < 0 ? resp_baddb() : resp_error400();
+               goto out;
+       } else if (0 == val.size) {
+               resp_baddb();
+               goto out;
+       }
+
+       cp = (char *)val.data;
+       catm = 'c' == *cp++;
+
+       if (NULL == memchr(cp, '\0', val.size - 1)) 
+               resp_baddb();
+       else {
+               file[(int)sz] = '\0';
+               strlcat(file, "/", MAXPATHLEN);
+               strlcat(file, cp, MAXPATHLEN);
+               if (catm) 
+                       catman(req, file);
+               else
+                       format(req, file);
+       }
+out:
+       if (idx)
+               (*idx->close)(idx);
+       manpath_free(&ps);
+}
+
+static void
+pg_search(const struct req *req, char *path)
+{
+       size_t            tt, ressz;
+       struct manpaths   ps;
+       int               i, sz, rc;
+       const char       *ep, *start;
+       struct res      *res;
+       char            **cp;
+       struct opts       opt;
+       struct expr      *expr;
+
+       if (req->q.manroot < 0 || 0 == req->psz) {
+               resp_search(NULL, 0, (void *)req);
+               return;
+       }
+
+       memset(&opt, 0, sizeof(struct opts));
+
+       ep       = req->q.expr;
+       opt.arch = req->q.arch;
+       opt.cat  = req->q.sec;
+       rc       = -1;
+       sz       = 0;
+       cp       = NULL;
+       ressz    = 0;
+       res      = NULL;
+
+       /*
+        * Begin by chdir()ing into the root of the manpath.
+        * This way we can pick up the database files, which are
+        * relative to the manpath root.
+        */
+
+       assert(req->q.manroot < (int)req->psz);
+       if (-1 == (chdir(req->p[req->q.manroot].path))) {
+               perror(req->p[req->q.manroot].path);
+               resp_search(NULL, 0, (void *)req);
+               return;
+       }
+
+       memset(&ps, 0, sizeof(struct manpaths));
+       manpath_manconf(&ps, "etc/catman.conf");
+
+       /*
+        * Poor man's tokenisation: just break apart by spaces.
+        * Yes, this is half-ass.  But it works for now.
+        */
+
+       while (ep && isspace((unsigned char)*ep))
+               ep++;
+
+       while (ep && '\0' != *ep) {
+               cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *));
+               start = ep;
+               while ('\0' != *ep && ! isspace((unsigned char)*ep))
+                       ep++;
+               cp[sz] = mandoc_malloc((ep - start) + 1);
+               memcpy(cp[sz], start, ep - start);
+               cp[sz++][ep - start] = '\0';
+               while (isspace((unsigned char)*ep))
+                       ep++;
+       }
+
+       /*
+        * Pump down into apropos backend.
+        * The resp_search() function is called with the results.
+        */
+
+       expr = req->q.legacy ? 
+               termcomp(sz, cp, &tt) : exprcomp(sz, cp, &tt);
+
+       if (NULL != expr)
+               rc = apropos_search
+                       (ps.sz, ps.paths, &opt, expr, tt, 
+                        (void *)req, &ressz, &res, resp_search);
+
+       /* ...unless errors occured. */
+
+       if (0 == rc)
+               resp_baddb();
+       else if (-1 == rc)
+               resp_search(NULL, 0, NULL);
+
+       for (i = 0; i < sz; i++)
+               free(cp[i]);
+
+       free(cp);
+       resfree(res, ressz);
+       exprfree(expr);
+       manpath_free(&ps);
+}
+
+int
+main(void)
+{
+       int              i;
+       char             buf[MAXPATHLEN];
+       DIR             *cwd;
+       struct req       req;
+       char            *p, *path, *subpath;
+
+       /* Scan our run-time environment. */
+
+       if (NULL == (cache = getenv("CACHE_DIR")))
+               cache = "/cache/man.cgi";
+
+       if (NULL == (progname = getenv("SCRIPT_NAME")))
+               progname = "";
+
+       if (NULL == (css = getenv("CSS_DIR")))
+               css = "";
+
+       if (NULL == (host = getenv("HTTP_HOST")))
+               host = "localhost";
+
+       /*
+        * First we change directory into the cache directory so that
+        * subsequent scanning for manpath directories is rooted
+        * relative to the same position.
+        */
+
+       if (-1 == chdir(cache)) {
+               perror(cache);
+               resp_bad();
+               return(EXIT_FAILURE);
+       } else if (NULL == (cwd = opendir(cache))) {
+               perror(cache);
+               resp_bad();
+               return(EXIT_FAILURE);
+       } 
+
+       memset(&req, 0, sizeof(struct req));
+
+       strlcpy(buf, ".", MAXPATHLEN);
+       pathgen(cwd, buf, &req);
+       closedir(cwd);
+
+       /* Next parse out the query string. */
+
+       if (NULL != (p = getenv("QUERY_STRING")))
+               http_parse(&req, p);
+
+       /*
+        * Now juggle paths to extract information.
+        * We want to extract our filetype (the file suffix), the
+        * initial path component, then the trailing component(s).
+        * Start with leading subpath component. 
+        */
+
+       subpath = path = NULL;
+       req.page = PAGE__MAX;
+
+       if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path)
+               req.page = PAGE_INDEX;
+
+       if (NULL != path && '/' == *path && '\0' == *++path)
+               req.page = PAGE_INDEX;
+
+       /* Strip file suffix. */
+
+       if (NULL != path && NULL != (p = strrchr(path, '.')))
+               if (NULL != p && NULL == strchr(p, '/'))
+                       *p++ = '\0';
+
+       /* Resolve subpath component. */
+
+       if (NULL != path && NULL != (subpath = strchr(path, '/')))
+               *subpath++ = '\0';
+
+       /* Map path into one we recognise. */
+
+       if (NULL != path && '\0' != *path)
+               for (i = 0; i < (int)PAGE__MAX; i++) 
+                       if (0 == strcmp(pages[i], path)) {
+                               req.page = (enum page)i;
+                               break;
+                       }
+
+       /* Route pages. */
+
+       switch (req.page) {
+       case (PAGE_INDEX):
+               pg_index(&req, subpath);
+               break;
+       case (PAGE_SEARCH):
+               pg_search(&req, subpath);
+               break;
+       case (PAGE_SHOW):
+               pg_show(&req, subpath);
+               break;
+       default:
+               resp_error404(path);
+               break;
+       }
+
+       for (i = 0; i < (int)req.psz; i++) {
+               free(req.p[i].path);
+               free(req.p[i].name);
+       }
+
+       free(req.p);
+       return(EXIT_SUCCESS);
+}
+
+static int
+cmp(const void *p1, const void *p2)
+{
+
+       return(strcasecmp(((const struct res *)p1)->title,
+                               ((const struct res *)p2)->title));
+}
+
+/*
+ * Check to see if an "etc" path consists of a catman.conf file.  If it
+ * does, that means that the path contains a tree created by catman(8)
+ * and should be used for indexing.
+ */
+static int
+pathstop(DIR *dir)
+{
+       struct dirent   *d;
+
+       while (NULL != (d = readdir(dir)))
+               if (DT_REG == d->d_type)
+                       if (0 == strcmp(d->d_name, "catman.conf"))
+                               return(1);
+
+       return(0);
+}
+
+/*
+ * Scan for indexable paths.
+ * This adds all paths with "etc/catman.conf" to the buffer.
+ */
+static void
+pathgen(DIR *dir, char *path, struct req *req)
+{
+       struct dirent   *d;
+       char            *cp;
+       DIR             *cd;
+       int              rc;
+       size_t           sz, ssz;
+
+       sz = strlcat(path, "/", MAXPATHLEN);
+       if (sz >= MAXPATHLEN) {
+               fprintf(stderr, "%s: Path too long", path);
+               return;
+       } 
+
+       /* 
+        * First, scan for the "etc" directory.
+        * If it's found, then see if it should cause us to stop.  This
+        * happens when a catman.conf is found in the directory.
+        */
+
+       rc = 0;
+       while (0 == rc && NULL != (d = readdir(dir))) {
+               if (DT_DIR != d->d_type || strcmp(d->d_name, "etc"))
+                       continue;
+
+               path[(int)sz] = '\0';
+               ssz = strlcat(path, d->d_name, MAXPATHLEN);
+
+               if (ssz >= MAXPATHLEN) {
+                       fprintf(stderr, "%s: Path too long", path);
+                       return;
+               } else if (NULL == (cd = opendir(path))) {
+                       perror(path);
+                       return;
+               } 
+               
+               rc = pathstop(cd);
+               closedir(cd);
+       }
+
+       if (rc > 0) {
+               /* This also strips the trailing slash. */
+               path[(int)--sz] = '\0';
+               req->p = mandoc_realloc
+                       (req->p, 
+                        (req->psz + 1) * sizeof(struct paths));
+               /*
+                * Strip out the leading "./" unless we're just a ".",
+                * in which case use an empty string as our name.
+                */
+               req->p[(int)req->psz].path = mandoc_strdup(path);
+               req->p[(int)req->psz].name = 
+                       cp = mandoc_strdup(path + (1 == sz ? 1 : 2));
+               req->psz++;
+               /* 
+                * The name is just the path with all the slashes taken
+                * out of it.  Simple but effective. 
+                */
+               for ( ; '\0' != *cp; cp++) 
+                       if ('/' == *cp)
+                               *cp = ' ';
+               return;
+       } 
+
+       /*
+        * If no etc/catman.conf was found, recursively enter child
+        * directory and continue scanning.
+        */
+
+       rewinddir(dir);
+       while (NULL != (d = readdir(dir))) {
+               if (DT_DIR != d->d_type || '.' == d->d_name[0])
+                       continue;
+
+               path[(int)sz] = '\0';
+               ssz = strlcat(path, d->d_name, MAXPATHLEN);
+
+               if (ssz >= MAXPATHLEN) {
+                       fprintf(stderr, "%s: Path too long", path);
+                       return;
+               } else if (NULL == (cd = opendir(path))) {
+                       perror(path);
+                       return;
+               }
+
+               pathgen(cd, path, req);
+               closedir(cd);
+       }
+}
index 5158612..ce03347 100644 (file)
@@ -1,6 +1,6 @@
-/*     $Id: chars.c,v 1.46 2011/05/24 21:31:23 kristaps Exp $ */
+/*     $Id: chars.c,v 1.52 2011/11/08 00:15:23 kristaps Exp $ */
 /*
- * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -21,7 +21,6 @@
 
 #include <assert.h>
 #include <ctype.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -38,7 +37,7 @@ struct        ln {
        int               unicode;
 };
 
-#define        LINES_MAX         325
+#define        LINES_MAX         328
 
 #define CHAR(in, ch, code) \
        { NULL, (in), (ch), (code) },
@@ -52,8 +51,8 @@ struct        mchars {
        struct ln       **htab;
 };
 
-static inline int        match(const struct ln *, const char *, size_t);
-static const struct ln  *find(struct mchars *, const char *, size_t);
+static const struct ln  *find(const struct mchars *, 
+                               const char *, size_t);
 
 void
 mchars_free(struct mchars *arg)
@@ -74,8 +73,7 @@ mchars_alloc(void)
        /*
         * 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).
+        * Subsequent entries are chained in the order they're processed.
         */
 
        tab = mandoc_malloc(sizeof(struct mchars));
@@ -98,12 +96,8 @@ mchars_alloc(void)
        return(tab);
 }
 
-
-/* 
- * Special character to Unicode codepoint.
- */
 int
-mchars_spec2cp(struct mchars *arg, const char *p, size_t sz)
+mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz)
 {
        const struct ln *ln;
 
@@ -113,103 +107,61 @@ mchars_spec2cp(struct mchars *arg, const char *p, size_t sz)
        return(ln->unicode);
 }
 
-/*
- * Numbered character string to ASCII codepoint.
- * This can only be a printable character (i.e., alnum, punct, space) so
- * prevent the character from ruining our state (backspace, newline, and
- * so on).
- * If the character is illegal, returns '\0'.
- */
 char
 mchars_num2char(const char *p, size_t sz)
 {
        int               i;
 
-       if ((i = mandoc_strntou(p, sz, 10)) < 0)
+       if ((i = mandoc_strntoi(p, sz, 10)) < 0)
                return('\0');
-       return(isprint(i) ? i : '\0');
+       return(i > 0 && i < 256 && isprint(i) ? 
+                       /* LINTED */ i : '\0');
 }
 
-/*
- * Hex character string to Unicode codepoint.
- * If the character is illegal, returns '\0'.
- */
 int
 mchars_num2uc(const char *p, size_t sz)
 {
        int               i;
 
-       if ((i = mandoc_strntou(p, sz, 16)) < 0)
+       if ((i = mandoc_strntoi(p, sz, 16)) < 0)
                return('\0');
        /* FIXME: make sure we're not in a bogus range. */
        return(i > 0x80 && i <= 0x10FFFF ? i : '\0');
 }
 
-/* 
- * Special character to string array.
- */
 const char *
-mchars_spec2str(struct mchars *arg, const char *p, size_t sz, size_t *rsz)
+mchars_spec2str(const struct mchars *arg, 
+               const char *p, size_t sz, size_t *rsz)
 {
        const struct ln *ln;
 
        ln = find(arg, p, sz);
-       if (NULL == ln)
+       if (NULL == ln) {
+               *rsz = 1;
                return(NULL);
+       }
 
        *rsz = strlen(ln->ascii);
        return(ln->ascii);
 }
 
 static const struct ln *
-find(struct mchars *tab, const char *p, size_t sz)
+find(const struct mchars *tab, const char *p, size_t sz)
 {
-       struct ln        *pp, *prev;
-       struct ln       **htab;
+       const struct ln  *pp;
        int               hash;
 
        assert(p);
-       if (0 == sz)
-               return(NULL);
 
-       if (p[0] < PRINT_LO || p[0] > PRINT_HI)
+       if (0 == sz || 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)) {
-                       prev = pp;
-                       continue;
-               }
-
-               if (prev) {
-                       prev->next = pp->next;
-                       pp->next = htab[hash];
-                       htab[hash] = pp;
-               }
-
-               return(pp);
-       }
+       for (pp = tab->htab[hash]; pp; pp = pp->next)
+               if (0 == strncmp(pp->code, p, sz) && 
+                               '\0' == pp->code[(int)sz])
+                       return(pp);
 
        return(NULL);
 }
-
-static inline int
-match(const struct ln *ln, const char *p, size_t sz)
-{
-
-       if (strncmp(ln->code, p, sz))
-               return(0);
-       return('\0' == ln->code[(int)sz]);
-}
index 483a2bb..a4c45b3 100644 (file)
@@ -1,6 +1,6 @@
-/*     $Id: chars.in,v 1.39 2011/05/24 21:40:14 kristaps Exp $ */
+/*     $Id: chars.in,v 1.42 2011/10/02 10:02:26 kristaps Exp $ */
 /*
- * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2009, 2010, 2011 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
@@ -33,15 +33,15 @@ static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' };
 CHAR_TBL_START
 
 /* Spacing. */
-CHAR("c",                      "",             8203)
+CHAR("c",                      "",             0)
 CHAR("0",                      " ",            8194)
 CHAR(" ",                      ascii_nbrsp,    160)
 CHAR("~",                      ascii_nbrsp,    160)
-CHAR("%",                      "",             8203)
-CHAR("&",                      "",             8203)
-CHAR("^",                      "",             8203)
-CHAR("|",                      "",             8203)
-CHAR("}",                      "",             8203)
+CHAR("%",                      "",             0)
+CHAR("&",                      "",             0)
+CHAR("^",                      "",             0)
+CHAR("|",                      "",             0)
+CHAR("}",                      "",             0)
 
 /* Accents. */
 CHAR("a\"",                    "\"",           779)
@@ -312,6 +312,9 @@ CHAR("Im",                  "I",            8465)
 CHAR("Re",                     "R",            8476)
 CHAR("pd",                     "a",            8706)
 CHAR("-h",                     "/h",           8463)
+CHAR("12",                     "1/2",          189)
+CHAR("14",                     "1/4",          188)
+CHAR("34",                     "3/4",          190)
 
 /* Ligatures. */
 CHAR("ff",                     "ff",           64256)
diff --git a/contrib/mdocml/compat_fgetln.c b/contrib/mdocml/compat_fgetln.c
new file mode 100644 (file)
index 0000000..49c9985
--- /dev/null
@@ -0,0 +1,93 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_FGETLN
+
+int dummy;
+
+#else
+
+/*     $NetBSD: fgetln.c,v 1.3 2006/09/25 07:18:17 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+fgetln(fp, len)
+       FILE *fp;
+       size_t *len;
+{
+       static char *buf = NULL;
+       static size_t bufsiz = 0;
+       char *ptr;
+
+
+       if (buf == NULL) {
+               bufsiz = BUFSIZ;
+               if ((buf = malloc(bufsiz)) == NULL)
+                       return NULL;
+       }
+
+       if (fgets(buf, bufsiz, fp) == NULL)
+               return NULL;
+
+       *len = 0;
+       while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
+               size_t nbufsiz = bufsiz + BUFSIZ;
+               char *nbuf = realloc(buf, nbufsiz);
+
+               if (nbuf == NULL) {
+                       int oerrno = errno;
+                       free(buf);
+                       errno = oerrno;
+                       buf = NULL;
+                       return NULL;
+               } else
+                       buf = nbuf;
+
+               *len = bufsiz;
+               if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL)
+                       return buf;
+
+               bufsiz = nbufsiz;
+       }
+
+       *len = (ptr - buf) + 1;
+       return buf;
+}
+
+#endif
diff --git a/contrib/mdocml/compat_getsubopt.c b/contrib/mdocml/compat_getsubopt.c
new file mode 100644 (file)
index 0000000..9cd4153
--- /dev/null
@@ -0,0 +1,104 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_GETSUBOPT
+
+int dummy;
+
+#else
+
+/*     $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 espie Exp $     */
+
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The SVID interface to getsubopt provides no way of figuring out which
+ * part of the suboptions list wasn't matched.  This makes error messages
+ * tricky...  The extern variable suboptarg is a pointer to the token
+ * which didn't match.
+ */
+char *suboptarg;
+
+int
+getsubopt(char **optionp, char * const *tokens, char **valuep)
+{
+       int cnt;
+       char *p;
+
+       suboptarg = *valuep = NULL;
+
+       if (!optionp || !*optionp)
+               return(-1);
+
+       /* skip leading white-space, commas */
+       for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p);
+
+       if (!*p) {
+               *optionp = p;
+               return(-1);
+       }
+
+       /* save the start of the token, and skip the rest of the token. */
+       for (suboptarg = p;
+           *++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';);
+
+       if (*p) {
+               /*
+                * If there's an equals sign, set the value pointer, and
+                * skip over the value part of the token.  Terminate the
+                * token.
+                */
+               if (*p == '=') {
+                       *p = '\0';
+                       for (*valuep = ++p;
+                           *p && *p != ',' && *p != ' ' && *p != '\t'; ++p);
+                       if (*p) 
+                               *p++ = '\0';
+               } else
+                       *p++ = '\0';
+               /* Skip any whitespace or commas after this token. */
+               for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p);
+       }
+
+       /* set optionp for next round. */
+       *optionp = p;
+
+       for (cnt = 0; *tokens; ++tokens, ++cnt)
+               if (!strcmp(suboptarg, *tokens))
+                       return(cnt);
+       return(-1);
+}
+
+#endif
similarity index 70%
copy from contrib/mdocml/compat.c
copy to contrib/mdocml/compat_strlcat.c
index f00cc5c..543d40b 100644 (file)
@@ -1,3 +1,13 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STRLCAT
+
+int dummy;
+
+#else
+
 /*     $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $      */
 
 /*
  * 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
@@ -59,37 +63,5 @@ strlcat(char *dst, const char *src, size_t siz)
 
        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 
+#endif
similarity index 62%
rename from contrib/mdocml/compat.c
rename to contrib/mdocml/compat_strlcpy.c
index f00cc5c..a7c64ff 100644 (file)
@@ -1,4 +1,14 @@
-/*     $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $      */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STRLCPY
+
+int dummy;
+
+#else
+
+/*     $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $    */
 
 /*
  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
  * 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).
@@ -92,4 +59,5 @@ strlcpy(char *dst, const char *src, size_t siz)
 
        return(s - src - 1);    /* count does not include NUL */
 }
-#endif 
+
+#endif
index 81c01b9..39da2b2 100644 (file)
 #  endif
 #endif
 
+#if defined(__APPLE__)
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define betoh32(x) OSSwapBigToHostInt32(x)
+# define htobe64(x) OSSwapHostToBigInt64(x)
+# define betoh64(x) OSSwapBigToHostInt64(x)
+#elif defined(__linux__)
+# define betoh32(x) be32toh(x)
+# define betoh64(x) be64toh(x)
+#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
+#ifndef HAVE_GETSUBOPT
+extern int       getsubopt(char **, char * const *, char **);
+extern char     *suboptarg;
+#endif
+#ifndef HAVE_FGETLN
+extern char     *fgetln(FILE *, size_t *);
+#endif
 
 #endif /* MANDOC_CONFIG_H */
index a309ed9..bc59478 100644 (file)
@@ -4,3 +4,5 @@
 #if defined(__linux__) || defined(__MINT__)
 # define _GNU_SOURCE /* strptime(), getsubopt() */
 #endif
+
+#include <stdio.h>
diff --git a/contrib/mdocml/demandoc.1 b/contrib/mdocml/demandoc.1
new file mode 100644 (file)
index 0000000..845b9c1
--- /dev/null
@@ -0,0 +1,109 @@
+.\"    $Id: demandoc.1,v 1.6 2011/12/25 19:35:44 kristaps Exp $
+.\"
+.\" Copyright (c) 2011 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: December 25 2011 $
+.Dt DEMANDOC 1
+.Os
+.Sh NAME
+.Nm demandoc
+.Nd emit only text of UNIX manuals
+.Sh SYNOPSIS
+.Nm demandoc
+.Op Fl w
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility emits only the text portions of well-formed
+.Xr mdoc 7
+and
+.Xr man 7
+.Ux
+manual files.
+.Pp
+By default,
+.Nm
+parses standard input and outputs only text nodes, preserving line
+and column position.
+Escape sequences are omitted from the output.
+.Pp
+Its arguments are as follows:
+.Bl -tag -width Ds
+.It Fl w
+Output a word list.
+This outputs each word of text on its own line.
+A
+.Qq word ,
+in this case, refers to whitespace-delimited terms beginning with at
+least two letters and not consisting of any escape sequences.
+Words have their leading and trailing punctuation
+.Pq double-quotes, sentence punctuation, etc.
+stripped.
+.It Ar
+The input files.
+.El
+.Pp
+If a document is not well-formed, it is skipped.
+.Pp
+The
+.Fl i ,
+.Fl k ,
+.Fl m ,
+and
+.Fl p
+flags are silently discarded for calling compatibility with the
+historical deroff.
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -compact
+.It 0
+No errors occurred.
+.It 6
+An operating system error occurred, for example memory exhaustion or an
+error accessing input files.
+Such errors cause
+.Nm
+to exit at once, possibly in the middle of parsing or formatting a file.
+The output databases are corrupt and should be removed .
+.El
+.Sh EXAMPLES
+The traditional usage of
+.Nm
+is for spell-checking manuals on
+.Bx .
+This is accomplished as follows (assuming British spelling):
+.Pp
+.Dl $ demandoc -w file.1 | spell -b
+.Sh SEE ALSO
+.Xr mandoc 1 ,
+.Xr man 7
+.Xr mdoc 7
+.Sh HISTORY
+.Nm
+replaces the historical deroff utility for handling modern
+.Xr man 7
+and
+.Xr mdoc 7
+documents.
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Kristaps Dzonsons ,
+.Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/demandoc.c b/contrib/mdocml/demandoc.c
new file mode 100644 (file)
index 0000000..2474a35
--- /dev/null
@@ -0,0 +1,257 @@
+/*     $Id: demandoc.c,v 1.6 2011/09/01 22:25:53 kristaps Exp $ */
+/*
+ * Copyright (c) 2011 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 <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "man.h"
+#include "mdoc.h"
+#include "mandoc.h"
+
+static void     pline(int, int *, int *, int);
+static void     pman(const struct man_node *, int *, int *, int);
+static void     pmandoc(struct mparse *, int, const char *, int);
+static void     pmdoc(const struct mdoc_node *, int *, int *, int);
+static void     pstring(const char *, int, int *, int);
+static void     usage(void);
+
+static const char       *progname;
+
+int
+main(int argc, char *argv[])
+{
+       struct mparse   *mp;
+       int              ch, i, list;
+       extern int       optind;
+
+       progname = strrchr(argv[0], '/');
+       if (progname == NULL)
+               progname = argv[0];
+       else
+               ++progname;
+
+       mp = NULL;
+       list = 0;
+
+       while (-1 != (ch = getopt(argc, argv, "ikm:pw")))
+               switch (ch) {
+               case ('i'):
+                       /* FALLTHROUGH */
+               case ('k'):
+                       /* FALLTHROUGH */
+               case ('m'):
+                       /* FALLTHROUGH */
+               case ('p'):
+                       break;
+               case ('w'):
+                       list = 1;
+                       break;
+               default:
+                       usage();
+                       return((int)MANDOCLEVEL_BADARG);
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
+       assert(mp);
+
+       if (0 == argc)
+               pmandoc(mp, STDIN_FILENO, "<stdin>", list);
+
+       for (i = 0; i < argc; i++) {
+               mparse_reset(mp);
+               pmandoc(mp, -1, argv[i], list);
+       }
+
+       mparse_free(mp);
+       return((int)MANDOCLEVEL_OK);
+}
+
+static void
+usage(void)
+{
+
+       fprintf(stderr, "usage: %s [-w] [files...]\n", progname);
+}
+
+static void
+pmandoc(struct mparse *mp, int fd, const char *fn, int list)
+{
+       struct mdoc     *mdoc;
+       struct man      *man;
+       int              line, col;
+
+       if (mparse_readfd(mp, fd, fn) >= MANDOCLEVEL_FATAL) {
+               fprintf(stderr, "%s: Parse failure\n", fn);
+               return;
+       }
+
+       mparse_result(mp, &mdoc, &man);
+       line = 1;
+       col = 0;
+
+       if (mdoc) 
+               pmdoc(mdoc_node(mdoc), &line, &col, list);
+       else if (man)
+               pman(man_node(man), &line, &col, list);
+       else
+               return;
+
+       if ( ! list)
+               putchar('\n');
+}
+
+/*
+ * Strip the escapes out of a string, emitting the results.
+ */
+static void
+pstring(const char *p, int col, int *colp, int list)
+{
+       enum mandoc_esc  esc;
+       const char      *start, *end;
+       int              emit;
+
+       /*
+        * Print as many column spaces til we achieve parity with the
+        * input document.
+        */
+
+again:
+       if (list && '\0' != *p) {
+               while (isspace((unsigned char)*p))
+                       p++;
+
+               while ('\'' == *p || '(' == *p || '"' == *p)
+                       p++;
+
+               emit = isalpha((unsigned char)p[0]) &&
+                       isalpha((unsigned char)p[1]);
+
+               for (start = p; '\0' != *p; p++)
+                       if ('\\' == *p) {
+                               p++;
+                               esc = mandoc_escape(&p, NULL, NULL);
+                               if (ESCAPE_ERROR == esc)
+                                       return;
+                               emit = 0;
+                       } else if (isspace((unsigned char)*p))
+                               break;
+
+               end = p - 1;
+
+               while (end > start)
+                       if ('.' == *end || ',' == *end || 
+                                       '\'' == *end || '"' == *end ||
+                                       ')' == *end || '!' == *end ||
+                                       '?' == *end || ':' == *end ||
+                                       ';' == *end)
+                               end--;
+                       else
+                               break;
+
+               if (emit && end - start >= 1) {
+                       for ( ; start <= end; start++)
+                               if (ASCII_HYPH == *start)
+                                       putchar('-');
+                               else
+                                       putchar((unsigned char)*start);
+                       putchar('\n');
+               }
+
+               if (isspace((unsigned char)*p))
+                       goto again;
+
+               return;
+       }
+
+       while (*colp < col) {
+               putchar(' ');
+               (*colp)++;
+       }
+
+       /*
+        * Print the input word, skipping any special characters.
+        */
+       while ('\0' != *p) 
+               if ('\\' == *p) {
+                       p++;
+                       esc = mandoc_escape(&p, NULL, NULL);
+                       if (ESCAPE_ERROR == esc)
+                               break;
+               } else {
+                       putchar((unsigned char )*p++);
+                       (*colp)++;
+               }
+}
+
+static void
+pline(int line, int *linep, int *col, int list)
+{
+
+       if (list)
+               return;
+
+       /*
+        * Print out as many lines as needed to reach parity with the
+        * original input. 
+        */
+
+       while (*linep < line) {
+               putchar('\n');
+               (*linep)++;
+       }
+
+       *col = 0;
+}
+
+static void
+pmdoc(const struct mdoc_node *p, int *line, int *col, int list)
+{
+
+       for ( ; p; p = p->next) {
+               if (MDOC_LINE & p->flags)
+                       pline(p->line, line, col, list);
+               if (MDOC_TEXT == p->type)
+                       pstring(p->string, p->pos, col, list);
+               if (p->child) 
+                       pmdoc(p->child, line, col, list);
+       }
+}
+
+static void
+pman(const struct man_node *p, int *line, int *col, int list)
+{
+
+       for ( ; p; p = p->next) {
+               if (MAN_LINE & p->flags)
+                       pline(p->line, line, col, list);
+               if (MAN_TEXT == p->type)
+                       pstring(p->string, p->pos, col, list);
+               if (p->child) 
+                       pman(p->child, line, col, list);
+       }
+}
index b2b66f4..f86b9c4 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $Id: eqn.7,v 1.2 2011/03/17 15:12:42 kristaps Exp $
+.\"    $Id: eqn.7,v 1.28 2011/09/25 18:37:09 schwarze Exp $
 .\"
 .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: March 17 2011 $
+.Dd $Mdocdate: September 25 2011 $
 .Dt EQN 7
 .Os
 .Sh NAME
 .Sh DESCRIPTION
 The
 .Nm eqn
-language is a equation-formatting language.
+language is an equation-formatting language.
 It is used within
 .Xr mdoc 7
 and
 .Xr man 7
 .Ux
 manual pages.
-This manual describes the subset of the
+It describes the
+.Em structure
+of an equation, not its mathematical meaning.
+This manual describes the
 .Nm
 language accepted by the
 .Xr mandoc 1
-utility.
+utility, which corresponds to the Second Edition eqn specification (see
+.Sx SEE ALSO
+for references).
 .Pp
 Equations within
 .Xr mdoc 7
 or
 .Xr man 7
-are enclosed by the
-.Sq EQ
+documents are enclosed by the standalone
+.Sq \&.EQ
 and
-.Sq EN
-macro tags, whose precise syntax is documented in
-.Xr roff 7 .
-Equations consist of multi-line equation data.
+.Sq \&.EN
+tags.
+Equations are multi-line blocks consisting of formulas and control
+statements.
+.Sh EQUATION STRUCTURE
+Each equation is bracketed by
+.Sq \&.EQ
+and
+.Sq \&.EN
+strings.
+.Em Note :
+these are not the same as
+.Xr roff 7
+macros, and may only be invoked as
+.Sq \&.EQ .
 .Pp
-For the time being,
-.Xr mandoc 1
-reproduces the contents of
+The equation grammar is as follows, where quoted strings are
+case-sensitive literals in the input:
+.Bd -literal -offset indent
+eqn     : box | eqn box
+box     : text
+        | \*q{\*q eqn \*q}\*q
+        | \*qdefine\*q text text
+        | \*qndefine\*q text text
+        | \*qtdefine\*q text text
+        | \*qgfont\*q text
+        | \*qgsize\*q text
+        | \*qset\*q text text
+        | \*qundef\*q text
+        | box pos box
+        | box mark
+        | \*qmatrix\*q \*q{\*q [col \*q{\*q list \*q}\*q ]*
+        | pile \*q{\*q list \*q}\*q
+        | font box
+        | \*qsize\*q text box
+        | \*qleft\*q text eqn [\*qright\*q text]
+col     : \*qlcol\*q | \*qrcol\*q | \*qccol\*q | \*qcol\*q
+text    : [^space\e\*q]+ | \e\*q.*\e\*q
+pile    : \*qlpile\*q | \*qcpile\*q | \*qrpile\*q | \*qpile\*q
+pos     : \*qover\*q | \*qsup\*q | \*qsub\*q | \*qto\*q | \*qfrom\*q
+mark   : \*qdot\*q | \*qdotdot\*q | \*qhat\*q | \*qtilde\*q | \*qvec\*q
+        | \*qdyad\*q | \*qbar\*q | \*qunder\*q
+font    : \*qroman\*q | \*qitalic\*q | \*qbold\*q | \*qfat\*q
+list    : eqn
+        | list \*qabove\*q eqn
+space   : [\e^~ \et]
+.Ed
+.Pp
+White-space consists of the space, tab, circumflex, and tilde
+characters.
+If within a quoted string, these space characters are retained.
+Quoted strings are also not scanned for replacement definitions.
+.Pp
+The following text terms are translated into a rendered glyph, if
+available: alpha, beta, chi, delta, epsilon, eta, gamma, iota, kappa,
+lambda, mu, nu, omega, omicron, phi, pi, psi, rho, sigma, tau, theta,
+upsilon, xi, zeta, DELTA, GAMMA, LAMBDA, OMEGA, PHI, PI, PSI, SIGMA,
+THETA, UPSILON, XI, inter (intersection), union (union), prod (product),
+int (integral), sum (summation), grad (gradient), del (vector
+differential), times (multiply), cdot (centre-dot), nothing (zero-width
+space), approx (approximately equals), prime (prime), half (one-half),
+partial (partial differential), inf (infinity), >> (much greater), <<
+(much less), \-> (left arrow), <\- (right arrow), += (plus-minus), !=
+(not equal), == (equivalence), <= (less-than-equal), and >=
+(more-than-equal).
+.Pp
+The following control statements are available:
+.Bl -tag -width Ds
+.It Cm define
+Replace all occurrences of a key with a value.
+Its syntax is as follows:
+.Pp
+.D1 define Ar key cvalc
+.Pp
+The first character of the value string,
+.Ar c ,
+is used as the delimiter for the value
+.Ar val .
+This allows for arbitrary enclosure of terms (not just quotes), such as
+.Pp
+.D1 define Ar foo 'bar baz'
+.D1 define Ar foo cbar bazc
+.Pp
+It is an error to have an empty
+.Ar key
+or
+.Ar val .
+Note that a quoted
+.Ar key
+causes errors in some
 .Nm
-equations verbatim in its output.
+implementations and should not be considered portable.
+It is not expanded for replacements.
+Definitions may refer to other definitions; these are evaluated
+recursively when text replacement occurs and not when the definition is
+created.
+.Pp
+Definitions can create arbitrary strings, for example, the following is
+a legal construction.
+.Bd -literal -offset indent
+define foo 'define'
+foo bar 'baz'
+.Ed
+.Pp
+Self-referencing definitions will raise an error.
+The
+.Cm ndefine
+statement is a synonym for
+.Cm define ,
+while
+.Cm tdefine
+is discarded.
+.It Cm gfont
+Set the default font of subsequent output.
+Its syntax is as follows:
+.Pp
+.D1 gfont Ar font
+.Pp
+In mandoc, this value is discarded.
+.It Cm gsize
+Set the default size of subsequent output.
+Its syntax is as follows:
+.Pp
+.D1 gsize Ar size
+.Pp
+The
+.Ar size
+value should be an integer.
+.It Cm set
+Set an equation mode.
+In mandoc, both arguments are thrown away.
+Its syntax is as follows:
+.Pp
+.D1 set Ar key val
 .Pp
 The
+.Ar key
+and
+.Ar val
+are not expanded for replacements.
+This statement is a GNU extension.
+.It Cm undef
+Unset a previously-defined key.
+Its syntax is as follows:
+.Pp
+.D1 define Ar key
+.Pp
+Once invoked, the definition for
+.Ar key
+is discarded.
+The
+.Ar key
+is not expanded for replacements.
+This statement is a GNU extension.
+.El
+.Sh COMPATIBILITY
+This section documents the compatibility of mandoc
 .Nm
-implementation in
-.Xr mandoc 1
-is
-.Ud
+and the troff
+.Nm
+implementation (including GNU troff).
+.Pp
+.Bl -dash -compact
+.It
+The text string
+.Sq \e\*q
+is interpreted as a literal quote in troff.
+In mandoc, this is interpreted as a comment.
+.It
+In troff, The circumflex and tilde white-space symbols map to
+fixed-width spaces.
+In mandoc, these characters are synonyms for the space character.
+.It
+The troff implementation of
+.Nm
+allows for equation alignment with the
+.Cm mark
+and
+.Cm lineup
+tokens.
+mandoc discards these tokens.
+The
+.Cm back Ar n ,
+.Cm fwd Ar n ,
+.Cm up Ar n ,
+and
+.Cm down Ar n
+commands are also ignored.
+.El
 .Sh SEE ALSO
 .Xr mandoc 1 ,
 .Xr man 7 ,
@@ -75,18 +252,29 @@ is
 .%P 151\(en157
 .%D March, 1975
 .Re
-.\" .Sh HISTORY
-.\" The tbl utility, a preprocessor for troff, was originally written by M.
-.\" E. Lesk at Bell Labs in 1975.
-.\" The GNU reimplementation of tbl, part of the groff package, was released
-.\" in 1990 by James Clark.
-.\" A standalone tbl implementation was written by Kristaps Dzonsons in
-.\" 2010.
-.\" This formed the basis of the implementation that is part of the
-.\" .Xr mandoc 1
-.\" utility.
+.Rs
+.%A Brian W. Kernighan
+.%A Lorinda L. Cherry
+.%T Typesetting Mathematics, User's Guide
+.%D 1976
+.Re
+.Rs
+.%A Brian W. Kernighan
+.%A Lorinda L. Cherry
+.%T Typesetting Mathematics, User's Guide (Second Edition)
+.%D 1978
+.Re
+.Sh HISTORY
+The eqn utility, a preprocessor for troff, was originally written by
+Brian W. Kernighan and Lorinda L. Cherry in 1975.
+The GNU reimplementation of eqn, part of the GNU troff package, was
+released in 1989 by James Clark.
+The eqn component of
+.Xr mandoc 1
+was added in 2011.
 .Sh AUTHORS
-This partial
+This
 .Nm
 reference was written by
-.An Kristaps Dzonsons Aq kristaps@bsd.lv .
+.An Kristaps Dzonsons ,
+.Mt kristaps@bsd.lv .
index 220f3f8..37f01bc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: eqn.c,v 1.4 2011/03/22 09:48:13 kristaps Exp $ */
+/*     $Id: eqn.c,v 1.38 2011/07/25 15:37:00 kristaps Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -19,6 +19,7 @@
 #endif
 
 #include <assert.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "libmandoc.h"
 #include "libroff.h"
 
+#define        EQN_NEST_MAX     128 /* maximum nesting of defines */
+#define        EQN_MSG(t, x)    mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
+
+enum   eqn_rest {
+       EQN_DESCOPE,
+       EQN_ERR,
+       EQN_OK,
+       EQN_EOF
+};
+
+enum   eqn_symt {
+       EQNSYM_alpha,
+       EQNSYM_beta,
+       EQNSYM_chi,
+       EQNSYM_delta,
+       EQNSYM_epsilon,
+       EQNSYM_eta,
+       EQNSYM_gamma,
+       EQNSYM_iota,
+       EQNSYM_kappa,
+       EQNSYM_lambda,
+       EQNSYM_mu,
+       EQNSYM_nu,
+       EQNSYM_omega,
+       EQNSYM_omicron,
+       EQNSYM_phi,
+       EQNSYM_pi,
+       EQNSYM_ps,
+       EQNSYM_rho,
+       EQNSYM_sigma,
+       EQNSYM_tau,
+       EQNSYM_theta,
+       EQNSYM_upsilon,
+       EQNSYM_xi,
+       EQNSYM_zeta,
+       EQNSYM_DELTA,
+       EQNSYM_GAMMA,
+       EQNSYM_LAMBDA,
+       EQNSYM_OMEGA,
+       EQNSYM_PHI,
+       EQNSYM_PI,
+       EQNSYM_PSI,
+       EQNSYM_SIGMA,
+       EQNSYM_THETA,
+       EQNSYM_UPSILON,
+       EQNSYM_XI,
+       EQNSYM_inter,
+       EQNSYM_union,
+       EQNSYM_prod,
+       EQNSYM_int,
+       EQNSYM_sum,
+       EQNSYM_grad,
+       EQNSYM_del,
+       EQNSYM_times,
+       EQNSYM_cdot,
+       EQNSYM_nothing,
+       EQNSYM_approx,
+       EQNSYM_prime,
+       EQNSYM_half,
+       EQNSYM_partial,
+       EQNSYM_inf,
+       EQNSYM_muchgreat,
+       EQNSYM_muchless,
+       EQNSYM_larrow,
+       EQNSYM_rarrow,
+       EQNSYM_pm,
+       EQNSYM_nequal,
+       EQNSYM_equiv,
+       EQNSYM_lessequal,
+       EQNSYM_moreequal,
+       EQNSYM__MAX
+};
+
+enum   eqnpartt {
+       EQN_DEFINE = 0,
+       EQN_NDEFINE,
+       EQN_TDEFINE,
+       EQN_SET,
+       EQN_UNDEF,
+       EQN_GFONT,
+       EQN_GSIZE,
+       EQN_BACK,
+       EQN_FWD,
+       EQN_UP,
+       EQN_DOWN,
+       EQN__MAX
+};
+
+struct eqnstr {
+       const char      *name;
+       size_t           sz;
+};
+
+#define        STRNEQ(p1, sz1, p2, sz2) \
+       ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
+#define        EQNSTREQ(x, p, sz) \
+       STRNEQ((x)->name, (x)->sz, (p), (sz))
+
+struct eqnpart {
+       struct eqnstr    str;
+       int             (*fp)(struct eqn_node *);
+};
+
+struct eqnsym {
+       struct eqnstr    str;
+       const char      *sym;
+};
+
+
+static enum eqn_rest    eqn_box(struct eqn_node *, struct eqn_box *);
+static struct eqn_box  *eqn_box_alloc(struct eqn_node *, 
+                               struct eqn_box *);
+static void             eqn_box_free(struct eqn_box *);
+static struct eqn_def  *eqn_def_find(struct eqn_node *, 
+                               const char *, size_t);
+static int              eqn_do_gfont(struct eqn_node *);
+static int              eqn_do_gsize(struct eqn_node *);
+static int              eqn_do_define(struct eqn_node *);
+static int              eqn_do_ign1(struct eqn_node *);
+static int              eqn_do_ign2(struct eqn_node *);
+static int              eqn_do_tdefine(struct eqn_node *);
+static int              eqn_do_undef(struct eqn_node *);
+static enum eqn_rest    eqn_eqn(struct eqn_node *, struct eqn_box *);
+static enum eqn_rest    eqn_list(struct eqn_node *, struct eqn_box *);
+static enum eqn_rest    eqn_matrix(struct eqn_node *, struct eqn_box *);
+static const char      *eqn_nexttok(struct eqn_node *, size_t *);
+static const char      *eqn_nextrawtok(struct eqn_node *, size_t *);
+static const char      *eqn_next(struct eqn_node *, 
+                               char, size_t *, int);
+static void             eqn_rewind(struct eqn_node *);
+
+static const struct eqnpart eqnparts[EQN__MAX] = {
+       { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */
+       { { "ndefine", 7 }, eqn_do_define }, /* EQN_NDEFINE */
+       { { "tdefine", 7 }, eqn_do_tdefine }, /* EQN_TDEFINE */
+       { { "set", 3 }, eqn_do_ign2 }, /* EQN_SET */
+       { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */
+       { { "gfont", 5 }, eqn_do_gfont }, /* EQN_GFONT */
+       { { "gsize", 5 }, eqn_do_gsize }, /* EQN_GSIZE */
+       { { "back", 4 }, eqn_do_ign1 }, /* EQN_BACK */
+       { { "fwd", 3 }, eqn_do_ign1 }, /* EQN_FWD */
+       { { "up", 2 }, eqn_do_ign1 }, /* EQN_UP */
+       { { "down", 4 }, eqn_do_ign1 }, /* EQN_DOWN */
+};
+
+static const struct eqnstr eqnmarks[EQNMARK__MAX] = {
+       { "", 0 }, /* EQNMARK_NONE */
+       { "dot", 3 }, /* EQNMARK_DOT */
+       { "dotdot", 6 }, /* EQNMARK_DOTDOT */
+       { "hat", 3 }, /* EQNMARK_HAT */
+       { "tilde", 5 }, /* EQNMARK_TILDE */
+       { "vec", 3 }, /* EQNMARK_VEC */
+       { "dyad", 4 }, /* EQNMARK_DYAD */
+       { "bar", 3 }, /* EQNMARK_BAR */
+       { "under", 5 }, /* EQNMARK_UNDER */
+};
+
+static const struct eqnstr eqnfonts[EQNFONT__MAX] = {
+       { "", 0 }, /* EQNFONT_NONE */
+       { "roman", 5 }, /* EQNFONT_ROMAN */
+       { "bold", 4 }, /* EQNFONT_BOLD */
+       { "fat", 3 }, /* EQNFONT_FAT */
+       { "italic", 6 }, /* EQNFONT_ITALIC */
+};
+
+static const struct eqnstr eqnposs[EQNPOS__MAX] = {
+       { "", 0 }, /* EQNPOS_NONE */
+       { "over", 4 }, /* EQNPOS_OVER */
+       { "sup", 3 }, /* EQNPOS_SUP */
+       { "sub", 3 }, /* EQNPOS_SUB */
+       { "to", 2 }, /* EQNPOS_TO */
+       { "from", 4 }, /* EQNPOS_FROM */
+};
+
+static const struct eqnstr eqnpiles[EQNPILE__MAX] = {
+       { "", 0 }, /* EQNPILE_NONE */
+       { "pile", 4 }, /* EQNPILE_PILE */
+       { "cpile", 5 }, /* EQNPILE_CPILE */
+       { "rpile", 5 }, /* EQNPILE_RPILE */
+       { "lpile", 5 }, /* EQNPILE_LPILE */
+       { "col", 3 }, /* EQNPILE_COL */
+       { "ccol", 4 }, /* EQNPILE_CCOL */
+       { "rcol", 4 }, /* EQNPILE_RCOL */
+       { "lcol", 4 }, /* EQNPILE_LCOL */
+};
+
+static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
+       { { "alpha", 5 }, "*a" }, /* EQNSYM_alpha */
+       { { "beta", 4 }, "*b" }, /* EQNSYM_beta */
+       { { "chi", 3 }, "*x" }, /* EQNSYM_chi */
+       { { "delta", 5 }, "*d" }, /* EQNSYM_delta */
+       { { "epsilon", 7 }, "*e" }, /* EQNSYM_epsilon */
+       { { "eta", 3 }, "*y" }, /* EQNSYM_eta */
+       { { "gamma", 5 }, "*g" }, /* EQNSYM_gamma */
+       { { "iota", 4 }, "*i" }, /* EQNSYM_iota */
+       { { "kappa", 5 }, "*k" }, /* EQNSYM_kappa */
+       { { "lambda", 6 }, "*l" }, /* EQNSYM_lambda */
+       { { "mu", 2 }, "*m" }, /* EQNSYM_mu */
+       { { "nu", 2 }, "*n" }, /* EQNSYM_nu */
+       { { "omega", 5 }, "*w" }, /* EQNSYM_omega */
+       { { "omicron", 7 }, "*o" }, /* EQNSYM_omicron */
+       { { "phi", 3 }, "*f" }, /* EQNSYM_phi */
+       { { "pi", 2 }, "*p" }, /* EQNSYM_pi */
+       { { "psi", 2 }, "*q" }, /* EQNSYM_psi */
+       { { "rho", 3 }, "*r" }, /* EQNSYM_rho */
+       { { "sigma", 5 }, "*s" }, /* EQNSYM_sigma */
+       { { "tau", 3 }, "*t" }, /* EQNSYM_tau */
+       { { "theta", 5 }, "*h" }, /* EQNSYM_theta */
+       { { "upsilon", 7 }, "*u" }, /* EQNSYM_upsilon */
+       { { "xi", 2 }, "*c" }, /* EQNSYM_xi */
+       { { "zeta", 4 }, "*z" }, /* EQNSYM_zeta */
+       { { "DELTA", 5 }, "*D" }, /* EQNSYM_DELTA */
+       { { "GAMMA", 5 }, "*G" }, /* EQNSYM_GAMMA */
+       { { "LAMBDA", 6 }, "*L" }, /* EQNSYM_LAMBDA */
+       { { "OMEGA", 5 }, "*W" }, /* EQNSYM_OMEGA */
+       { { "PHI", 3 }, "*F" }, /* EQNSYM_PHI */
+       { { "PI", 2 }, "*P" }, /* EQNSYM_PI */
+       { { "PSI", 3 }, "*Q" }, /* EQNSYM_PSI */
+       { { "SIGMA", 5 }, "*S" }, /* EQNSYM_SIGMA */
+       { { "THETA", 5 }, "*H" }, /* EQNSYM_THETA */
+       { { "UPSILON", 7 }, "*U" }, /* EQNSYM_UPSILON */
+       { { "XI", 2 }, "*C" }, /* EQNSYM_XI */
+       { { "inter", 5 }, "ca" }, /* EQNSYM_inter */
+       { { "union", 5 }, "cu" }, /* EQNSYM_union */
+       { { "prod", 4 }, "product" }, /* EQNSYM_prod */
+       { { "int", 3 }, "integral" }, /* EQNSYM_int */
+       { { "sum", 3 }, "sum" }, /* EQNSYM_sum */
+       { { "grad", 4 }, "gr" }, /* EQNSYM_grad */
+       { { "del", 3 }, "gr" }, /* EQNSYM_del */
+       { { "times", 5 }, "mu" }, /* EQNSYM_times */
+       { { "cdot", 4 }, "pc" }, /* EQNSYM_cdot */
+       { { "nothing", 7 }, "&" }, /* EQNSYM_nothing */
+       { { "approx", 6 }, "~~" }, /* EQNSYM_approx */
+       { { "prime", 5 }, "aq" }, /* EQNSYM_prime */
+       { { "half", 4 }, "12" }, /* EQNSYM_half */
+       { { "partial", 7 }, "pd" }, /* EQNSYM_partial */
+       { { "inf", 3 }, "if" }, /* EQNSYM_inf */
+       { { ">>", 2 }, ">>" }, /* EQNSYM_muchgreat */
+       { { "<<", 2 }, "<<" }, /* EQNSYM_muchless */
+       { { "<-", 2 }, "<-" }, /* EQNSYM_larrow */
+       { { "->", 2 }, "->" }, /* EQNSYM_rarrow */
+       { { "+-", 2 }, "+-" }, /* EQNSYM_pm */
+       { { "!=", 2 }, "!=" }, /* EQNSYM_nequal */
+       { { "==", 2 }, "==" }, /* EQNSYM_equiv */
+       { { "<=", 2 }, "<=" }, /* EQNSYM_lessequal */
+       { { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */
+};
+
 /* ARGSUSED */
 enum rofferr
-eqn_read(struct eqn_node **epp, int ln, const char *p, int offs)
+eqn_read(struct eqn_node **epp, int ln, 
+               const char *p, int pos, int *offs)
 {
        size_t           sz;
        struct eqn_node *ep;
+       enum rofferr     er;
+
+       ep = *epp;
 
-       if (0 == strcmp(p, ".EN")) {
-               *epp = NULL;
-               return(ROFF_EQN);
+       /*
+        * If we're the terminating mark, unset our equation status and
+        * validate the full equation.
+        */
+
+       if (0 == strncmp(p, ".EN", 3)) {
+               er = eqn_end(epp);
+               p += 3;
+               while (' ' == *p || '\t' == *p)
+                       p++;
+               if ('\0' == *p) 
+                       return(er);
+               mandoc_msg(MANDOCERR_ARGSLOST, ep->parse, ln, pos, NULL);
+               return(er);
        }
 
-       ep = *epp;
+       /*
+        * Build up the full string, replacing all newlines with regular
+        * whitespace.
+        */
+
+       sz = strlen(p + pos) + 1;
+       ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
 
-       sz = strlen(&p[offs]);
-       ep->eqn.data = mandoc_realloc(ep->eqn.data, ep->eqn.sz + sz + 1);
-       if (0 == ep->eqn.sz)
-               *ep->eqn.data = '\0';
+       /* First invocation: nil terminate the string. */
 
-       ep->eqn.sz += sz;
-       strlcat(ep->eqn.data, &p[offs], ep->eqn.sz + 1);
+       if (0 == ep->sz)
+               *ep->data = '\0';
+
+       ep->sz += sz;
+       strlcat(ep->data, p + pos, ep->sz + 1);
+       strlcat(ep->data, " ", ep->sz + 1);
        return(ROFF_IGN);
 }
 
 struct eqn_node *
-eqn_alloc(int pos, int line)
+eqn_alloc(const char *name, int pos, int line, struct mparse *parse)
 {
        struct eqn_node *p;
+       size_t           sz;
+       const char      *end;
 
        p = mandoc_calloc(1, sizeof(struct eqn_node));
-       p->eqn.line = line;
+
+       if (name && '\0' != *name) {
+               sz = strlen(name);
+               assert(sz);
+               do {
+                       sz--;
+                       end = name + (int)sz;
+               } while (' ' == *end || '\t' == *end);
+               p->eqn.name = mandoc_strndup(name, sz + 1);
+       }
+
+       p->parse = parse;
+       p->eqn.ln = line;
        p->eqn.pos = pos;
+       p->gsize = EQN_DEFSIZE;
 
        return(p);
 }
 
-/* ARGSUSED */
-void
-eqn_end(struct eqn_node *e)
+enum rofferr
+eqn_end(struct eqn_node **epp)
+{
+       struct eqn_node *ep;
+       struct eqn_box  *root;
+       enum eqn_rest    c;
+
+       ep = *epp;
+       *epp = NULL;
+
+       ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
+
+       root = ep->eqn.root;
+       root->type = EQN_ROOT;
+
+       if (0 == ep->sz)
+               return(ROFF_IGN);
+
+       if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) {
+               EQN_MSG(MANDOCERR_EQNNSCOPE, ep);
+               c = EQN_ERR;
+       }
+
+       return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN);
+}
+
+static enum eqn_rest
+eqn_eqn(struct eqn_node *ep, struct eqn_box *last)
 {
+       struct eqn_box  *bp;
+       enum eqn_rest    c;
+
+       bp = eqn_box_alloc(ep, last);
+       bp->type = EQN_SUBEXPR;
 
-       /* Nothing to do. */
+       while (EQN_OK == (c = eqn_box(ep, bp)))
+               /* Spin! */ ;
+
+       return(c);
+}
+
+static enum eqn_rest
+eqn_matrix(struct eqn_node *ep, struct eqn_box *last)
+{
+       struct eqn_box  *bp;
+       const char      *start;
+       size_t           sz;
+       enum eqn_rest    c;
+
+       bp = eqn_box_alloc(ep, last);
+       bp->type = EQN_MATRIX;
+
+       if (NULL == (start = eqn_nexttok(ep, &sz))) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(EQN_ERR);
+       }
+       if ( ! STRNEQ(start, sz, "{", 1)) {
+               EQN_MSG(MANDOCERR_EQNSYNT, ep);
+               return(EQN_ERR);
+       }
+
+       while (EQN_OK == (c = eqn_box(ep, bp)))
+               switch (bp->last->pile) {
+               case (EQNPILE_LCOL):
+                       /* FALLTHROUGH */
+               case (EQNPILE_CCOL):
+                       /* FALLTHROUGH */
+               case (EQNPILE_RCOL):
+                       continue;
+               default:
+                       EQN_MSG(MANDOCERR_EQNSYNT, ep);
+                       return(EQN_ERR);
+               };
+
+       if (EQN_DESCOPE != c) {
+               if (EQN_EOF == c)
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(EQN_ERR);
+       }
+
+       eqn_rewind(ep);
+       start = eqn_nexttok(ep, &sz);
+       assert(start);
+       if (STRNEQ(start, sz, "}", 1))
+               return(EQN_OK);
+
+       EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
+       return(EQN_ERR);
+}
+
+static enum eqn_rest
+eqn_list(struct eqn_node *ep, struct eqn_box *last)
+{
+       struct eqn_box  *bp;
+       const char      *start;
+       size_t           sz;
+       enum eqn_rest    c;
+
+       bp = eqn_box_alloc(ep, last);
+       bp->type = EQN_LIST;
+
+       if (NULL == (start = eqn_nexttok(ep, &sz))) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(EQN_ERR);
+       }
+       if ( ! STRNEQ(start, sz, "{", 1)) {
+               EQN_MSG(MANDOCERR_EQNSYNT, ep);
+               return(EQN_ERR);
+       }
+
+       while (EQN_DESCOPE == (c = eqn_eqn(ep, bp))) {
+               eqn_rewind(ep);
+               start = eqn_nexttok(ep, &sz);
+               assert(start);
+               if ( ! STRNEQ(start, sz, "above", 5))
+                       break;
+       }
+
+       if (EQN_DESCOPE != c) {
+               if (EQN_ERR != c)
+                       EQN_MSG(MANDOCERR_EQNSCOPE, ep);
+               return(EQN_ERR);
+       }
+
+       eqn_rewind(ep);
+       start = eqn_nexttok(ep, &sz);
+       assert(start);
+       if (STRNEQ(start, sz, "}", 1))
+               return(EQN_OK);
+
+       EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
+       return(EQN_ERR);
+}
+
+static enum eqn_rest
+eqn_box(struct eqn_node *ep, struct eqn_box *last)
+{
+       size_t           sz;
+       const char      *start;
+       char            *left;
+       char             sym[64];
+       enum eqn_rest    c;
+       int              i, size;
+       struct eqn_box  *bp;
+
+       if (NULL == (start = eqn_nexttok(ep, &sz)))
+               return(EQN_EOF);
+
+       if (STRNEQ(start, sz, "}", 1))
+               return(EQN_DESCOPE);
+       else if (STRNEQ(start, sz, "right", 5))
+               return(EQN_DESCOPE);
+       else if (STRNEQ(start, sz, "above", 5))
+               return(EQN_DESCOPE);
+       else if (STRNEQ(start, sz, "mark", 4))
+               return(EQN_OK);
+       else if (STRNEQ(start, sz, "lineup", 6))
+               return(EQN_OK);
+
+       for (i = 0; i < (int)EQN__MAX; i++) {
+               if ( ! EQNSTREQ(&eqnparts[i].str, start, sz))
+                       continue;
+               return((*eqnparts[i].fp)(ep) ? 
+                               EQN_OK : EQN_ERR);
+       } 
+
+       if (STRNEQ(start, sz, "{", 1)) {
+               if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) {
+                       if (EQN_ERR != c)
+                               EQN_MSG(MANDOCERR_EQNSCOPE, ep);
+                       return(EQN_ERR);
+               }
+               eqn_rewind(ep);
+               start = eqn_nexttok(ep, &sz);
+               assert(start);
+               if (STRNEQ(start, sz, "}", 1))
+                       return(EQN_OK);
+               EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
+               return(EQN_ERR);
+       } 
+
+       for (i = 0; i < (int)EQNPILE__MAX; i++) {
+               if ( ! EQNSTREQ(&eqnpiles[i], start, sz))
+                       continue;
+               if (EQN_OK == (c = eqn_list(ep, last)))
+                       last->last->pile = (enum eqn_pilet)i;
+               return(c);
+       }
+
+       if (STRNEQ(start, sz, "matrix", 6))
+               return(eqn_matrix(ep, last));
+
+       if (STRNEQ(start, sz, "left", 4)) {
+               if (NULL == (start = eqn_nexttok(ep, &sz))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               }
+               left = mandoc_strndup(start, sz);
+               c = eqn_eqn(ep, last);
+               if (last->last)
+                       last->last->left = left;
+               else
+                       free(left);
+               if (EQN_DESCOPE != c)
+                       return(c);
+               assert(last->last);
+               eqn_rewind(ep);
+               start = eqn_nexttok(ep, &sz);
+               assert(start);
+               if ( ! STRNEQ(start, sz, "right", 5))
+                       return(EQN_DESCOPE);
+               if (NULL == (start = eqn_nexttok(ep, &sz))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               }
+               last->last->right = mandoc_strndup(start, sz);
+               return(EQN_OK);
+       }
+
+       for (i = 0; i < (int)EQNPOS__MAX; i++) {
+               if ( ! EQNSTREQ(&eqnposs[i], start, sz))
+                       continue;
+               if (NULL == last->last) {
+                       EQN_MSG(MANDOCERR_EQNSYNT, ep);
+                       return(EQN_ERR);
+               } 
+               last->last->pos = (enum eqn_post)i;
+               if (EQN_EOF == (c = eqn_box(ep, last))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               }
+               return(c);
+       }
+
+       for (i = 0; i < (int)EQNMARK__MAX; i++) {
+               if ( ! EQNSTREQ(&eqnmarks[i], start, sz))
+                       continue;
+               if (NULL == last->last) {
+                       EQN_MSG(MANDOCERR_EQNSYNT, ep);
+                       return(EQN_ERR);
+               } 
+               last->last->mark = (enum eqn_markt)i;
+               if (EQN_EOF == (c = eqn_box(ep, last))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               }
+               return(c);
+       }
+
+       for (i = 0; i < (int)EQNFONT__MAX; i++) {
+               if ( ! EQNSTREQ(&eqnfonts[i], start, sz))
+                       continue;
+               if (EQN_EOF == (c = eqn_box(ep, last))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               } else if (EQN_OK == c)
+                       last->last->font = (enum eqn_fontt)i;
+               return(c);
+       }
+
+       if (STRNEQ(start, sz, "size", 4)) {
+               if (NULL == (start = eqn_nexttok(ep, &sz))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               }
+               size = mandoc_strntoi(start, sz, 10);
+               if (EQN_EOF == (c = eqn_box(ep, last))) {
+                       EQN_MSG(MANDOCERR_EQNEOF, ep);
+                       return(EQN_ERR);
+               } else if (EQN_OK != c)
+                       return(c);
+               last->last->size = size;
+       }
+
+       bp = eqn_box_alloc(ep, last);
+       bp->type = EQN_TEXT;
+       for (i = 0; i < (int)EQNSYM__MAX; i++)
+               if (EQNSTREQ(&eqnsyms[i].str, start, sz)) {
+                       sym[63] = '\0';
+                       snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym);
+                       bp->text = mandoc_strdup(sym);
+                       return(EQN_OK);
+               }
+
+       bp->text = mandoc_strndup(start, sz);
+       return(EQN_OK);
 }
 
 void
 eqn_free(struct eqn_node *p)
 {
+       int              i;
+
+       eqn_box_free(p->eqn.root);
+
+       for (i = 0; i < (int)p->defsz; i++) {
+               free(p->defs[i].key);
+               free(p->defs[i].val);
+       }
 
-       free(p->eqn.data);
+       free(p->eqn.name);
+       free(p->data);
+       free(p->defs);
        free(p);
 }
+
+static struct eqn_box *
+eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
+{
+       struct eqn_box  *bp;
+
+       bp = mandoc_calloc(1, sizeof(struct eqn_box));
+       bp->parent = parent;
+       bp->size = ep->gsize;
+
+       if (NULL == parent->first)
+               parent->first = bp;
+       else
+               parent->last->next = bp;
+
+       parent->last = bp;
+       return(bp);
+}
+
+static void
+eqn_box_free(struct eqn_box *bp)
+{
+
+       if (bp->first)
+               eqn_box_free(bp->first);
+       if (bp->next)
+               eqn_box_free(bp->next);
+
+       free(bp->text);
+       free(bp->left);
+       free(bp->right);
+       free(bp);
+}
+
+static const char *
+eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
+{
+
+       return(eqn_next(ep, '"', sz, 0));
+}
+
+static const char *
+eqn_nexttok(struct eqn_node *ep, size_t *sz)
+{
+
+       return(eqn_next(ep, '"', sz, 1));
+}
+
+static void
+eqn_rewind(struct eqn_node *ep)
+{
+
+       ep->cur = ep->rew;
+}
+
+static const char *
+eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
+{
+       char            *start, *next;
+       int              q, diff, lim;
+       size_t           ssz, dummy;
+       struct eqn_def  *def;
+
+       if (NULL == sz)
+               sz = &dummy;
+
+       lim = 0;
+       ep->rew = ep->cur;
+again:
+       /* Prevent self-definitions. */
+
+       if (lim >= EQN_NEST_MAX) {
+               EQN_MSG(MANDOCERR_ROFFLOOP, ep);
+               return(NULL);
+       }
+
+       ep->cur = ep->rew;
+       start = &ep->data[(int)ep->cur];
+       q = 0;
+
+       if ('\0' == *start)
+               return(NULL);
+
+       if (quote == *start) {
+               ep->cur++;
+               q = 1;
+       }
+
+       start = &ep->data[(int)ep->cur];
+
+       if ( ! q) {
+               if ('{' == *start || '}' == *start)
+                       ssz = 1;
+               else
+                       ssz = strcspn(start + 1, " ^~\"{}\t") + 1;
+               next = start + (int)ssz;
+               if ('\0' == *next)
+                       next = NULL;
+       } else
+               next = strchr(start, quote);
+
+       if (NULL != next) {
+               *sz = (size_t)(next - start);
+               ep->cur += *sz;
+               if (q)
+                       ep->cur++;
+               while (' ' == ep->data[(int)ep->cur] ||
+                               '\t' == ep->data[(int)ep->cur] ||
+                               '^' == ep->data[(int)ep->cur] ||
+                               '~' == ep->data[(int)ep->cur])
+                       ep->cur++;
+       } else {
+               if (q)
+                       EQN_MSG(MANDOCERR_BADQUOTE, ep);
+               next = strchr(start, '\0');
+               *sz = (size_t)(next - start);
+               ep->cur += *sz;
+       }
+
+       /* Quotes aren't expanded for values. */
+
+       if (q || ! repl)
+               return(start);
+
+       if (NULL != (def = eqn_def_find(ep, start, *sz))) {
+               diff = def->valsz - *sz;
+
+               if (def->valsz > *sz) {
+                       ep->sz += diff;
+                       ep->data = mandoc_realloc(ep->data, ep->sz + 1);
+                       ep->data[ep->sz] = '\0';
+                       start = &ep->data[(int)ep->rew];
+               }
+
+               diff = def->valsz - *sz;
+               memmove(start + *sz + diff, start + *sz, 
+                               (strlen(start) - *sz) + 1);
+               memcpy(start, def->val, def->valsz);
+               goto again;
+       }
+
+       return(start);
+}
+
+static int
+eqn_do_ign1(struct eqn_node *ep)
+{
+
+       if (NULL == eqn_nextrawtok(ep, NULL))
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+       else
+               return(1);
+
+       return(0);
+}
+
+static int
+eqn_do_ign2(struct eqn_node *ep)
+{
+
+       if (NULL == eqn_nextrawtok(ep, NULL))
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+       else if (NULL == eqn_nextrawtok(ep, NULL))
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+       else
+               return(1);
+
+       return(0);
+}
+
+static int
+eqn_do_tdefine(struct eqn_node *ep)
+{
+
+       if (NULL == eqn_nextrawtok(ep, NULL))
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+       else if (NULL == eqn_next(ep, ep->data[(int)ep->cur], NULL, 0))
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+       else
+               return(1);
+
+       return(0);
+}
+
+static int
+eqn_do_define(struct eqn_node *ep)
+{
+       const char      *start;
+       size_t           sz;
+       struct eqn_def  *def;
+       int              i;
+
+       if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(0);
+       }
+
+       /* 
+        * Search for a key that already exists. 
+        * Create a new key if none is found.
+        */
+
+       if (NULL == (def = eqn_def_find(ep, start, sz))) {
+               /* Find holes in string array. */
+               for (i = 0; i < (int)ep->defsz; i++)
+                       if (0 == ep->defs[i].keysz)
+                               break;
+
+               if (i == (int)ep->defsz) {
+                       ep->defsz++;
+                       ep->defs = mandoc_realloc
+                               (ep->defs, ep->defsz * 
+                                sizeof(struct eqn_def));
+                       ep->defs[i].key = ep->defs[i].val = NULL;
+               }
+
+               ep->defs[i].keysz = sz;
+               ep->defs[i].key = mandoc_realloc
+                       (ep->defs[i].key, sz + 1);
+
+               memcpy(ep->defs[i].key, start, sz);
+               ep->defs[i].key[(int)sz] = '\0';
+               def = &ep->defs[i];
+       }
+
+       start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
+
+       if (NULL == start) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(0);
+       }
+
+       def->valsz = sz;
+       def->val = mandoc_realloc(def->val, sz + 1);
+       memcpy(def->val, start, sz);
+       def->val[(int)sz] = '\0';
+       return(1);
+}
+
+static int
+eqn_do_gfont(struct eqn_node *ep)
+{
+
+       if (NULL == eqn_nextrawtok(ep, NULL)) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(0);
+       } 
+       return(1);
+}
+
+static int
+eqn_do_gsize(struct eqn_node *ep)
+{
+       const char      *start;
+       size_t           sz;
+
+       if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(0);
+       } 
+       ep->gsize = mandoc_strntoi(start, sz, 10);
+       return(1);
+}
+
+static int
+eqn_do_undef(struct eqn_node *ep)
+{
+       const char      *start;
+       struct eqn_def  *def;
+       size_t           sz;
+
+       if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
+               EQN_MSG(MANDOCERR_EQNEOF, ep);
+               return(0);
+       } else if (NULL != (def = eqn_def_find(ep, start, sz)))
+               def->keysz = 0;
+
+       return(1);
+}
+
+static struct eqn_def *
+eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
+{
+       int              i;
+
+       for (i = 0; i < (int)ep->defsz; i++) 
+               if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, 
+                                       ep->defs[i].keysz, key, sz))
+                       return(&ep->defs[i]);
+
+       return(NULL);
+}
diff --git a/contrib/mdocml/eqn_html.c b/contrib/mdocml/eqn_html.c
new file mode 100644 (file)
index 0000000..80c82f1
--- /dev/null
@@ -0,0 +1,81 @@
+/*     $Id: eqn_html.c,v 1.2 2011/07/24 10:09:03 kristaps Exp $ */
+/*
+ * Copyright (c) 2011 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 "out.h"
+#include "html.h"
+
+static const enum htmltag fontmap[EQNFONT__MAX] = {
+       TAG_SPAN, /* EQNFONT_NONE */
+       TAG_SPAN, /* EQNFONT_ROMAN */
+       TAG_B, /* EQNFONT_BOLD */
+       TAG_B, /* EQNFONT_FAT */
+       TAG_I /* EQNFONT_ITALIC */
+};
+
+
+static void    eqn_box(struct html *, const struct eqn_box *);
+
+void
+print_eqn(struct html *p, const struct eqn *ep)
+{
+       struct htmlpair  tag;
+       struct tag      *t;
+
+       PAIR_CLASS_INIT(&tag, "eqn");
+       t = print_otag(p, TAG_SPAN, 1, &tag);
+
+       p->flags |= HTML_NONOSPACE;
+       eqn_box(p, ep->root);
+       p->flags &= ~HTML_NONOSPACE;
+
+       print_tagq(p, t);
+}
+
+static void
+eqn_box(struct html *p, const struct eqn_box *bp)
+{
+       struct tag      *t;
+
+       t = EQNFONT_NONE == bp->font ? NULL : 
+               print_otag(p, fontmap[(int)bp->font], 0, NULL);
+
+       if (bp->left)
+               print_text(p, bp->left);
+       
+       if (bp->text)
+               print_text(p, bp->text);
+
+       if (bp->first)
+               eqn_box(p, bp->first);
+
+       if (NULL != t)
+               print_tagq(p, t);
+       if (bp->right)
+               print_text(p, bp->right);
+
+       if (bp->next)
+               eqn_box(p, bp->next);
+}
diff --git a/contrib/mdocml/eqn_term.c b/contrib/mdocml/eqn_term.c
new file mode 100644 (file)
index 0000000..cfbd8d4
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $Id: eqn_term.c,v 1.4 2011/07/24 10:09:03 kristaps Exp $ */
+/*
+ * Copyright (c) 2011 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 "out.h"
+#include "term.h"
+
+static const enum termfont fontmap[EQNFONT__MAX] = {
+       TERMFONT_NONE, /* EQNFONT_NONE */
+       TERMFONT_NONE, /* EQNFONT_ROMAN */
+       TERMFONT_BOLD, /* EQNFONT_BOLD */
+       TERMFONT_BOLD, /* EQNFONT_FAT */
+       TERMFONT_UNDER /* EQNFONT_ITALIC */
+};
+
+static void    eqn_box(struct termp *, const struct eqn_box *);
+
+void
+term_eqn(struct termp *p, const struct eqn *ep)
+{
+
+       p->flags |= TERMP_NONOSPACE;
+       eqn_box(p, ep->root);
+       term_word(p, " ");
+       p->flags &= ~TERMP_NONOSPACE;
+}
+
+static void
+eqn_box(struct termp *p, const struct eqn_box *bp)
+{
+
+       if (EQNFONT_NONE != bp->font)
+               term_fontpush(p, fontmap[(int)bp->font]);
+       if (bp->left)
+               term_word(p, bp->left);
+       if (EQN_SUBEXPR == bp->type)
+               term_word(p, "(");
+
+       if (bp->text)
+               term_word(p, bp->text);
+
+       if (bp->first)
+               eqn_box(p, bp->first);
+
+       if (EQN_SUBEXPR == bp->type)
+               term_word(p, ")");
+       if (bp->right)
+               term_word(p, bp->right);
+       if (EQNFONT_NONE != bp->font) 
+               term_fontpop(p);
+
+       if (bp->next)
+               eqn_box(p, bp->next);
+}
index 3907546..660f4d1 100644 (file)
-/* $Id: example.style.css,v 1.43 2011/04/11 22:58:28 kristaps Exp $ */
-
+/* $Id: example.style.css,v 1.49 2011/12/15 12:18:57 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.
- *
+ * It mimics the appearance of the legacy man.cgi output.
  * See mdoc(7) and man(7) for macro explanations.
  */
 
-html           { min-width: 580px; width: 580px; }
-body           { font-family: monospace; }
-h1             { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */
-h2             { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
-table          { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */
-td             { vertical-align: top; } /* All table cells. */
-p              { } /* Paragraph: Pp, Lp. */
-blockquote     { margin-top: 0ex; margin-bottom: 0ex; } /* D1. */
-div.section    { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */
-div.subsection { } /* Sub-sections (Ss, SS). */
-table.synopsis { } /* SYNOPSIS section table. */
-
-/* Preamble structure. */
-
-table.foot     { } /* Document footer. */
-td.foot-date   { width: 50%; } /* Document footer: date. */
-td.foot-os     { width: 50%; text-align: right; } /* Document footer: OS/source. */
-table.head     { } /* 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. */
-
-/* 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: 0ex; }
-
-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   { }
-
-/* Equation modes.  See eqn(7). */
-
-span.eqn       { }
-
-/* Table modes.  See tbl(7). */
-
-table.tbl      { }
+div.mandoc                     { min-width: 102ex; 
+                                 width: 102ex; 
+                                 font-family: monospace; } /* This is the outer node of all mandoc -T[x]html documents. */
+div.mandoc h1                  { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */
+div.mandoc h2                  { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
+div.mandoc table               { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */
+div.mandoc td                  { vertical-align: top; } /* All table cells. */
+div.mandoc p                   { } /* Paragraph: Pp, Lp. */
+div.mandoc blockquote          { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1, Dl. */
+div.mandoc div.section         { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */
+div.mandoc div.subsection      { } /* Sub-sections (Ss, SS). */
+div.mandoc table.synopsis      { } /* SYNOPSIS section table. */
+div.mandoc table.foot          { } /* Document footer. */
+div.mandoc td.foot-date                { width: 50%; } /* Document footer: date. */
+div.mandoc td.foot-os          { width: 50%; text-align: right; } /* Document footer: OS/source. */
+div.mandoc table.head          { } /* Document header. */
+div.mandoc td.head-ltitle      { width: 10%; } /* Document header: left-title. */
+div.mandoc td.head-vol         { width: 80%; text-align: center; } /* Document header: volume. */
+div.mandoc td.head-rtitle      { width: 10%; text-align: right; } /* Document header: right-title. */
+div.mandoc .display            { } /* All Bd, D1, Dl. */
+div.mandoc .list               { } /* All Bl. */
+div.mandoc i                   { } /* Italic: BI, IB, I, (implicit). */
+div.mandoc b                   { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */
+div.mandoc small               { } /* Small: SB, SM. */
+div.mandoc .emph               { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */
+div.mandoc .symb               { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */
+div.mandoc .lit                        { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */
+div.mandoc i.addr              { font-weight: normal; } /* Address (Ad). */
+div.mandoc i.arg               { font-weight: normal; } /* Command argument (Ar). */
+div.mandoc span.author         { } /* Author name (An). */
+div.mandoc b.cmd               { font-style: normal; } /* Command (Cm). */ 
+div.mandoc b.config            { font-style: normal; } /* Config statement (Cd). */
+div.mandoc span.define         { } /* Defines (Dv). */
+div.mandoc span.desc           { } /* Nd.  After em-dash. */
+div.mandoc b.diag              { font-style: normal; } /* Diagnostic (Bl -diag). */
+div.mandoc span.env            { } /* Environment variables (Ev). */
+div.mandoc span.errno          { } /* Error string (Er). */
+div.mandoc i.farg              { font-weight: normal; } /* Function argument (Fa, Fn). */
+div.mandoc i.file              { font-weight: normal; } /* File (Pa). */
+div.mandoc b.flag              { font-style: normal; } /* Flag (Fl, Cm). */
+div.mandoc b.fname             { font-style: normal; } /* Function name (Fa, Fn, Rv). */
+div.mandoc i.ftype             { font-weight: normal; } /* Function types (Ft, Fn). */
+div.mandoc b.includes          { font-style: normal; } /* Header includes (In). */
+div.mandoc span.lib            { } /* Library (Lb). */
+div.mandoc i.link-sec          { font-weight: normal; } /* Section links (Sx). */
+div.mandoc b.macro             { font-style: normal; } /* Macro-ish thing (Fd). */
+div.mandoc b.name              { font-style: normal; } /* Name of utility (Nm). */
+div.mandoc span.opt            { } /* Options (Op, Oo/Oc). */
+div.mandoc span.ref            { } /* Citations (Rs). */
+div.mandoc span.ref-auth       { } /* Reference author (%A). */
+div.mandoc i.ref-book          { font-weight: normal; } /* Reference book (%B). */
+div.mandoc span.ref-city       { } /* Reference city (%C). */
+div.mandoc span.ref-date       { } /* Reference date (%D). */
+div.mandoc i.ref-issue         { font-weight: normal; } /* Reference issuer/publisher (%I). */
+div.mandoc i.ref-jrnl          { font-weight: normal; } /* Reference journal (%J). */
+div.mandoc span.ref-num                { } /* Reference number (%N). */
+div.mandoc span.ref-opt                { } /* Reference optionals (%O). */
+div.mandoc span.ref-page       { } /* Reference page (%P). */
+div.mandoc span.ref-corp       { } /* Reference corporate/foreign author (%Q). */
+div.mandoc span.ref-rep                { } /* Reference report (%R). */
+div.mandoc span.ref-title      { text-decoration: underline; } /* Reference title (%T). */
+div.mandoc span.ref-vol                { } /* Reference volume (%V). */
+div.mandoc span.type           { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
+div.mandoc span.unix           { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
+div.mandoc b.utility           { font-style: normal; } /* Name of utility (Ex). */
+div.mandoc b.var               { font-style: normal; } /* Variables (Rv). */
+div.mandoc a.link-ext          { } /* Off-site link (Lk). */
+div.mandoc a.link-includes     { } /* Include-file link (In). */
+div.mandoc a.link-mail         { } /* Mailto links (Mt). */
+div.mandoc a.link-man          { } /* Manual links (Xr). */
+div.mandoc a.link-ref          { } /* Reference section links (%Q). */
+div.mandoc a.link-sec          { } /* Section links (Sx). */
+div.mandoc dl.list-diag                { } /* Formatting for lists.  See mdoc(7). */
+div.mandoc dt.list-diag                { }
+div.mandoc dd.list-diag                { }
+div.mandoc dl.list-hang                { }
+div.mandoc dt.list-hang                { }
+div.mandoc dd.list-hang                { }
+div.mandoc dl.list-inset       { }
+div.mandoc dt.list-inset       { }
+div.mandoc dd.list-inset       { }
+div.mandoc dl.list-ohang       { }
+div.mandoc dt.list-ohang       { }
+div.mandoc dd.list-ohang       { margin-left: 0ex; }
+div.mandoc dl.list-tag         { }
+div.mandoc dt.list-tag         { }
+div.mandoc dd.list-tag         { }
+div.mandoc table.list-col      { }
+div.mandoc tr.list-col         { }
+div.mandoc td.list-col         { }
+div.mandoc ul.list-bul         { list-style-type: disc; padding-left: 1em; }
+div.mandoc li.list-bul         { }
+div.mandoc ul.list-dash                { list-style-type: none; padding-left: 0em; }
+div.mandoc li.list-dash:before { content: "\2014  "; }
+div.mandoc ul.list-hyph                { list-style-type: none; padding-left: 0em; }
+div.mandoc li.list-hyph:before { content: "\2013  "; }
+div.mandoc ul.list-item                { list-style-type: none; padding-left: 0em; }
+div.mandoc li.list-item                { }
+div.mandoc ol.list-enum                { padding-left: 2em; }
+div.mandoc li.list-enum                { }
+div.mandoc span.eqn            { } /* Equation modes.  See eqn(7). */
+div.mandoc table.tbl           { } /* Table modes.  See tbl(7). */
index 45471fe..326df03 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: html.c,v 1.147 2011/05/24 21:40:14 kristaps Exp $ */
+/*     $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -118,13 +118,14 @@ static void *
 ml_alloc(char *outopts, enum htmltype type)
 {
        struct html     *h;
-       const char      *toks[4];
+       const char      *toks[5];
        char            *v;
 
        toks[0] = "style";
        toks[1] = "man";
        toks[2] = "includes";
-       toks[3] = NULL;
+       toks[3] = "fragment";
+       toks[4] = NULL;
 
        h = mandoc_calloc(1, sizeof(struct html));
 
@@ -143,6 +144,9 @@ ml_alloc(char *outopts, enum htmltype type)
                case (2):
                        h->base_includes = v;
                        break;
+               case (3):
+                       h->oflags |= HTML_FRAGMENT;
+                       break;
                default:
                        break;
                }
@@ -513,9 +517,11 @@ print_text(struct html *h, const char *word)
                        print_otag(h, TAG_I, 0, NULL);
 
        assert(word);
-       if ( ! print_encode(h, word, 0))
+       if ( ! print_encode(h, word, 0)) {
                if ( ! (h->flags & HTML_NONOSPACE))
                        h->flags &= ~HTML_NOSPACE;
+       } else
+               h->flags |= HTML_NOSPACE;
 
        if (h->metaf) {
                print_tagq(h, h->metaf);
@@ -595,7 +601,6 @@ bufcat(struct html *h, const char *p)
 
        h->buflen = strlcat(h->buf, p, BUFSIZ);
        assert(h->buflen < BUFSIZ);
-       h->buflen--;
 }
 
 void
index aba635f..6096070 100644 (file)
@@ -1,6 +1,6 @@
-/*     $Id: html.h,v 1.44 2011/05/17 11:34:31 kristaps Exp $ */
+/*     $Id: html.h,v 1.47 2011/10/05 21:35:17 kristaps Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2008, 2009, 2010, 2011 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
@@ -104,7 +104,7 @@ struct      htmlpair {
 #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 {
+enum   htmltype { 
        HTML_HTML_4_01_STRICT,
        HTML_XHTML_1_0_STRICT
 };
@@ -125,11 +125,13 @@ struct    html {
        char             *base_includes; /* base for include href */
        char             *style; /* style-sheet URI */
        char              buf[BUFSIZ]; /* see bufcat and friends */
-       size_t            buflen;
+       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;
+       enum htmltype     type; /* output media type */
+       int               oflags; /* output options */
+#define        HTML_FRAGMENT    (1 << 0) /* don't emit HTML/HEAD/BODY */
 };
 
 void             print_gen_decls(struct html *);
@@ -141,6 +143,7 @@ void                  print_stagq(struct html *, const struct tag *);
 void             print_text(struct html *, const char *);
 void             print_tblclose(struct html *);
 void             print_tbl(struct html *, const struct tbl_span *);
+void             print_eqn(struct html *, const struct eqn *);
 
 void             bufcat_fmt(struct html *, const char *, ...);
 void             bufcat(struct html *, const char *);
index ce0898d..d98316e 100644 (file)
@@ -11,7 +11,9 @@ body          { text-align: justify;
 
 p,ul,table     { margin-left: 3em; }
 
-p.head, p.foot { margin-left: 0.0em; margin-right: 0.0em; }
+p.head, 
+p.subhead,
+p.foot         { margin-left: 0.0em; margin-right: 0.0em; }
 
 p.news         { margin-left: 2.0em; }
 
@@ -20,10 +22,13 @@ li          { margin: 0.25em; }
 h1             { font-size: 110%; }
 h2             { font-size: 105%; margin-left: 1.5em }
 
-p.head         { margin-bottom: 1.75em;
+p.head         { margin-bottom: 0.5em;
                  border-bottom: 1px solid #dddddd; 
                  padding-bottom: 0.2em; }
 
+p.subhead      { margin-top: 0em; 
+                 margin-bottom: 1.75em; }
+
 p.foot         { border-top: 1px solid #dddddd; 
                  color: #666666;
                  padding-top: 0.2em;
index fa7d8b4..4386a9e 100644 (file)
@@ -3,11 +3,16 @@
        <HEAD>
                <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
                <LINK REL="stylesheet" HREF="index.css" TYPE="text/css" MEDIA="all">
-               <TITLE>mdocml | mdoc macro compiler</TITLE>
+               <TITLE>mdocml | UNIX manpage compiler</TITLE>
        </HEAD>
        <BODY>
                <P CLASS="head">
-                       <B>mdocml</B> &#8211; mdoc macro compiler
+                       <B>mdocml</B> &#8211; UNIX manpage compiler, current version @VERSION@ (@VDATE@)
+               </P>
+               <P CLASS="subhead">
+                       Sources: <A HREF="/snapshots/mdocml.tar.gz">current</A>,
+                       <A HREF="/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A>
+                       (<A HREF="/snapshots/">archives</A>)
                </P>
                <H1>
                        <A NAME="description">Description</A>
                        <SPAN CLASS="nm">mdocml</SPAN> consists of the <A HREF="mandoc.3.html">libmandoc</A> validating compiler and <A
                        HREF="mandoc.1.html">mandoc</A>, which interfaces with the compiler library to format output for UNIX terminals (with
                        support for wide-character locales), XHTML, HTML, PostScript, and PDF.  
-                       It also includes <A HREF="preconv.1.html">preconv</A>, for recoding multibyte manuals; and <A
-                       HREF="makewhatis.1.html">makewhatis</A>, for indexing manuals.
+                       It also includes <A HREF="preconv.1.html">preconv</A>, for recoding multibyte manuals; 
+                       <A HREF="demandoc.1.html">demandoc</A>, for emitting only text parts of manuals;
+                       <A HREF="mandocdb.8.html">mandocdb</A>, for indexing manuals; and
+                       <A HREF="apropos.1.html">apropos</A>, <A HREF="whatis.1.html">whatis</A>, and
+                       <A HREF="man.cgi.7.html">man.cgi</A> (via <A HREF="catman.8.html">catman</A>) for semantic search of manual content.
                        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>
-               <H1>
+               <H2>
                        <A NAME="sources">Sources</A>
-               </H1>
-               <P>
-                       <SPAN CLASS="nm">mdocml</SPAN> is in plain-old ANSI C and should build and run on any UNIX system, although <A
-                       HREF="makewhatis.1.html">makewhatis</A> requires <A CLASS="external"
-                       HREF="http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html">Berkeley Database</A> (this is
-                       installed by default on all BSD operating systems).  
-                       To compile <SPAN CLASS="nm">mdocml</SPAN>, run <CODE>make</CODE>, then <CODE>make install</CODE> to install into
-                       <I>/usr/local</I>.
-                       Be aware: if you have an existing <A HREF="http://www.gnu.org/software/groff/" CLASS="external">groff</A> installation,
-                       this may overwrite its <B>preconv</B> binary.
-                       The <A HREF="makewhatis.1.html">makewhatis</A> utility is not yet linked to the build.  You must run <CODE>make
-                       makewhatis</CODE> to build it (it does not install).
-               </P>
+               </H2>
                <P>
-                       The most current version of <SPAN CLASS="nm">mdocml</SPAN> is <SPAN CLASS="attn">@VERSION@</SPAN>, dated <SPAN
-                       class="attn">@VDATE@</SPAN>.  
+                       <SPAN CLASS="nm">mdocml</SPAN> is in plain-old ANSI C and should build and run on any modern system; however, you'll
+                       need <A HREF="http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html">libdb</A> to build <A
+                       HREF="apropos.1.html">apropos</A>, <A HREF="whatis.1.html">whatis</A>, <A HREF="man.cgi.7.html">man.cgi</A>, <A
+                       HREF="catman.8.html">catman</A>, and <A HREF="mandocdb.8.html">mandocdb</A> (this is installed by default on BSD UNIX
+                       systems &mdash; see the <I>Makefile</I> if you're running Linux).  To build and install into <I>/usr/local/</I>, just
+                       run <CODE>make install</CODE>.  Be careful: the <B>preconv</B>, <B>apropos</B>, and <B>whatis</B> binary names are
+                       usually taken by existing utilities.
                </P>
-
                <H2>
-                       Current
+                       <A NAME="binaries">Binaries</A>
                </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> 
-                                       <SMALL>(<A HREF="/snapshots/mdocml.md5">md5</A>)</SMALL>
-                                       </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>
-
+               <P>
+                       Binary archives consist of pre-compiled binaries, manuals, and other necessary files.
+                       Universal (Mac OS X) binaries are compiled for the PCC, i386, and x86_64 architectures.
+                       Windows binaries are compiled with <A CLASS="external" HREF="http://www.mingw.org">MingW</A> for the 32-bit (i686) and
+                       64-bit (x86_64) architectures.
+               </P>
                <H2>
                        Downstream
                </H2>
-
+               <P>
+                       Several systems come bundled with <SPAN CLASS="nm">mdocml</SPAN> utilities.
+                       If your system does not appear below, the maintainers have not contacted me and it should not be considered
+                       <Q>official</Q>.
+                       Please <A HREF="#contact">contact us</A> if you plan on maintaining a downstream version!
+               </P>
                <TABLE WIDTH="100%" SUMMARY="Downstream Sources">
                        <COL WIDTH="175">
                        <COL>
                                <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>
+                                       <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>
+                                       <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>
+                                       <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> 
+                                       <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>pkgsrc</TD>
                                        <TD>
-                                       <A HREF="/snapshots/">/snapshots/</A> 
+                                       <A HREF="http://pkgsrc.se/textproc/mdocml" CLASS="external">textproc/mdocml</A> 
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD>Minix3</TD>
+                                       <TD>
+                                       <A HREF="http://git.minix3.org/?p=minix.git;a=tree;f=external/bsd/mdocml" CLASS="external">external/bsd/mdocml</A>
                                        </TD>
                                </TR>
                        </TBODY>
                </TABLE>
-
                <H1>
                        <A NAME="documentation">Documentation</A>
                </H1>
-
                <P>
-                       These manuals are generated automatically and refer to the current snapshot.
+                       These manuals are generated automatically and refer to the current release.
+                       They are the authoritative documentation for the <SPAN CLASS="nm">mdocml</SPAN> system.
                </P>
 
                <TABLE WIDTH="100%" SUMMARY="Documentation">
                        <COL WIDTH="175">
                        <COL>
                        <TBODY>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="apropos.1.html">apropos(1)</A></TD>
+                                       <TD VALIGN="top">
+                                               search the manual page database
+                                                       (<A HREF="apropos.1.txt">text</A> | 
+                                                       <A HREF="apropos.1.xhtml">xhtml</A> |
+                                                       <A HREF="apropos.1.pdf">pdf</A> |
+                                                       <A HREF="apropos.1.ps">ps</A>)
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="demandoc.1.html">demandoc(1)</A></TD>
+                                       <TD VALIGN="top">
+                                               emit only text of UNIX manuals
+                                                       (<A HREF="demandoc.1.txt">text</A> | 
+                                                       <A HREF="demandoc.1.xhtml">xhtml</A> |
+                                                       <A HREF="demandoc.1.pdf">pdf</A> |
+                                                       <A HREF="demandoc.1.ps">ps</A>)
+                                       </TD>
+                               </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="mandoc.1.html">mandoc(1)</A></TD>
                                        <TD VALIGN="top">
                                                format and display UNIX manuals
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
-                                       </TD>
-                               </TR>
-                               <TR>
-                                       <TD VALIGN="top"><A HREF="makewhatis.1.html">makewhatis(1)</A></TD>
-                                       <TD VALIGN="top">
-                                               index UNIX manuals
-                                               <SMALL>
-                                                       (<A HREF="makewhatis.1.txt">text</A> | 
-                                                       <A HREF="makewhatis.1.xhtml">xhtml</A> |
-                                                       <A HREF="makewhatis.1.pdf">pdf</A> |
-                                                       <A HREF="makewhatis.1.ps">postscript</A>)
-                                               </SMALL>
+                                                       <A HREF="mandoc.1.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="preconv.1.html">preconv(1)</A></TD>
                                        <TD VALIGN="top">
                                                recode multibyte UNIX manuals
-                                               <SMALL>
                                                        (<A HREF="preconv.1.txt">text</A> | 
                                                        <A HREF="preconv.1.xhtml">xhtml</A> |
                                                        <A HREF="preconv.1.pdf">pdf</A> |
-                                                       <A HREF="preconv.1.ps">postscript</A>)
-                                               </SMALL>
+                                                       <A HREF="preconv.1.ps">ps</A>)
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="whatis.1.html">whatis(1)</A></TD>
+                                       <TD VALIGN="top">
+                                               search the manual page database
+                                                       (<A HREF="whatis.1.txt">text</A> | 
+                                                       <A HREF="whatis.1.xhtml">xhtml</A> |
+                                                       <A HREF="whatis.1.pdf">pdf</A> |
+                                                       <A HREF="whatis.1.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="mandoc.3.html">mandoc(3)</A></TD>
                                        <TD VALIGN="top">
                                                mandoc macro compiler library
-                                               <SMALL>
                                                        (<A HREF="mandoc.3.txt">text</A> | 
                                                        <A HREF="mandoc.3.xhtml">xhtml</A> |
                                                        <A HREF="mandoc.3.pdf">pdf</A> |
-                                                       <A HREF="mandoc.3.ps">postscript</A>)
-                                               </SMALL>
+                                                       <A HREF="mandoc.3.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="man.7.html">man(7)</A></TD>
                                        <TD VALIGN="top">
                                                man language reference
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
+                                                       <A HREF="man.7.ps">ps</A>)
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="man.cgi.7.html">man.cgi(7)</A></TD>
+                                       <TD VALIGN="top">
+                                               cgi for manpage query and display
+                                                       (<A HREF="man.cgi.7.txt">text</A> | 
+                                                       <A HREF="man.cgi.7.xhtml">xhtml</A> |
+                                                       <A HREF="man.cgi.7.pdf">pdf</A> |
+                                                       <A HREF="man.cgi.7.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="eqn.7.html">eqn(7)</A></TD>
                                        <TD VALIGN="top">
                                                eqn-mandoc language reference
-                                               <SMALL>
                                                        (<A HREF="eqn.7.txt">text</A> | 
                                                        <A HREF="eqn.7.xhtml">xhtml</A> |
                                                        <A HREF="eqn.7.pdf">pdf</A> |
-                                                       <A HREF="eqn.7.ps">postscript</A>)
-                                               </SMALL>
+                                                       <A HREF="eqn.7.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="mandoc_char.7.html">mandoc_char(7)</A></TD>
                                        <TD VALIGN="top">
                                                mandoc special characters
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
+                                                       <A HREF="mandoc_char.7.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="mdoc.7.html">mdoc(7)</A></TD>
                                        <TD VALIGN="top">
                                                mdoc language reference
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
+                                                       <A HREF="mdoc.7.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="roff.7.html">roff(7)</A></TD>
                                        <TD VALIGN="top">
                                                roff-mandoc language reference
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
+                                                       <A HREF="roff.7.ps">ps</A>)
                                        </TD>
                                </TR>
                                <TR>
                                        <TD VALIGN="top"><A HREF="tbl.7.html">tbl(7)</A></TD>
                                        <TD VALIGN="top">
                                                tbl-mandoc language reference
-                                               <SMALL>
                                                        (<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>)
-                                               </SMALL>
+                                                       <A HREF="tbl.7.ps">ps</A>)
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="catman.8.html">catman(8)</A></TD>
+                                       <TD VALIGN="top">
+                                               update a man.cgi manpage cache
+                                                       (<A HREF="catman.8.txt">text</A> | 
+                                                       <A HREF="catman.8.xhtml">xhtml</A> |
+                                                       <A HREF="catman.8.pdf">pdf</A> |
+                                                       <A HREF="catman.8.ps">ps</A>)
+                                       </TD>
+                               </TR>
+                               <TR>
+                                       <TD VALIGN="top"><A HREF="mandocdb.8.html">mandocdb(8)</A></TD>
+                                       <TD VALIGN="top">
+                                               index UNIX manuals
+                                                       (<A HREF="mandocdb.8.txt">text</A> | 
+                                                       <A HREF="mandocdb.8.xhtml">xhtml</A> |
+                                                       <A HREF="mandocdb.8.pdf">pdf</A> |
+                                                       <A HREF="mandocdb.8.ps">ps</A>)
                                        </TD>
                                </TR>
                        </TBODY>
                </TABLE>
-
                <H1>
                        <A NAME="contact">Contact</A>
                </H1>
-
                <P>
-                       Use the mailing lists for bug-reports, patches, questions, etc. (these require subscription).  Please check the
+                       Use the mailing lists for bug-reports, patches, questions, etc.  Please check the
                        <A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/TODO?cvsroot=mdocml">TODO</A> for known issues
-                       before posting.  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.
+                       before posting.  All lists are subscription-only: send a blank e-mail to the listed address to subscribe.  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.  Archives are available at <A HREF="http://gmane.org/" CLASS="external">Gmane</A>.
                </P>
-
                <TABLE WIDTH="100%" SUMMARY="Mailing Lists">
                        <COL WIDTH="175">
                        <COL>
                                        </TD>
                                        <TD>
                                                bug-reports, general questions, and announcements 
-                                               <SMALL>(<A HREF="/archives/discuss/summary.html">archive</A>)</SMALL>
                                        </TD>
                                </TR>
                                <TR>
                                        </TD>
                                        <TD>
                                                patches and system discussions 
-                                               <SMALL>(<A HREF="/archives/tech/summary.html">archive</A>)</SMALL>
                                        </TD>
                                </TR>
                                <TR>
                                        </TD>
                                        <TD>
                                                source commit messages 
-                                               <SMALL>(<A HREF="/archives/source/summary.html">archive</A>)</SMALL>
                                        </TD>
                                </TR>
                        </TBODY>
                </TABLE>
-
                <H1>
                        <A NAME="news">News</A>
                </H1>
-
-               <P CLASS="news">
-                       26-05-2011: version 1.11.3
-               </P>
-               <P>
-                       Introduce locale-encoding of output with the <B>-Tlocale</B> output option and Unicode escaped-character input.
-                       See <A HREF="mandoc.1.html">mandoc</A> and <A HREF="mandoc_char.7.html">mandoc_char</A>, respectively, for details.
-                       This allows for non-ASCII characters (e.g., <I>\[u5000]</I>) to be rendered in the locale's encoding, if said
-                       environment supports wide-character encoding (if it does not, <B>-Tascii</B> is used instead).
-                       Locale support can be turned off at compile time by removing <I>-DUSE_WCHAR</I> in the <I>Makefile</I>, in which case
-                       <B>-Tlocale</B> is always a synonym for <B>-Tascii</B>.
-               </P>
-               <P>
-                       Furthermore, multibyte-encoded documents, such as those in UTF-8, may be on-the-fly recoded into <A
-                       HREF="mandoc.1.html">mandoc</A> input by using the newly-added <A HREF="preconv.1.html">preconv</A> utility.
-                       Note: in the future, this feature may be integrated into <A HREF="mandoc.1.html">mandoc</A>.
-               </P>
-
                <P CLASS="news">
-                       12-05-2011: version 1.11.2
+                       23-03-2011: version 1.12.1
                </P>
                <P>
-                       Corrected some installation issues in version 1.11.1.
-                       Further migration to <A HREF="mandoc.3.html">libmandoc</A>.
-                       Initial public release (this utility is very much under development) of <A HREF="makewhatis.1.html">makewhatis</A>,
-                       initially named mandoc-db.
-                       This utility produces keyword databases of manual content
-                       <A HREF="http://mdocml.bsd.lv/mandoc-cgi/index.html">mandoc-cgi</A>, which features semantic querying of manual content.
-               </P>
-
-               <P CLASS="news">
-                       04-04-2011: version 1.11.1
+                       Significant work on <A HREF="apropos.1.html">apropos</A> and <A HREF="mandocdb.8.html">mandocdb</A>.  These tools are
+                       now much more robust.  
+                       A <A HREF="whatis.1.html">whatis</A> implementation is now handled as an <A HREF="apropos.1.html">apropos</A> mode.
+                       These tools are also able to minimally handle pre-formatted pages, that is, those already formatted by another utility
+                       such as GNU troff.
                </P>
                <P>
-                       The earlier <I>libroff</I>, <I>libmdoc</I>, and <I>libman</I> soup have been merged into
-                       a single library, <A HREF="mandoc.3.html">libmandoc</A>, which manages all aspects of
-                       parsing real manuals (from line-handling to <A HREF="tbl.7.html">tbl</A> parsing).
+                       The <A HREF="man.cgi.7.html">man.cgi</A> script is also now available for wider testing.  It interfaces with <A
+                       HREF="mandocdb.8.html">mandocdb</A> manuals cached by <A HREF="